| // 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. | |
| // ** outline ** /////////////////////////////////////////////////////////////// | |
| // Generated by the Emboss compiler. DO NOT EDIT! | |
| #ifndef $_header_guard_$ | |
| #define $_header_guard_$ | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <algorithm> | |
| #include <ostream> | |
| #include <type_traits> | |
| #include <utility> | |
| #include "public/emboss_cpp_util.h" | |
| $_includes_$ | |
| $_body_$ | |
| #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()} {} | |
| // 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()); | |
| } | |
| 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(" }"); | |
| } | |
| } | |
| $_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 bool value = false; | |
| }; | |
| template <class Storage> | |
| struct EmbossReservedInternalIsGeneric$_name_$View< | |
| Generic$_name_$View<Storage>> { | |
| static constexpr 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); | |
| } | |
| // ** 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)) { | |
| 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"); | |
| } | |
| } | |
| // ** write_read_only_field_to_text_stream ** ////////////////////////////////// | |
| if (has_$_field_name_$().ValueOr(false) && | |
| emboss_reserved_local_field_options.comments()) { | |
| 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"); | |
| } | |
| // ** 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) { | |
| 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. | |
| if ($_parameters_known_$ has_$_name_$().ValueOr(false) && $_size_$.Known() && | |
| $_size_$.ValueOr(0) >= 0 && $_offset_$.Known() && | |
| $_offset_$.ValueOr(0) >= 0) { | |
| return $_type_reader_$( | |
| $_parameter_values_$ backing_ | |
| .template GetOffsetStorage<$_alignment_$, $_static_offset_$>( | |
| $_offset_$.ValueOrDefault(), $_size_$.ValueOrDefault())); | |
| } else { | |
| 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 ** ////////////// | |
| 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 $_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 ** //////////////////// | |
| 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); | |
| } | |
| $_write_methods_$ | |
| private: | |
| ::emboss::support::Maybe</**/ $_logical_type_$> MaybeRead() const { | |
| return $_read_value_$; | |
| } | |
| static constexpr bool ValueIsOk( | |
| $_logical_type_$ emboss_reserved_local_value) { | |
| 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) { | |
| EMBOSS_CHECK(TryToWrite(emboss_reserved_local_value)); | |
| } | |
| 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. | |
| 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_$::$_name_$; | |
| return true; | |
| } | |
| // ** name_from_enum_case ** /////////////////////////////////////////////////// | |
| case $_enum_$::$_name_$: return "$_name_$"; | |
| // ** enum_is_known_case ** //////////////////////////////////////////////////// | |
| case $_enum_$::$_name_$: return true; | |
| // ** enum_value ** //////////////////////////////////////////////////////////// | |
| $_name_$ = $_value_$, | |
| // ** enum_using_statement ** ////////////////////////////////////////////////// | |
| using $_name_$ = $_component_$; |