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