1// Copyright 2019 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// -*- mode: C++ -*- 16// vim: set filetype=cpp: 17 18// Fragments of C++ code used by the Emboss C++ code generator. Anything before 19// the first template is ignored. The names between ** ** are used as template 20// names. See code_template.py for more details. Local variable names are 21// prefixed with `emboss_reserved_local_` to avoid conflicting with struct field 22// names. 23 24// clang-format off 25 26// ** outline ** /////////////////////////////////////////////////////////////// 27/** 28 * Generated by the Emboss compiler. DO NOT EDIT! 29 */ 30#ifndef ${header_guard} 31#define ${header_guard} 32#include <stdint.h> 33#include <string.h> 34 35#include <algorithm> 36#include <type_traits> 37#include <utility> 38 39#include "runtime/cpp/emboss_cpp_util.h" 40 41${includes} 42 43/* NOLINTBEGIN */ 44${body} 45/* NOLINTEND */ 46 47#endif // ${header_guard} 48 49 50// ** include ** /////////////////////////////////////////////////////////////// 51#include "${file_name}" 52 53 54// ** body ** ////////////////////////////////////////////////////////////////// 55${type_declarations} 56${type_definitions} 57${method_definitions} 58 59 60// ** namespace_wrap ** //////////////////////////////////////////////////////// 61namespace ${component} { 62${body} 63} // namespace ${component} 64 65 66// ** structure_view_declaration ** //////////////////////////////////////////// 67template <class Storage> 68class Generic${name}View; 69 70 71// ** structure_view_class ** ////////////////////////////////////////////////// 72template <class View> 73struct EmbossReservedInternalIsGeneric${name}View; 74 75template <class Storage> 76class Generic${name}View final { 77 public: 78 Generic${name}View() : backing_() {} 79 explicit Generic${name}View( 80 ${constructor_parameters} Storage emboss_reserved_local_bytes) 81 : backing_(emboss_reserved_local_bytes) ${parameter_initializers} 82 ${initialize_parameters_initialized_true} {} 83 84 // Views over compatible backing storage should be freely assignable. 85 template <typename OtherStorage> 86 Generic${name}View( 87 const Generic${name}View<OtherStorage> &emboss_reserved_local_other) 88 : backing_{emboss_reserved_local_other.BackingStorage()} 89 ${parameter_copy_initializers} {} 90 91 // Allow pass-through construction of backing_, but only if there is at least 92 // one argument, and, if exactly one argument, that argument is not a 93 // (possibly c/v/ref-qualified) Generic${name}View. 94 // 95 // Explicitly ruling out overloads that might match the copy or move 96 // constructor is necessary in order for the copy and move constructors to be 97 // reliably found during overload resolution. 98 template <typename Arg, 99 typename = typename ::std::enable_if< 100 !EmbossReservedInternalIsGeneric${name}View< 101 typename ::std::remove_cv<typename ::std::remove_reference< 102 Arg>::type>::type>::value>::type> 103 explicit Generic${name}View( 104 ${constructor_parameters} Arg &&emboss_reserved_local_arg) 105 : backing_(::std::forward<Arg>( 106 emboss_reserved_local_arg)) ${parameter_initializers} 107 ${initialize_parameters_initialized_true} {} 108 template <typename Arg0, typename Arg1, typename... Args> 109 explicit Generic${name}View( 110 ${constructor_parameters} Arg0 &&emboss_reserved_local_arg0, 111 Arg1 &&emboss_reserved_local_arg1, Args &&... emboss_reserved_local_args) 112 : backing_(::std::forward<Arg0>(emboss_reserved_local_arg0), 113 ::std::forward<Arg1>(emboss_reserved_local_arg1), 114 ::std::forward<Args>( 115 emboss_reserved_local_args)...) ${parameter_initializers} 116 ${initialize_parameters_initialized_true} {} 117 118 template <typename OtherStorage> 119 Generic${name}View<Storage> &operator=( 120 const Generic${name}View<OtherStorage> &emboss_reserved_local_other) { 121 backing_ = emboss_reserved_local_other.BackingStorage(); 122 return *this; 123 } 124 125 ${enum_usings} 126 127 bool Ok() const { 128 if (!IsComplete()) return false; 129${parameter_ok_checks} 130${field_ok_checks} 131${requires_check} 132 return true; 133 } 134 Storage BackingStorage() const { return backing_; } 135 bool IsComplete() const { 136 return backing_.Ok() && IntrinsicSizeIn${units}().Ok() && 137 backing_.SizeIn${units}() >= 138 static_cast</**/ ::std::size_t>( 139 IntrinsicSizeIn${units}().UncheckedRead()); 140 } 141${size_method} 142 143 template <typename OtherStorage> 144 bool Equals( 145 Generic${name}View<OtherStorage> emboss_reserved_local_other) const { 146 ${equals_method_body} return true; 147 } 148 template <typename OtherStorage> 149 bool UncheckedEquals( 150 Generic${name}View<OtherStorage> emboss_reserved_local_other) const { 151 ${unchecked_equals_method_body} return true; 152 } 153 // (Unchecked)CopyFrom copies the number of bytes included in the other view, 154 // and ignores the size of the current view. Even if they differ before 155 // copying, the destination view's size should match the source view's size 156 // after copying, because any fields used in the calculation of the 157 // destination view's size should be updated by the copy. 158 template <typename OtherStorage> 159 void UncheckedCopyFrom( 160 Generic${name}View<OtherStorage> emboss_reserved_local_other) const { 161 backing_.UncheckedCopyFrom( 162 emboss_reserved_local_other.BackingStorage(), 163 emboss_reserved_local_other.IntrinsicSizeIn${units}().UncheckedRead()); 164 } 165 166 template <typename OtherStorage> 167 void CopyFrom( 168 Generic${name}View<OtherStorage> emboss_reserved_local_other) const { 169 backing_.CopyFrom( 170 emboss_reserved_local_other.BackingStorage(), 171 emboss_reserved_local_other.IntrinsicSizeIn${units}().Read()); 172 } 173 template <typename OtherStorage> 174 bool TryToCopyFrom( 175 Generic${name}View<OtherStorage> emboss_reserved_local_other) const { 176 return emboss_reserved_local_other.Ok() && backing_.TryToCopyFrom( 177 emboss_reserved_local_other.BackingStorage(), 178 emboss_reserved_local_other.IntrinsicSizeIn${units}().Read()); 179 } 180 181${text_stream_methods} 182 183 static constexpr bool IsAggregate() { return true; } 184 185${field_method_declarations} 186 187 private: 188 Storage backing_; 189 ${parameter_fields} 190 ${parameters_initialized_flag} 191 192 // This is a bit of a hack to handle Equals() and UncheckedEquals() between 193 // views with different underlying storage -- otherwise, structs with 194 // anonymous members run into access violations. 195 // 196 // TODO(bolms): Revisit this once the special-case code for anonymous members 197 // is replaced by explicit read/write virtual fields in the IR. 198 template <class OtherStorage> 199 friend class Generic${name}View; 200}; 201using ${name}View = 202 Generic${name}View</**/ ::emboss::support::ReadOnlyContiguousBuffer>; 203using ${name}Writer = 204 Generic${name}View</**/ ::emboss::support::ReadWriteContiguousBuffer>; 205 206template <class View> 207struct EmbossReservedInternalIsGeneric${name}View { 208 static constexpr const bool value = false; 209}; 210 211template <class Storage> 212struct EmbossReservedInternalIsGeneric${name}View< 213 Generic${name}View<Storage>> { 214 static constexpr const bool value = true; 215}; 216 217template <typename T> 218inline Generic${name}View< 219 /**/ ::emboss::support::ContiguousBuffer< 220 typename ::std::remove_reference< 221 decltype(*::std::declval<T>()->data())>::type, 222 1, 0>> 223Make${name}View(${constructor_parameters} T &&emboss_reserved_local_arg) { 224 return Generic${name}View< 225 /**/ ::emboss::support::ContiguousBuffer< 226 typename ::std::remove_reference<decltype( 227 *::std::declval<T>()->data())>::type, 228 1, 0>>( 229 ${forwarded_parameters} ::std::forward<T>(emboss_reserved_local_arg)); 230} 231 232template <typename T> 233inline Generic${name}View</**/ ::emboss::support::ContiguousBuffer<T, 1, 0>> 234Make${name}View(${constructor_parameters} T *emboss_reserved_local_data, 235 ::std::size_t emboss_reserved_local_size) { 236 return Generic${name}View</**/ ::emboss::support::ContiguousBuffer<T, 1, 0>>( 237 ${forwarded_parameters} emboss_reserved_local_data, 238 emboss_reserved_local_size); 239} 240 241template <typename T, ::std::size_t kAlignment> 242inline Generic${name}View< 243 /**/ ::emboss::support::ContiguousBuffer<T, kAlignment, 0>> 244MakeAligned${name}View( 245 ${constructor_parameters} T *emboss_reserved_local_data, 246 ::std::size_t emboss_reserved_local_size) { 247 return Generic${name}View< 248 /**/ ::emboss::support::ContiguousBuffer<T, kAlignment, 0>>( 249 ${forwarded_parameters} emboss_reserved_local_data, 250 emboss_reserved_local_size); 251} 252 253// ** struct_text_stream ** //////////////////////////////////////////////////// 254 template <class Stream> 255 bool UpdateFromTextStream(Stream *emboss_reserved_local_stream) const { 256 ::std::string emboss_reserved_local_brace; 257 if (!::emboss::support::ReadToken(emboss_reserved_local_stream, 258 &emboss_reserved_local_brace)) 259 return false; 260 if (emboss_reserved_local_brace != "{") return false; 261 for (;;) { 262 ::std::string emboss_reserved_local_name; 263 if (!::emboss::support::ReadToken(emboss_reserved_local_stream, 264 &emboss_reserved_local_name)) 265 return false; 266 if (emboss_reserved_local_name == ",") 267 if (!::emboss::support::ReadToken(emboss_reserved_local_stream, 268 &emboss_reserved_local_name)) 269 return false; 270 if (emboss_reserved_local_name == "}") return true; 271 ::std::string emboss_reserved_local_colon; 272 if (!::emboss::support::ReadToken(emboss_reserved_local_stream, 273 &emboss_reserved_local_colon)) 274 return false; 275 if (emboss_reserved_local_colon != ":") return false; 276${decode_fields} 277 // decode_fields will `continue` if it successfully finds a field. 278 return false; 279 } 280 } 281 282 template <class Stream> 283 void WriteToTextStream( 284 Stream *emboss_reserved_local_stream, 285 ::emboss::TextOutputOptions emboss_reserved_local_options) const { 286 ::emboss::TextOutputOptions emboss_reserved_local_field_options = 287 emboss_reserved_local_options.PlusOneIndent(); 288 if (emboss_reserved_local_options.multiline()) { 289 emboss_reserved_local_stream->Write("{\n"); 290 } else { 291 emboss_reserved_local_stream->Write("{"); 292 } 293 bool emboss_reserved_local_wrote_field = false; 294${write_fields} 295 // Avoid unused variable warnings for empty structures: 296 (void)emboss_reserved_local_wrote_field; 297 if (emboss_reserved_local_options.multiline()) { 298 emboss_reserved_local_stream->Write( 299 emboss_reserved_local_options.current_indent()); 300 emboss_reserved_local_stream->Write("}"); 301 } else { 302 emboss_reserved_local_stream->Write(" }"); 303 } 304 } 305 306 307// ** decode_field ** ////////////////////////////////////////////////////////// 308 // If the field name matches ${field_name}, handle it, otherwise fall 309 // through to the next field. 310 if (emboss_reserved_local_name == "${field_name}") { 311 // TODO(bolms): How should missing optional fields be handled? 312 if (!${field_name}().UpdateFromTextStream( 313 emboss_reserved_local_stream)) { 314 return false; 315 } 316 continue; 317 } 318 319// ** write_field_to_text_stream ** //////////////////////////////////////////// 320 if (has_${field_name}().ValueOr(false)) { 321 // Don't try to read the field if `allow_partial_output` is set and the 322 // field can't be `Read()`. Aggregates should still be visited, even if 323 // they are not `Ok()` overall, since submembers may still be `Ok()`. 324 if (!emboss_reserved_local_field_options.allow_partial_output() || 325 ${field_name}().IsAggregate() || ${field_name}().Ok()) { 326 if (emboss_reserved_local_field_options.multiline()) { 327 emboss_reserved_local_stream->Write( 328 emboss_reserved_local_field_options.current_indent()); 329 } else { 330 if (emboss_reserved_local_wrote_field) { 331 emboss_reserved_local_stream->Write(","); 332 } 333 emboss_reserved_local_stream->Write(" "); 334 } 335 emboss_reserved_local_stream->Write("${field_name}: "); 336 ${field_name}().WriteToTextStream(emboss_reserved_local_stream, 337 emboss_reserved_local_field_options); 338 emboss_reserved_local_wrote_field = true; 339 if (emboss_reserved_local_field_options.multiline()) { 340 emboss_reserved_local_stream->Write("\n"); 341 } 342 } else if (emboss_reserved_local_field_options.allow_partial_output() && 343 emboss_reserved_local_field_options.comments() && 344 !${field_name}().IsAggregate() && !${field_name}().Ok()) { 345 if (emboss_reserved_local_field_options.multiline()) { 346 emboss_reserved_local_stream->Write( 347 emboss_reserved_local_field_options.current_indent()); 348 } 349 emboss_reserved_local_stream->Write("# ${field_name}: UNREADABLE\n"); 350 } 351 } 352 353// ** write_read_only_field_to_text_stream ** ////////////////////////////////// 354 if (has_${field_name}().ValueOr(false) && 355 emboss_reserved_local_field_options.comments()) { 356 if (!emboss_reserved_local_field_options.allow_partial_output() || 357 ${field_name}().IsAggregate() || ${field_name}().Ok()) { 358 emboss_reserved_local_stream->Write( 359 emboss_reserved_local_field_options.current_indent()); 360 // TODO(bolms): When there are multiline read-only fields, add an option 361 // to TextOutputOptions to add `# ` to the current indent and use it 362 // here, so that subsequent lines are also commented out. 363 emboss_reserved_local_stream->Write("# ${field_name}: "); 364 ${field_name}().WriteToTextStream(emboss_reserved_local_stream, 365 emboss_reserved_local_field_options); 366 emboss_reserved_local_stream->Write("\n"); 367 } else { 368 if (emboss_reserved_local_field_options.multiline()) { 369 emboss_reserved_local_stream->Write( 370 emboss_reserved_local_field_options.current_indent()); 371 } 372 emboss_reserved_local_stream->Write("# ${field_name}: UNREADABLE\n"); 373 } 374 } 375 376// ** constant_structure_size_method ** //////////////////////////////////////// 377 static constexpr ::std::size_t SizeIn${units}() { 378 return static_cast</**/ ::std::size_t>(IntrinsicSizeIn${units}().Read()); 379 } 380 static constexpr bool SizeIsKnown() { 381 return IntrinsicSizeIn${units}().Ok(); 382 } 383 384// ** runtime_structure_size_method ** ///////////////////////////////////////// 385 ::std::size_t SizeIn${units}() const { 386 return static_cast</**/ ::std::size_t>(IntrinsicSizeIn${units}().Read()); 387 } 388 bool SizeIsKnown() const { return IntrinsicSizeIn${units}().Ok(); } 389 390 391// ** ok_method_test ** //////////////////////////////////////////////////////// 392 // If we don't have enough information to determine whether ${field} is 393 // present in the structure, then structure.Ok() should be false. 394 if (!has_${field}.Known()) return false; 395 // If ${field} is present, but not Ok(), then structure.Ok() should be 396 // false. If ${field} is not present, it does not matter whether it is 397 // Ok(). 398 if (has_${field}.ValueOrDefault() && !${field}.Ok()) return false; 399 400 401// ** equals_method_test ** //////////////////////////////////////////////////// 402 // If this->${field} is not equal to emboss_reserved_local_other.${field}, 403 // then the structures are not equal. 404 405 // If either structure's has_${field} is unknown, then default to not 406 // Equals(). 407 // 408 // TODO(bolms): Should Equals() return Maybe<bool> and/or return true for 409 // non-Ok()-but-equivalent structures? 410 if (!has_${field}.Known()) return false; 411 if (!emboss_reserved_local_other.has_${field}.Known()) return false; 412 413 // If one side has ${field} but the other side does not, then the fields 414 // are not equal. We use ValueOrDefault() instead of Value() since Value() 415 // is more complex and non-constexpr, and we already know that 416 // has_${field}.Known() is true for both structures. 417 if (emboss_reserved_local_other.has_${field}.ValueOrDefault() && 418 !has_${field}.ValueOrDefault()) 419 return false; 420 if (has_${field}.ValueOrDefault() && 421 !emboss_reserved_local_other.has_${field}.ValueOrDefault()) 422 return false; 423 424 // If both sides have ${field}, then check that their Equals() returns 425 // true. 426 if (emboss_reserved_local_other.has_${field}.ValueOrDefault() && 427 has_${field}.ValueOrDefault() && 428 !${field}.Equals(emboss_reserved_local_other.${field})) 429 return false; 430 431 432// ** unchecked_equals_method_test ** ////////////////////////////////////////// 433 // The contract for UncheckedEquals() is that the caller must assure that 434 // both views are Ok() (which implies that has_${field}.Known() is true), 435 // and UncheckedEquals() will never perform any assertion checks (which 436 // implies that UncheckedEquals() cannot call has_${field}.Value()). 437 438 // If this->has_${field} but !emboss_reserved_local_other.has_${field}, or 439 // vice versa, then the structures are not equal. If neither structure 440 // has_${field}, then ${field} is considered equal. 441 if (emboss_reserved_local_other.has_${field}.ValueOr(false) && 442 !has_${field}.ValueOr(false)) 443 return false; 444 if (has_${field}.ValueOr(false) && 445 !emboss_reserved_local_other.has_${field}.ValueOr(false)) 446 return false; 447 448 // If ${field} is present in both structures, then check its equality. 449 if (emboss_reserved_local_other.has_${field}.ValueOr(false) && 450 has_${field}.ValueOr(false) && 451 !${field}.UncheckedEquals(emboss_reserved_local_other.${field})) 452 return false; 453 454 455// ** structure_view_type ** /////////////////////////////////////////////////// 456${namespace}::Generic${name}View<typename ${buffer_type}> 457 458 459// ** external_view_type ** //////////////////////////////////////////////////// 460${namespace}::${name}View< 461 /**/ ::emboss::support::FixedSizeViewParameters<${bits}, ${validator}>, 462 typename ${buffer_type}> 463 464 465// ** enum_view_type ** //////////////////////////////////////////////////////// 466${support_namespace}::EnumView< 467 /**/ ${enum_type}, 468 ::emboss::support::FixedSizeViewParameters<${bits}, ${validator}>, 469 typename ${buffer_type}> 470 471 472// ** array_view_adapter ** //////////////////////////////////////////////////// 473${support_namespace}::GenericArrayView< 474 typename ${element_view_type}, typename ${buffer_type}, ${element_size}, 475 ${addressable_unit_size} ${element_view_parameter_types}> 476 477 478// ** structure_field_validator ** ///////////////////////////////////////////// 479struct ${name} { 480 template <typename ValueType> 481 static constexpr bool ValueIsOk(ValueType emboss_reserved_local_value) { 482 (void)emboss_reserved_local_value; // Silence -Wunused-parameter 483 return (${expression}).ValueOrDefault(); 484 } 485}; 486 487 488// ** structure_single_field_method_declarations ** //////////////////////////// 489 ${visibility}: 490 typename ${type_reader} ${name}() const; 491 ::emboss::support::Maybe<bool> has_${name}() const; 492 493 494// ** structure_single_field_method_definitions ** ///////////////////////////// 495template <class Storage> 496inline typename ${type_reader} Generic${parent_type}View<Storage>::${name}() 497 const { 498 // If it's not possible to read the location of this field, provide a view 499 // into a null storage -- the only safe methods to call on it will be Ok() and 500 // IsComplete(), but it is necessary to return a view so that client code can 501 // call those methods at all. Similarly, if the end of the field would come 502 // before the start, we provide a null storage, though arguably we should 503 // not. 504${parameter_subexpressions} 505 if (${parameters_known} has_${name}().ValueOr(false)) { 506${size_and_offset_subexpressions} 507 auto emboss_reserved_local_size = ${size}; 508 auto emboss_reserved_local_offset = ${offset}; 509 if (emboss_reserved_local_size.Known() && 510 emboss_reserved_local_size.ValueOr(0) >= 0 && 511 emboss_reserved_local_offset.Known() && 512 emboss_reserved_local_offset.ValueOr(0) >= 0) { 513 return ${type_reader}( 514 ${parameter_values} backing_ 515 .template GetOffsetStorage<${alignment}, 516 ${static_offset}>( 517 emboss_reserved_local_offset.ValueOrDefault(), 518 emboss_reserved_local_size.ValueOrDefault())); 519 } 520 } 521 return ${type_reader}(); 522} 523 524template <class Storage> 525inline ::emboss::support::Maybe<bool> 526Generic${parent_type}View<Storage>::has_${name}() const { 527 return ${field_exists}; 528} 529 530 531// ** structure_single_const_virtual_field_method_declarations ** ////////////// 532 ${visibility}: 533 class ${virtual_view_type_name} final { 534 public: 535 using ValueType = ${logical_type}; 536 537 constexpr ${virtual_view_type_name}() {} 538 ${virtual_view_type_name}(const ${virtual_view_type_name} &) = default; 539 ${virtual_view_type_name}(${virtual_view_type_name} &&) = default; 540 ${virtual_view_type_name} &operator=(const ${virtual_view_type_name} &) = 541 default; 542 ${virtual_view_type_name} &operator=(${virtual_view_type_name} &&) = 543 default; 544 ~${virtual_view_type_name}() = default; 545 546 static constexpr ${logical_type} Read(); 547 static constexpr ${logical_type} UncheckedRead(); 548 static constexpr bool Ok() { return true; } 549 template <class Stream> 550 void WriteToTextStream(Stream *emboss_reserved_local_stream, 551 const ::emboss::TextOutputOptions 552 &emboss_reserved_local_options) const { 553 ::emboss::support::${write_to_text_stream_function}( 554 this, emboss_reserved_local_stream, emboss_reserved_local_options); 555 } 556 557 static constexpr bool IsAggregate() { return false; } 558 }; 559 560 static constexpr ${virtual_view_type_name} ${name}() { 561 return ${virtual_view_type_name}(); 562 } 563 static constexpr ::emboss::support::Maybe<bool> has_${name}() { 564 return ::emboss::support::Maybe<bool>(true); 565 } 566 567 568// ** structure_single_const_virtual_field_method_definitions ** /////////////// 569namespace ${parent_type} { 570inline constexpr ${logical_type} ${name}() { 571 return ${read_value}.ValueOrDefault(); 572} 573} // namespace ${parent_type} 574 575template <class Storage> 576inline constexpr ${logical_type} 577Generic${parent_type}View<Storage>::${virtual_view_type_name}::Read() { 578 return ${parent_type}::${name}(); 579} 580 581template <class Storage> 582inline constexpr ${logical_type} 583Generic${parent_type}View< 584 Storage>::${virtual_view_type_name}::UncheckedRead() { 585 return ${parent_type}::${name}(); 586} 587 588// ** structure_single_virtual_field_method_declarations ** //////////////////// 589 ${visibility}: 590 class ${virtual_view_type_name} final { 591 public: 592 using ValueType = ${logical_type}; 593 594 explicit ${virtual_view_type_name}( 595 const Generic${parent_type}View &emboss_reserved_local_view) 596 : view_(emboss_reserved_local_view) {} 597 ${virtual_view_type_name}() = delete; 598 ${virtual_view_type_name}(const ${virtual_view_type_name} &) = default; 599 ${virtual_view_type_name}(${virtual_view_type_name} &&) = default; 600 ${virtual_view_type_name} &operator=(const ${virtual_view_type_name} &) = 601 default; 602 ${virtual_view_type_name} &operator=(${virtual_view_type_name} &&) = 603 default; 604 ~${virtual_view_type_name}() = default; 605 606 ${logical_type} Read() const { 607 EMBOSS_CHECK(view_.has_${name}().ValueOr(false)); 608 auto emboss_reserved_local_value = MaybeRead(); 609 EMBOSS_CHECK(emboss_reserved_local_value.Known()); 610 EMBOSS_CHECK(ValueIsOk(emboss_reserved_local_value.ValueOrDefault())); 611 return emboss_reserved_local_value.ValueOrDefault(); 612 } 613 ${logical_type} UncheckedRead() const { 614 // UncheckedRead() on a virtual still calls Ok() on its dependencies; 615 // i.e., it still does some bounds checking. This is because of a subtle 616 // case, illustrated by the example below: 617 // 618 // # .emb 619 // struct Foo: 620 // 0 [+1] UInt x 621 // if x != 0: 622 // 1 [+1] UInt y 623 // let x_and_y = x != 0 && y != 0 624 // 625 // // .cc 626 // std::array<char, 1> buffer = {0}; 627 // const auto view = MakeFooView(&buffer); 628 // assert(!view.x_and_y().UncheckedRead()); 629 // 630 // Without the checks for Ok(), the implementation of UncheckedRead() 631 // looks something like: 632 // 633 // bool UncheckedRead() const { 634 // return And(view_.x().UncheckedRead(), 635 // view_.y().UncheckedRead()).ValueOrDefault(); 636 // } 637 // 638 // Unfortunately, even if x().UncheckedRead() is false, this will call 639 // UncheckedRead() on y(), which will segfault. 640 // 641 // TODO(bolms): Figure out a way to minimize bounds checking, instead of 642 // just always checking here. 643 return MaybeRead().ValueOrDefault(); 644 } 645 // Ok() can be false if some dependency is unreadable, *or* if there is an 646 // error somewhere in the arithmetic -- say, division by zero. 647 bool Ok() const { 648 auto emboss_reserved_local_value = MaybeRead(); 649 return emboss_reserved_local_value.Known() && 650 ValueIsOk(emboss_reserved_local_value.ValueOrDefault()); 651 } 652 template <class Stream> 653 void WriteToTextStream(Stream *emboss_reserved_local_stream, 654 const ::emboss::TextOutputOptions 655 &emboss_reserved_local_options) const { 656 ::emboss::support::${write_to_text_stream_function}( 657 this, emboss_reserved_local_stream, emboss_reserved_local_options); 658 } 659 660 static constexpr bool IsAggregate() { return false; } 661 662${write_methods} 663 664 private: 665 ::emboss::support::Maybe</**/ ${logical_type}> MaybeRead() const { 666${read_subexpressions} 667 return ${read_value}; 668 } 669 670 static constexpr bool ValueIsOk( 671 ${logical_type} emboss_reserved_local_value) { 672 (void)emboss_reserved_local_value; // Silence -Wunused-parameter 673 return ${value_is_ok}.ValueOr(false); 674 } 675 676 const Generic${parent_type}View view_; 677 }; 678 ${virtual_view_type_name} ${name}() const; 679 ::emboss::support::Maybe<bool> has_${name}() const; 680 681 682// ** structure_single_virtual_field_write_methods ** ////////////////////////// 683 bool TryToWrite(${logical_type} emboss_reserved_local_value) { 684 const auto emboss_reserved_local_maybe_new_value = ${transform}; 685 if (!CouldWriteValue(emboss_reserved_local_value)) return false; 686 return view_.${destination}.TryToWrite( 687 emboss_reserved_local_maybe_new_value.ValueOrDefault()); 688 } 689 void Write(${logical_type} emboss_reserved_local_value) { 690 const bool result = TryToWrite(emboss_reserved_local_value); 691 (void)result; 692 EMBOSS_CHECK(result); 693 } 694 void UncheckedWrite(${logical_type} emboss_reserved_local_value) { 695 view_.${destination}.UncheckedWrite((${transform}).ValueOrDefault()); 696 } 697 bool CouldWriteValue(${logical_type} emboss_reserved_local_value) { 698 if (!ValueIsOk(emboss_reserved_local_value)) return false; 699 const auto emboss_reserved_local_maybe_new_value = ${transform}; 700 if (!emboss_reserved_local_maybe_new_value.Known()) return false; 701 return view_.${destination}.CouldWriteValue( 702 emboss_reserved_local_maybe_new_value.ValueOrDefault()); 703 } 704 template <class Stream> 705 bool UpdateFromTextStream(Stream *emboss_reserved_local_stream) { 706 return ::emboss::support::ReadIntegerFromTextStream( 707 this, emboss_reserved_local_stream); 708 } 709 710 711// ** structure_single_virtual_field_method_definitions ** ///////////////////// 712template <class Storage> 713inline typename Generic${parent_type}View<Storage>::${virtual_view_type_name} 714Generic${parent_type}View<Storage>::${name}() const { 715 return 716 typename Generic${parent_type}View<Storage>::${virtual_view_type_name}( 717 *this); 718} 719 720template <class Storage> 721inline ::emboss::support::Maybe<bool> 722Generic${parent_type}View<Storage>::has_${name}() const { 723 return ${field_exists}; 724} 725 726 727// ** structure_single_field_indirect_method_declarations ** /////////////////// 728 ${visibility}: 729 // The "this->" is required for (some versions of?) GCC. 730 auto ${name}() const -> decltype(this->${aliased_field}) { 731 return has_${name}().ValueOrDefault() ? ${aliased_field} 732 : decltype(this->${aliased_field})(); 733 } 734 ::emboss::support::Maybe<bool> has_${name}() const; 735 736 737// ** struct_single_field_indirect_method_definitions ** /////////////////////// 738template <class Storage> 739inline ::emboss::support::Maybe<bool> 740Generic${parent_type}View<Storage>::has_${name}() const { 741 return ${field_exists}; 742} 743 744 745// ** structure_single_parameter_field_method_declarations ** ////////////////// 746 private: 747 // TODO(bolms): Is there any harm if these are public methods? 748 constexpr ::emboss::support::MaybeConstantView</**/ ${logical_type}> 749 ${name}() const { 750 return parameters_initialized_ 751 ? ::emboss::support::MaybeConstantView</**/ ${logical_type}>( 752 ${name}_) 753 : ::emboss::support::MaybeConstantView</**/ ${logical_type}>(); 754 } 755 constexpr ::emboss::support::Maybe<bool> has_${name}() const { 756 return ::emboss::support::Maybe<bool>(parameters_initialized_); 757 } 758 759 760// ** enum_declaration ** ////////////////////////////////////////////////////// 761enum class ${enum} : ${enum_type}; 762 763 764// ** enum_definition ** /////////////////////////////////////////////////////// 765enum class ${enum} : ${enum_type} { 766${enum_values} 767}; 768 769// This setup (ab)uses the fact that C++ templates can be defined in many 770// translation units, but will be collapsed to a single definition at link time 771// (or no definition, if no client code instantiates the template). 772// 773// Emboss could accomplish almost the same result by generating multiple .cc 774// files (one per function), but Bazel doesn't have great support for specifying 775// "the output of this rule is an indeterminate number of files, all of which 776// should be used as input to this other rule," which would be necessary to 777// generate all the .cc files and then build and link them into a library. 778// ** enum_traits ** /////////////////////////////////////////////////////////// 779template <class Enum> 780class EnumTraits; 781 782template <> 783class EnumTraits<${enum}> final { 784 public: 785 static bool TryToGetEnumFromName(const char *emboss_reserved_local_name, 786 ${enum} *emboss_reserved_local_result) { 787 if (emboss_reserved_local_name == nullptr) return false; 788 // TODO(bolms): The generated code here would be much more efficient for 789 // large enums if the mapping were performed using a prefix trie rather than 790 // repeated strcmp(). 791${enum_from_name_cases} 792 return false; 793 } 794 795 static const char *TryToGetNameFromEnum( 796 ${enum} emboss_reserved_local_value) { 797 switch (emboss_reserved_local_value) { 798${name_from_enum_cases} 799 default: return nullptr; 800 } 801 } 802 803 static bool EnumIsKnown(${enum} emboss_reserved_local_value) { 804 switch (emboss_reserved_local_value) { 805${enum_is_known_cases} 806 default: 807 return false; 808 } 809 } 810 811 static ::std::ostream &SendToOstream(::std::ostream &emboss_reserved_local_os, 812 ${enum} emboss_reserved_local_value) { 813 const char *emboss_reserved_local_name = 814 TryToGetNameFromEnum(emboss_reserved_local_value); 815 if (emboss_reserved_local_name == nullptr) { 816 emboss_reserved_local_os 817 << static_cast</**/ ::std::underlying_type<${enum}>::type>( 818 emboss_reserved_local_value); 819 } else { 820 emboss_reserved_local_os << emboss_reserved_local_name; 821 } 822 return emboss_reserved_local_os; 823 } 824}; 825 826// These functions are intended to be found via ADL. 827static inline bool TryToGetEnumFromName( 828 const char *emboss_reserved_local_name, 829 ${enum} *emboss_reserved_local_result) { 830 return EnumTraits<${enum}>::TryToGetEnumFromName( 831 emboss_reserved_local_name, emboss_reserved_local_result); 832} 833 834static inline const char *TryToGetNameFromEnum( 835 ${enum} emboss_reserved_local_value) { 836 return EnumTraits<${enum}>::TryToGetNameFromEnum( 837 emboss_reserved_local_value); 838} 839 840static inline bool EnumIsKnown(${enum} emboss_reserved_local_value) { 841 return EnumTraits<${enum}>::EnumIsKnown(emboss_reserved_local_value); 842} 843 844static inline ::std::ostream &operator<<( 845 ::std::ostream &emboss_reserved_local_os, 846 ${enum} emboss_reserved_local_value) { 847 return EnumTraits<${enum}>::SendToOstream(emboss_reserved_local_os, 848 emboss_reserved_local_value); 849} 850 851// ** enum_from_name_case ** /////////////////////////////////////////////////// 852 if (!strcmp("${name}", emboss_reserved_local_name)) { 853 *emboss_reserved_local_result = ${enum}::${value}; 854 return true; 855 } 856 857// ** name_from_enum_case ** /////////////////////////////////////////////////// 858 case ${enum}::${value}: return "${name}"; 859 860// ** enum_is_known_case ** //////////////////////////////////////////////////// 861 case ${enum}::${name}: return true; 862 863// ** enum_value ** //////////////////////////////////////////////////////////// 864 ${name} = ${value}, 865 866// ** enum_using_statement ** ////////////////////////////////////////////////// 867 using ${name} = ${component}; 868