| // Copyright 2019 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // -*- mode: C++ -*- |
| // vim: set filetype=cpp: |
| |
| // Fragments of C++ code used by the Emboss C++ code generator. Anything before |
| // the first template is ignored. The names between ** ** are used as template |
| // names. See code_template.py for more details. Local variable names are |
| // prefixed with `emboss_reserved_local_` to avoid conflicting with struct field |
| // names. |
| |
| // clang-format off |
| |
| // ** outline ** /////////////////////////////////////////////////////////////// |
| /** |
| * Generated by the Emboss compiler. DO NOT EDIT! |
| */ |
| #ifndef ${header_guard} |
| #define ${header_guard} |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "runtime/cpp/emboss_cpp_util.h" |
| |
| ${includes} |
| |
| /* NOLINTBEGIN */ |
| ${body} |
| /* NOLINTEND */ |
| |
| #endif // ${header_guard} |
| |
| |
| // ** include ** /////////////////////////////////////////////////////////////// |
| #include "${file_name}" |
| |
| |
| // ** body ** ////////////////////////////////////////////////////////////////// |
| ${type_declarations} |
| ${type_definitions} |
| ${method_definitions} |
| |
| |
| // ** namespace_wrap ** //////////////////////////////////////////////////////// |
| namespace ${component} { |
| ${body} |
| } // namespace ${component} |
| |
| |
| // ** structure_view_declaration ** //////////////////////////////////////////// |
| template <class Storage> |
| class Generic${name}View; |
| |
| |
| // ** structure_view_class ** ////////////////////////////////////////////////// |
| template <class View> |
| struct EmbossReservedInternalIsGeneric${name}View; |
| |
| template <class Storage> |
| class Generic${name}View final { |
| public: |
| Generic${name}View() : backing_() {} |
| explicit Generic${name}View( |
| ${constructor_parameters} Storage emboss_reserved_local_bytes) |
| : backing_(emboss_reserved_local_bytes) ${parameter_initializers} |
| ${initialize_parameters_initialized_true} {} |
| |
| // Views over compatible backing storage should be freely assignable. |
| template <typename OtherStorage> |
| Generic${name}View( |
| const Generic${name}View<OtherStorage> &emboss_reserved_local_other) |
| : backing_{emboss_reserved_local_other.BackingStorage()} |
| ${parameter_copy_initializers} {} |
| |
| // Allow pass-through construction of backing_, but only if there is at least |
| // one argument, and, if exactly one argument, that argument is not a |
| // (possibly c/v/ref-qualified) Generic${name}View. |
| // |
| // Explicitly ruling out overloads that might match the copy or move |
| // constructor is necessary in order for the copy and move constructors to be |
| // reliably found during overload resolution. |
| template <typename Arg, |
| typename = typename ::std::enable_if< |
| !EmbossReservedInternalIsGeneric${name}View< |
| typename ::std::remove_cv<typename ::std::remove_reference< |
| Arg>::type>::type>::value>::type> |
| explicit Generic${name}View( |
| ${constructor_parameters} Arg &&emboss_reserved_local_arg) |
| : backing_(::std::forward<Arg>( |
| emboss_reserved_local_arg)) ${parameter_initializers} |
| ${initialize_parameters_initialized_true} {} |
| template <typename Arg0, typename Arg1, typename... Args> |
| explicit Generic${name}View( |
| ${constructor_parameters} Arg0 &&emboss_reserved_local_arg0, |
| Arg1 &&emboss_reserved_local_arg1, Args &&... emboss_reserved_local_args) |
| : backing_(::std::forward<Arg0>(emboss_reserved_local_arg0), |
| ::std::forward<Arg1>(emboss_reserved_local_arg1), |
| ::std::forward<Args>( |
| emboss_reserved_local_args)...) ${parameter_initializers} |
| ${initialize_parameters_initialized_true} {} |
| |
| template <typename OtherStorage> |
| Generic${name}View<Storage> &operator=( |
| const Generic${name}View<OtherStorage> &emboss_reserved_local_other) { |
| backing_ = emboss_reserved_local_other.BackingStorage(); |
| return *this; |
| } |
| |
| ${enum_usings} |
| |
| bool Ok() const { |
| if (!IsComplete()) return false; |
| ${parameter_ok_checks} |
| ${field_ok_checks} |
| ${requires_check} |
| return true; |
| } |
| Storage BackingStorage() const { return backing_; } |
| bool IsComplete() const { |
| return backing_.Ok() && IntrinsicSizeIn${units}().Ok() && |
| backing_.SizeIn${units}() >= |
| static_cast</**/ ::std::size_t>( |
| IntrinsicSizeIn${units}().UncheckedRead()); |
| } |
| ${size_method} |
| |
| template <typename OtherStorage> |
| bool Equals( |
| Generic${name}View<OtherStorage> emboss_reserved_local_other) const { |
| ${equals_method_body} return true; |
| } |
| template <typename OtherStorage> |
| bool UncheckedEquals( |
| Generic${name}View<OtherStorage> emboss_reserved_local_other) const { |
| ${unchecked_equals_method_body} return true; |
| } |
| // (Unchecked)CopyFrom copies the number of bytes included in the other view, |
| // and ignores the size of the current view. Even if they differ before |
| // copying, the destination view's size should match the source view's size |
| // after copying, because any fields used in the calculation of the |
| // destination view's size should be updated by the copy. |
| template <typename OtherStorage> |
| void UncheckedCopyFrom( |
| Generic${name}View<OtherStorage> emboss_reserved_local_other) const { |
| backing_.UncheckedCopyFrom( |
| emboss_reserved_local_other.BackingStorage(), |
| emboss_reserved_local_other.IntrinsicSizeIn${units}().UncheckedRead()); |
| } |
| |
| template <typename OtherStorage> |
| void CopyFrom( |
| Generic${name}View<OtherStorage> emboss_reserved_local_other) const { |
| backing_.CopyFrom( |
| emboss_reserved_local_other.BackingStorage(), |
| emboss_reserved_local_other.IntrinsicSizeIn${units}().Read()); |
| } |
| template <typename OtherStorage> |
| bool TryToCopyFrom( |
| Generic${name}View<OtherStorage> emboss_reserved_local_other) const { |
| return emboss_reserved_local_other.Ok() && backing_.TryToCopyFrom( |
| emboss_reserved_local_other.BackingStorage(), |
| emboss_reserved_local_other.IntrinsicSizeIn${units}().Read()); |
| } |
| |
| ${text_stream_methods} |
| |
| static constexpr bool IsAggregate() { return true; } |
| |
| ${field_method_declarations} |
| |
| private: |
| Storage backing_; |
| ${parameter_fields} |
| ${parameters_initialized_flag} |
| |
| // This is a bit of a hack to handle Equals() and UncheckedEquals() between |
| // views with different underlying storage -- otherwise, structs with |
| // anonymous members run into access violations. |
| // |
| // TODO(bolms): Revisit this once the special-case code for anonymous members |
| // is replaced by explicit read/write virtual fields in the IR. |
| template <class OtherStorage> |
| friend class Generic${name}View; |
| }; |
| using ${name}View = |
| Generic${name}View</**/ ::emboss::support::ReadOnlyContiguousBuffer>; |
| using ${name}Writer = |
| Generic${name}View</**/ ::emboss::support::ReadWriteContiguousBuffer>; |
| |
| template <class View> |
| struct EmbossReservedInternalIsGeneric${name}View { |
| static constexpr const bool value = false; |
| }; |
| |
| template <class Storage> |
| struct EmbossReservedInternalIsGeneric${name}View< |
| Generic${name}View<Storage>> { |
| static constexpr const bool value = true; |
| }; |
| |
| template <typename T> |
| inline Generic${name}View< |
| /**/ ::emboss::support::ContiguousBuffer< |
| typename ::std::remove_reference< |
| decltype(*::std::declval<T>()->data())>::type, |
| 1, 0>> |
| Make${name}View(${constructor_parameters} T &&emboss_reserved_local_arg) { |
| return Generic${name}View< |
| /**/ ::emboss::support::ContiguousBuffer< |
| typename ::std::remove_reference<decltype( |
| *::std::declval<T>()->data())>::type, |
| 1, 0>>( |
| ${forwarded_parameters} ::std::forward<T>(emboss_reserved_local_arg)); |
| } |
| |
| template <typename T> |
| inline Generic${name}View</**/ ::emboss::support::ContiguousBuffer<T, 1, 0>> |
| Make${name}View(${constructor_parameters} T *emboss_reserved_local_data, |
| ::std::size_t emboss_reserved_local_size) { |
| return Generic${name}View</**/ ::emboss::support::ContiguousBuffer<T, 1, 0>>( |
| ${forwarded_parameters} emboss_reserved_local_data, |
| emboss_reserved_local_size); |
| } |
| |
| template <typename T, ::std::size_t kAlignment> |
| inline Generic${name}View< |
| /**/ ::emboss::support::ContiguousBuffer<T, kAlignment, 0>> |
| MakeAligned${name}View( |
| ${constructor_parameters} T *emboss_reserved_local_data, |
| ::std::size_t emboss_reserved_local_size) { |
| return Generic${name}View< |
| /**/ ::emboss::support::ContiguousBuffer<T, kAlignment, 0>>( |
| ${forwarded_parameters} emboss_reserved_local_data, |
| emboss_reserved_local_size); |
| } |
| |
| // ** struct_text_stream ** //////////////////////////////////////////////////// |
| template <class Stream> |
| bool UpdateFromTextStream(Stream *emboss_reserved_local_stream) const { |
| ::std::string emboss_reserved_local_brace; |
| if (!::emboss::support::ReadToken(emboss_reserved_local_stream, |
| &emboss_reserved_local_brace)) |
| return false; |
| if (emboss_reserved_local_brace != "{") return false; |
| for (;;) { |
| ::std::string emboss_reserved_local_name; |
| if (!::emboss::support::ReadToken(emboss_reserved_local_stream, |
| &emboss_reserved_local_name)) |
| return false; |
| if (emboss_reserved_local_name == ",") |
| if (!::emboss::support::ReadToken(emboss_reserved_local_stream, |
| &emboss_reserved_local_name)) |
| return false; |
| if (emboss_reserved_local_name == "}") return true; |
| ::std::string emboss_reserved_local_colon; |
| if (!::emboss::support::ReadToken(emboss_reserved_local_stream, |
| &emboss_reserved_local_colon)) |
| return false; |
| if (emboss_reserved_local_colon != ":") return false; |
| ${decode_fields} |
| // decode_fields will `continue` if it successfully finds a field. |
| return false; |
| } |
| } |
| |
| template <class Stream> |
| void WriteToTextStream( |
| Stream *emboss_reserved_local_stream, |
| ::emboss::TextOutputOptions emboss_reserved_local_options) const { |
| ::emboss::TextOutputOptions emboss_reserved_local_field_options = |
| emboss_reserved_local_options.PlusOneIndent(); |
| if (emboss_reserved_local_options.multiline()) { |
| emboss_reserved_local_stream->Write("{\n"); |
| } else { |
| emboss_reserved_local_stream->Write("{"); |
| } |
| bool emboss_reserved_local_wrote_field = false; |
| ${write_fields} |
| // Avoid unused variable warnings for empty structures: |
| (void)emboss_reserved_local_wrote_field; |
| if (emboss_reserved_local_options.multiline()) { |
| emboss_reserved_local_stream->Write( |
| emboss_reserved_local_options.current_indent()); |
| emboss_reserved_local_stream->Write("}"); |
| } else { |
| emboss_reserved_local_stream->Write(" }"); |
| } |
| } |
| |
| |
| // ** decode_field ** ////////////////////////////////////////////////////////// |
| // If the field name matches ${field_name}, handle it, otherwise fall |
| // through to the next field. |
| if (emboss_reserved_local_name == "${field_name}") { |
| // TODO(bolms): How should missing optional fields be handled? |
| if (!${field_name}().UpdateFromTextStream( |
| emboss_reserved_local_stream)) { |
| return false; |
| } |
| continue; |
| } |
| |
| // ** write_field_to_text_stream ** //////////////////////////////////////////// |
| if (has_${field_name}().ValueOr(false)) { |
| // Don't try to read the field if `allow_partial_output` is set and the |
| // field can't be `Read()`. Aggregates should still be visited, even if |
| // they are not `Ok()` overall, since submembers may still be `Ok()`. |
| if (!emboss_reserved_local_field_options.allow_partial_output() || |
| ${field_name}().IsAggregate() || ${field_name}().Ok()) { |
| if (emboss_reserved_local_field_options.multiline()) { |
| emboss_reserved_local_stream->Write( |
| emboss_reserved_local_field_options.current_indent()); |
| } else { |
| if (emboss_reserved_local_wrote_field) { |
| emboss_reserved_local_stream->Write(","); |
| } |
| emboss_reserved_local_stream->Write(" "); |
| } |
| emboss_reserved_local_stream->Write("${field_name}: "); |
| ${field_name}().WriteToTextStream(emboss_reserved_local_stream, |
| emboss_reserved_local_field_options); |
| emboss_reserved_local_wrote_field = true; |
| if (emboss_reserved_local_field_options.multiline()) { |
| emboss_reserved_local_stream->Write("\n"); |
| } |
| } else if (emboss_reserved_local_field_options.allow_partial_output() && |
| emboss_reserved_local_field_options.comments() && |
| !${field_name}().IsAggregate() && !${field_name}().Ok()) { |
| if (emboss_reserved_local_field_options.multiline()) { |
| emboss_reserved_local_stream->Write( |
| emboss_reserved_local_field_options.current_indent()); |
| } |
| emboss_reserved_local_stream->Write("# ${field_name}: UNREADABLE\n"); |
| } |
| } |
| |
| // ** write_read_only_field_to_text_stream ** ////////////////////////////////// |
| if (has_${field_name}().ValueOr(false) && |
| emboss_reserved_local_field_options.comments()) { |
| if (!emboss_reserved_local_field_options.allow_partial_output() || |
| ${field_name}().IsAggregate() || ${field_name}().Ok()) { |
| emboss_reserved_local_stream->Write( |
| emboss_reserved_local_field_options.current_indent()); |
| // TODO(bolms): When there are multiline read-only fields, add an option |
| // to TextOutputOptions to add `# ` to the current indent and use it |
| // here, so that subsequent lines are also commented out. |
| emboss_reserved_local_stream->Write("# ${field_name}: "); |
| ${field_name}().WriteToTextStream(emboss_reserved_local_stream, |
| emboss_reserved_local_field_options); |
| emboss_reserved_local_stream->Write("\n"); |
| } else { |
| if (emboss_reserved_local_field_options.multiline()) { |
| emboss_reserved_local_stream->Write( |
| emboss_reserved_local_field_options.current_indent()); |
| } |
| emboss_reserved_local_stream->Write("# ${field_name}: UNREADABLE\n"); |
| } |
| } |
| |
| // ** constant_structure_size_method ** //////////////////////////////////////// |
| static constexpr ::std::size_t SizeIn${units}() { |
| return static_cast</**/ ::std::size_t>(IntrinsicSizeIn${units}().Read()); |
| } |
| static constexpr bool SizeIsKnown() { |
| return IntrinsicSizeIn${units}().Ok(); |
| } |
| |
| // ** runtime_structure_size_method ** ///////////////////////////////////////// |
| ::std::size_t SizeIn${units}() const { |
| return static_cast</**/ ::std::size_t>(IntrinsicSizeIn${units}().Read()); |
| } |
| bool SizeIsKnown() const { return IntrinsicSizeIn${units}().Ok(); } |
| |
| |
| // ** ok_method_test ** //////////////////////////////////////////////////////// |
| // If we don't have enough information to determine whether ${field} is |
| // present in the structure, then structure.Ok() should be false. |
| if (!has_${field}.Known()) return false; |
| // If ${field} is present, but not Ok(), then structure.Ok() should be |
| // false. If ${field} is not present, it does not matter whether it is |
| // Ok(). |
| if (has_${field}.ValueOrDefault() && !${field}.Ok()) return false; |
| |
| |
| // ** equals_method_test ** //////////////////////////////////////////////////// |
| // If this->${field} is not equal to emboss_reserved_local_other.${field}, |
| // then the structures are not equal. |
| |
| // If either structure's has_${field} is unknown, then default to not |
| // Equals(). |
| // |
| // TODO(bolms): Should Equals() return Maybe<bool> and/or return true for |
| // non-Ok()-but-equivalent structures? |
| if (!has_${field}.Known()) return false; |
| if (!emboss_reserved_local_other.has_${field}.Known()) return false; |
| |
| // If one side has ${field} but the other side does not, then the fields |
| // are not equal. We use ValueOrDefault() instead of Value() since Value() |
| // is more complex and non-constexpr, and we already know that |
| // has_${field}.Known() is true for both structures. |
| if (emboss_reserved_local_other.has_${field}.ValueOrDefault() && |
| !has_${field}.ValueOrDefault()) |
| return false; |
| if (has_${field}.ValueOrDefault() && |
| !emboss_reserved_local_other.has_${field}.ValueOrDefault()) |
| return false; |
| |
| // If both sides have ${field}, then check that their Equals() returns |
| // true. |
| if (emboss_reserved_local_other.has_${field}.ValueOrDefault() && |
| has_${field}.ValueOrDefault() && |
| !${field}.Equals(emboss_reserved_local_other.${field})) |
| return false; |
| |
| |
| // ** unchecked_equals_method_test ** ////////////////////////////////////////// |
| // The contract for UncheckedEquals() is that the caller must assure that |
| // both views are Ok() (which implies that has_${field}.Known() is true), |
| // and UncheckedEquals() will never perform any assertion checks (which |
| // implies that UncheckedEquals() cannot call has_${field}.Value()). |
| |
| // If this->has_${field} but !emboss_reserved_local_other.has_${field}, or |
| // vice versa, then the structures are not equal. If neither structure |
| // has_${field}, then ${field} is considered equal. |
| if (emboss_reserved_local_other.has_${field}.ValueOr(false) && |
| !has_${field}.ValueOr(false)) |
| return false; |
| if (has_${field}.ValueOr(false) && |
| !emboss_reserved_local_other.has_${field}.ValueOr(false)) |
| return false; |
| |
| // If ${field} is present in both structures, then check its equality. |
| if (emboss_reserved_local_other.has_${field}.ValueOr(false) && |
| has_${field}.ValueOr(false) && |
| !${field}.UncheckedEquals(emboss_reserved_local_other.${field})) |
| return false; |
| |
| |
| // ** structure_view_type ** /////////////////////////////////////////////////// |
| ${namespace}::Generic${name}View<typename ${buffer_type}> |
| |
| |
| // ** external_view_type ** //////////////////////////////////////////////////// |
| ${namespace}::${name}View< |
| /**/ ::emboss::support::FixedSizeViewParameters<${bits}, ${validator}>, |
| typename ${buffer_type}> |
| |
| |
| // ** enum_view_type ** //////////////////////////////////////////////////////// |
| ${support_namespace}::EnumView< |
| /**/ ${enum_type}, |
| ::emboss::support::FixedSizeViewParameters<${bits}, ${validator}>, |
| typename ${buffer_type}> |
| |
| |
| // ** array_view_adapter ** //////////////////////////////////////////////////// |
| ${support_namespace}::GenericArrayView< |
| typename ${element_view_type}, typename ${buffer_type}, ${element_size}, |
| ${addressable_unit_size} ${element_view_parameter_types}> |
| |
| |
| // ** structure_field_validator ** ///////////////////////////////////////////// |
| struct ${name} { |
| template <typename ValueType> |
| static constexpr bool ValueIsOk(ValueType emboss_reserved_local_value) { |
| (void)emboss_reserved_local_value; // Silence -Wunused-parameter |
| return (${expression}).ValueOrDefault(); |
| } |
| }; |
| |
| |
| // ** structure_single_field_method_declarations ** //////////////////////////// |
| ${visibility}: |
| typename ${type_reader} ${name}() const; |
| ::emboss::support::Maybe<bool> has_${name}() const; |
| |
| |
| // ** structure_single_field_method_definitions ** ///////////////////////////// |
| template <class Storage> |
| inline typename ${type_reader} Generic${parent_type}View<Storage>::${name}() |
| const { |
| // If it's not possible to read the location of this field, provide a view |
| // into a null storage -- the only safe methods to call on it will be Ok() and |
| // IsComplete(), but it is necessary to return a view so that client code can |
| // call those methods at all. Similarly, if the end of the field would come |
| // before the start, we provide a null storage, though arguably we should |
| // not. |
| ${parameter_subexpressions} |
| if (${parameters_known} has_${name}().ValueOr(false)) { |
| ${size_and_offset_subexpressions} |
| auto emboss_reserved_local_size = ${size}; |
| auto emboss_reserved_local_offset = ${offset}; |
| if (emboss_reserved_local_size.Known() && |
| emboss_reserved_local_size.ValueOr(0) >= 0 && |
| emboss_reserved_local_offset.Known() && |
| emboss_reserved_local_offset.ValueOr(0) >= 0) { |
| return ${type_reader}( |
| ${parameter_values} backing_ |
| .template GetOffsetStorage<${alignment}, |
| ${static_offset}>( |
| emboss_reserved_local_offset.ValueOrDefault(), |
| emboss_reserved_local_size.ValueOrDefault())); |
| } |
| } |
| return ${type_reader}(); |
| } |
| |
| template <class Storage> |
| inline ::emboss::support::Maybe<bool> |
| Generic${parent_type}View<Storage>::has_${name}() const { |
| return ${field_exists}; |
| } |
| |
| |
| // ** structure_single_const_virtual_field_method_declarations ** ////////////// |
| ${visibility}: |
| class ${virtual_view_type_name} final { |
| public: |
| using ValueType = ${logical_type}; |
| |
| constexpr ${virtual_view_type_name}() {} |
| ${virtual_view_type_name}(const ${virtual_view_type_name} &) = default; |
| ${virtual_view_type_name}(${virtual_view_type_name} &&) = default; |
| ${virtual_view_type_name} &operator=(const ${virtual_view_type_name} &) = |
| default; |
| ${virtual_view_type_name} &operator=(${virtual_view_type_name} &&) = |
| default; |
| ~${virtual_view_type_name}() = default; |
| |
| static constexpr ${logical_type} Read(); |
| static constexpr ${logical_type} UncheckedRead(); |
| static constexpr bool Ok() { return true; } |
| template <class Stream> |
| void WriteToTextStream(Stream *emboss_reserved_local_stream, |
| const ::emboss::TextOutputOptions |
| &emboss_reserved_local_options) const { |
| ::emboss::support::${write_to_text_stream_function}( |
| this, emboss_reserved_local_stream, emboss_reserved_local_options); |
| } |
| |
| static constexpr bool IsAggregate() { return false; } |
| }; |
| |
| static constexpr ${virtual_view_type_name} ${name}() { |
| return ${virtual_view_type_name}(); |
| } |
| static constexpr ::emboss::support::Maybe<bool> has_${name}() { |
| return ::emboss::support::Maybe<bool>(true); |
| } |
| |
| |
| // ** structure_single_const_virtual_field_method_definitions ** /////////////// |
| namespace ${parent_type} { |
| inline constexpr ${logical_type} ${name}() { |
| return ${read_value}.ValueOrDefault(); |
| } |
| } // namespace ${parent_type} |
| |
| template <class Storage> |
| inline constexpr ${logical_type} |
| Generic${parent_type}View<Storage>::${virtual_view_type_name}::Read() { |
| return ${parent_type}::${name}(); |
| } |
| |
| template <class Storage> |
| inline constexpr ${logical_type} |
| Generic${parent_type}View< |
| Storage>::${virtual_view_type_name}::UncheckedRead() { |
| return ${parent_type}::${name}(); |
| } |
| |
| // ** structure_single_virtual_field_method_declarations ** //////////////////// |
| ${visibility}: |
| class ${virtual_view_type_name} final { |
| public: |
| using ValueType = ${logical_type}; |
| |
| explicit ${virtual_view_type_name}( |
| const Generic${parent_type}View &emboss_reserved_local_view) |
| : view_(emboss_reserved_local_view) {} |
| ${virtual_view_type_name}() = delete; |
| ${virtual_view_type_name}(const ${virtual_view_type_name} &) = default; |
| ${virtual_view_type_name}(${virtual_view_type_name} &&) = default; |
| ${virtual_view_type_name} &operator=(const ${virtual_view_type_name} &) = |
| default; |
| ${virtual_view_type_name} &operator=(${virtual_view_type_name} &&) = |
| default; |
| ~${virtual_view_type_name}() = default; |
| |
| ${logical_type} Read() const { |
| EMBOSS_CHECK(view_.has_${name}().ValueOr(false)); |
| auto emboss_reserved_local_value = MaybeRead(); |
| EMBOSS_CHECK(emboss_reserved_local_value.Known()); |
| EMBOSS_CHECK(ValueIsOk(emboss_reserved_local_value.ValueOrDefault())); |
| return emboss_reserved_local_value.ValueOrDefault(); |
| } |
| ${logical_type} UncheckedRead() const { |
| // UncheckedRead() on a virtual still calls Ok() on its dependencies; |
| // i.e., it still does some bounds checking. This is because of a subtle |
| // case, illustrated by the example below: |
| // |
| // # .emb |
| // struct Foo: |
| // 0 [+1] UInt x |
| // if x != 0: |
| // 1 [+1] UInt y |
| // let x_and_y = x != 0 && y != 0 |
| // |
| // // .cc |
| // std::array<char, 1> buffer = {0}; |
| // const auto view = MakeFooView(&buffer); |
| // assert(!view.x_and_y().UncheckedRead()); |
| // |
| // Without the checks for Ok(), the implementation of UncheckedRead() |
| // looks something like: |
| // |
| // bool UncheckedRead() const { |
| // return And(view_.x().UncheckedRead(), |
| // view_.y().UncheckedRead()).ValueOrDefault(); |
| // } |
| // |
| // Unfortunately, even if x().UncheckedRead() is false, this will call |
| // UncheckedRead() on y(), which will segfault. |
| // |
| // TODO(bolms): Figure out a way to minimize bounds checking, instead of |
| // just always checking here. |
| return MaybeRead().ValueOrDefault(); |
| } |
| // Ok() can be false if some dependency is unreadable, *or* if there is an |
| // error somewhere in the arithmetic -- say, division by zero. |
| bool Ok() const { |
| auto emboss_reserved_local_value = MaybeRead(); |
| return emboss_reserved_local_value.Known() && |
| ValueIsOk(emboss_reserved_local_value.ValueOrDefault()); |
| } |
| template <class Stream> |
| void WriteToTextStream(Stream *emboss_reserved_local_stream, |
| const ::emboss::TextOutputOptions |
| &emboss_reserved_local_options) const { |
| ::emboss::support::${write_to_text_stream_function}( |
| this, emboss_reserved_local_stream, emboss_reserved_local_options); |
| } |
| |
| static constexpr bool IsAggregate() { return false; } |
| |
| ${write_methods} |
| |
| private: |
| ::emboss::support::Maybe</**/ ${logical_type}> MaybeRead() const { |
| ${read_subexpressions} |
| return ${read_value}; |
| } |
| |
| static constexpr bool ValueIsOk( |
| ${logical_type} emboss_reserved_local_value) { |
| (void)emboss_reserved_local_value; // Silence -Wunused-parameter |
| return ${value_is_ok}.ValueOr(false); |
| } |
| |
| const Generic${parent_type}View view_; |
| }; |
| ${virtual_view_type_name} ${name}() const; |
| ::emboss::support::Maybe<bool> has_${name}() const; |
| |
| |
| // ** structure_single_virtual_field_write_methods ** ////////////////////////// |
| bool TryToWrite(${logical_type} emboss_reserved_local_value) { |
| const auto emboss_reserved_local_maybe_new_value = ${transform}; |
| if (!CouldWriteValue(emboss_reserved_local_value)) return false; |
| return view_.${destination}.TryToWrite( |
| emboss_reserved_local_maybe_new_value.ValueOrDefault()); |
| } |
| void Write(${logical_type} emboss_reserved_local_value) { |
| const bool result = TryToWrite(emboss_reserved_local_value); |
| (void)result; |
| EMBOSS_CHECK(result); |
| } |
| void UncheckedWrite(${logical_type} emboss_reserved_local_value) { |
| view_.${destination}.UncheckedWrite((${transform}).ValueOrDefault()); |
| } |
| bool CouldWriteValue(${logical_type} emboss_reserved_local_value) { |
| if (!ValueIsOk(emboss_reserved_local_value)) return false; |
| const auto emboss_reserved_local_maybe_new_value = ${transform}; |
| if (!emboss_reserved_local_maybe_new_value.Known()) return false; |
| return view_.${destination}.CouldWriteValue( |
| emboss_reserved_local_maybe_new_value.ValueOrDefault()); |
| } |
| template <class Stream> |
| bool UpdateFromTextStream(Stream *emboss_reserved_local_stream) { |
| return ::emboss::support::ReadIntegerFromTextStream( |
| this, emboss_reserved_local_stream); |
| } |
| |
| |
| // ** structure_single_virtual_field_method_definitions ** ///////////////////// |
| template <class Storage> |
| inline typename Generic${parent_type}View<Storage>::${virtual_view_type_name} |
| Generic${parent_type}View<Storage>::${name}() const { |
| return |
| typename Generic${parent_type}View<Storage>::${virtual_view_type_name}( |
| *this); |
| } |
| |
| template <class Storage> |
| inline ::emboss::support::Maybe<bool> |
| Generic${parent_type}View<Storage>::has_${name}() const { |
| return ${field_exists}; |
| } |
| |
| |
| // ** structure_single_field_indirect_method_declarations ** /////////////////// |
| ${visibility}: |
| // The "this->" is required for (some versions of?) GCC. |
| auto ${name}() const -> decltype(this->${aliased_field}) { |
| return has_${name}().ValueOrDefault() ? ${aliased_field} |
| : decltype(this->${aliased_field})(); |
| } |
| ::emboss::support::Maybe<bool> has_${name}() const; |
| |
| |
| // ** struct_single_field_indirect_method_definitions ** /////////////////////// |
| template <class Storage> |
| inline ::emboss::support::Maybe<bool> |
| Generic${parent_type}View<Storage>::has_${name}() const { |
| return ${field_exists}; |
| } |
| |
| |
| // ** structure_single_parameter_field_method_declarations ** ////////////////// |
| private: |
| // TODO(bolms): Is there any harm if these are public methods? |
| constexpr ::emboss::support::MaybeConstantView</**/ ${logical_type}> |
| ${name}() const { |
| return parameters_initialized_ |
| ? ::emboss::support::MaybeConstantView</**/ ${logical_type}>( |
| ${name}_) |
| : ::emboss::support::MaybeConstantView</**/ ${logical_type}>(); |
| } |
| constexpr ::emboss::support::Maybe<bool> has_${name}() const { |
| return ::emboss::support::Maybe<bool>(parameters_initialized_); |
| } |
| |
| |
| // ** enum_declaration ** ////////////////////////////////////////////////////// |
| enum class ${enum} : ${enum_type}; |
| |
| |
| // ** enum_definition ** /////////////////////////////////////////////////////// |
| enum class ${enum} : ${enum_type} { |
| ${enum_values} |
| }; |
| |
| // This setup (ab)uses the fact that C++ templates can be defined in many |
| // translation units, but will be collapsed to a single definition at link time |
| // (or no definition, if no client code instantiates the template). |
| // |
| // Emboss could accomplish almost the same result by generating multiple .cc |
| // files (one per function), but Bazel doesn't have great support for specifying |
| // "the output of this rule is an indeterminate number of files, all of which |
| // should be used as input to this other rule," which would be necessary to |
| // generate all the .cc files and then build and link them into a library. |
| // ** enum_traits ** /////////////////////////////////////////////////////////// |
| template <class Enum> |
| class EnumTraits; |
| |
| template <> |
| class EnumTraits<${enum}> final { |
| public: |
| static bool TryToGetEnumFromName(const char *emboss_reserved_local_name, |
| ${enum} *emboss_reserved_local_result) { |
| if (emboss_reserved_local_name == nullptr) return false; |
| // TODO(bolms): The generated code here would be much more efficient for |
| // large enums if the mapping were performed using a prefix trie rather than |
| // repeated strcmp(). |
| ${enum_from_name_cases} |
| return false; |
| } |
| |
| static const char *TryToGetNameFromEnum( |
| ${enum} emboss_reserved_local_value) { |
| switch (emboss_reserved_local_value) { |
| ${name_from_enum_cases} |
| default: return nullptr; |
| } |
| } |
| |
| static bool EnumIsKnown(${enum} emboss_reserved_local_value) { |
| switch (emboss_reserved_local_value) { |
| ${enum_is_known_cases} |
| default: |
| return false; |
| } |
| } |
| |
| static ::std::ostream &SendToOstream(::std::ostream &emboss_reserved_local_os, |
| ${enum} emboss_reserved_local_value) { |
| const char *emboss_reserved_local_name = |
| TryToGetNameFromEnum(emboss_reserved_local_value); |
| if (emboss_reserved_local_name == nullptr) { |
| emboss_reserved_local_os |
| << static_cast</**/ ::std::underlying_type<${enum}>::type>( |
| emboss_reserved_local_value); |
| } else { |
| emboss_reserved_local_os << emboss_reserved_local_name; |
| } |
| return emboss_reserved_local_os; |
| } |
| }; |
| |
| // These functions are intended to be found via ADL. |
| static inline bool TryToGetEnumFromName( |
| const char *emboss_reserved_local_name, |
| ${enum} *emboss_reserved_local_result) { |
| return EnumTraits<${enum}>::TryToGetEnumFromName( |
| emboss_reserved_local_name, emboss_reserved_local_result); |
| } |
| |
| static inline const char *TryToGetNameFromEnum( |
| ${enum} emboss_reserved_local_value) { |
| return EnumTraits<${enum}>::TryToGetNameFromEnum( |
| emboss_reserved_local_value); |
| } |
| |
| static inline bool EnumIsKnown(${enum} emboss_reserved_local_value) { |
| return EnumTraits<${enum}>::EnumIsKnown(emboss_reserved_local_value); |
| } |
| |
| static inline ::std::ostream &operator<<( |
| ::std::ostream &emboss_reserved_local_os, |
| ${enum} emboss_reserved_local_value) { |
| return EnumTraits<${enum}>::SendToOstream(emboss_reserved_local_os, |
| emboss_reserved_local_value); |
| } |
| |
| // ** enum_from_name_case ** /////////////////////////////////////////////////// |
| if (!strcmp("${name}", emboss_reserved_local_name)) { |
| *emboss_reserved_local_result = ${enum}::${value}; |
| return true; |
| } |
| |
| // ** name_from_enum_case ** /////////////////////////////////////////////////// |
| case ${enum}::${value}: return "${name}"; |
| |
| // ** enum_is_known_case ** //////////////////////////////////////////////////// |
| case ${enum}::${name}: return true; |
| |
| // ** enum_value ** //////////////////////////////////////////////////////////// |
| ${name} = ${value}, |
| |
| // ** enum_using_statement ** ////////////////////////////////////////////////// |
| using ${name} = ${component}; |