blob: 5325852aea902f19eb74fd066f2e155bb3916e20 [file] [log] [blame]
// 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};