// 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 #include #include #include #include #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 Generic${name}View; // ** structure_view_class ** ////////////////////////////////////////////////// template struct EmbossReservedInternalIsGeneric${name}View; template 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 Generic${name}View( const Generic${name}View &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 ::type>::type>::value>::type> explicit Generic${name}View( ${constructor_parameters} Arg &&emboss_reserved_local_arg) : backing_(::std::forward( emboss_reserved_local_arg)) ${parameter_initializers} ${initialize_parameters_initialized_true} {} template explicit Generic${name}View( ${constructor_parameters} Arg0 &&emboss_reserved_local_arg0, Arg1 &&emboss_reserved_local_arg1, Args &&... emboss_reserved_local_args) : backing_(::std::forward(emboss_reserved_local_arg0), ::std::forward(emboss_reserved_local_arg1), ::std::forward( emboss_reserved_local_args)...) ${parameter_initializers} ${initialize_parameters_initialized_true} {} template Generic${name}View &operator=( const Generic${name}View &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( IntrinsicSizeIn${units}().UncheckedRead()); } ${size_method} template bool Equals( Generic${name}View emboss_reserved_local_other) const { ${equals_method_body} return true; } template bool UncheckedEquals( Generic${name}View 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 void UncheckedCopyFrom( Generic${name}View emboss_reserved_local_other) const { backing_.UncheckedCopyFrom( emboss_reserved_local_other.BackingStorage(), emboss_reserved_local_other.IntrinsicSizeIn${units}().UncheckedRead()); } template void CopyFrom( Generic${name}View emboss_reserved_local_other) const { backing_.CopyFrom( emboss_reserved_local_other.BackingStorage(), emboss_reserved_local_other.IntrinsicSizeIn${units}().Read()); } template bool TryToCopyFrom( Generic${name}View 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 friend class Generic${name}View; }; using ${name}View = Generic${name}View; using ${name}Writer = Generic${name}View; template struct EmbossReservedInternalIsGeneric${name}View { static constexpr const bool value = false; }; template struct EmbossReservedInternalIsGeneric${name}View< Generic${name}View> { static constexpr const bool value = true; }; template inline Generic${name}View< /**/ ::emboss::support::ContiguousBuffer< typename ::std::remove_reference< decltype(*::std::declval()->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()->data())>::type, 1, 0>>( ${forwarded_parameters} ::std::forward(emboss_reserved_local_arg)); } template inline Generic${name}View> Make${name}View(${constructor_parameters} T *emboss_reserved_local_data, ::std::size_t emboss_reserved_local_size) { return Generic${name}View>( ${forwarded_parameters} emboss_reserved_local_data, emboss_reserved_local_size); } template inline Generic${name}View< /**/ ::emboss::support::ContiguousBuffer> MakeAligned${name}View( ${constructor_parameters} T *emboss_reserved_local_data, ::std::size_t emboss_reserved_local_size) { return Generic${name}View< /**/ ::emboss::support::ContiguousBuffer>( ${forwarded_parameters} emboss_reserved_local_data, emboss_reserved_local_size); } // ** struct_text_stream ** //////////////////////////////////////////////////// template 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 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(IntrinsicSizeIn${units}().Read()); } static constexpr bool SizeIsKnown() { return IntrinsicSizeIn${units}().Ok(); } // ** runtime_structure_size_method ** ///////////////////////////////////////// ::std::size_t SizeIn${units}() const { return static_cast(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 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 // ** 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 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 has_${name}() const; // ** structure_single_field_method_definitions ** ///////////////////////////// template inline typename ${type_reader} Generic${parent_type}View::${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 inline ::emboss::support::Maybe Generic${parent_type}View::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 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 has_${name}() { return ::emboss::support::Maybe(true); } // ** structure_single_const_virtual_field_method_definitions ** /////////////// namespace ${parent_type} { inline constexpr ${logical_type} ${name}() { return ${read_value}.ValueOrDefault(); } } // namespace ${parent_type} template inline constexpr ${logical_type} Generic${parent_type}View::${virtual_view_type_name}::Read() { return ${parent_type}::${name}(); } template 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 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 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 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 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 bool UpdateFromTextStream(Stream *emboss_reserved_local_stream) { return ::emboss::support::ReadIntegerFromTextStream( this, emboss_reserved_local_stream); } // ** structure_single_virtual_field_method_definitions ** ///////////////////// template inline typename Generic${parent_type}View::${virtual_view_type_name} Generic${parent_type}View::${name}() const { return typename Generic${parent_type}View::${virtual_view_type_name}( *this); } template inline ::emboss::support::Maybe Generic${parent_type}View::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 has_${name}() const; // ** struct_single_field_indirect_method_definitions ** /////////////////////// template inline ::emboss::support::Maybe Generic${parent_type}View::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 ${name}() const { return parameters_initialized_ ? ::emboss::support::MaybeConstantView( ${name}_) : ::emboss::support::MaybeConstantView(); } constexpr ::emboss::support::Maybe has_${name}() const { return ::emboss::support::Maybe(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 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::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};