File: stringify_util.h

package info (click to toggle)
golang-github-google-flatbuffers 24.12.23-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 17,704 kB
  • sloc: cpp: 53,217; python: 6,900; cs: 5,566; java: 4,370; php: 1,460; javascript: 1,061; xml: 1,016; sh: 886; makefile: 13
file content (189 lines) | stat: -rw-r--r-- 6,970 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * Copyright 2020 Google Inc. All rights reserved.
 *
 * 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
 *
 *     http://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.
 */

// This contains some utilities/examples for how to leverage the static reflec-
// tion features of tables and structs in the C++17 code generation to recur-
// sively produce a string representation of any Flatbuffer table or struct use
// compile-time iteration over the fields. Note that this code is completely
// generic in that it makes no reference to any particular Flatbuffer type.

#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/util.h"

namespace cpp17 {

// User calls this; need to forward declare it since it is called recursively.
template<typename T>
std::optional<std::string> StringifyFlatbufferValue(
    T &&val, const std::string &indent = "");

namespace detail {

/*******************************************************************************
** Metaprogramming helpers for detecting Flatbuffers Tables, Structs, & Vectors.
*******************************************************************************/
template<typename FBS, typename = void>
struct is_flatbuffers_table_or_struct : std::false_type {};

// We know it's a table or struct when it has a Traits subclass.
template<typename FBS>
struct is_flatbuffers_table_or_struct<FBS, std::void_t<typename FBS::Traits>>
    : std::true_type {};

template<typename FBS>
inline constexpr bool is_flatbuffers_table_or_struct_v =
    is_flatbuffers_table_or_struct<FBS>::value;

template<typename T> struct is_flatbuffers_vector : std::false_type {};

template<typename T>
struct is_flatbuffers_vector<flatbuffers::Vector<T>> : std::true_type {};

template<typename T>
inline constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector<T>::value;

/*******************************************************************************
** Compile-time Iteration & Recursive Stringification over Flatbuffers types.
*******************************************************************************/
template<size_t Index, typename FBS>
std::string AddStringifiedField(const FBS &fbs, const std::string &indent) {
  auto value_string =
      StringifyFlatbufferValue(fbs.template get_field<Index>(), indent);
  if (!value_string) { return ""; }
  return indent + FBS::Traits::field_names[Index] + " = " + *value_string +
         "\n";
}

template<typename FBS, size_t... Indexes>
std::string StringifyTableOrStructImpl(const FBS &fbs,
                                       const std::string &indent,
                                       std::index_sequence<Indexes...>) {
  // This line is where the compile-time iteration happens!
  return (AddStringifiedField<Indexes>(fbs, indent) + ...);
}

template<typename FBS>
std::string StringifyTableOrStruct(const FBS &fbs, const std::string &indent) {
  (void)fbs;
  (void)indent;
  static constexpr size_t field_count = FBS::Traits::fields_number;
  std::string out;
  if constexpr (field_count > 0) {
    out = std::string(FBS::Traits::fully_qualified_name) + "{\n" +
          StringifyTableOrStructImpl(fbs, indent + "  ",
                                     std::make_index_sequence<field_count>{}) +
          indent + '}';
  }
  return out;
}

template<typename T>
std::string StringifyVector(const flatbuffers::Vector<T> &vec,
                            const std::string &indent) {
  const auto prologue = indent + std::string("  ");
  const auto epilogue = std::string(",\n");
  std::string text;
  text += "[\n";
  for (auto it = vec.cbegin(), end = vec.cend(); it != end; ++it) {
    text += prologue;
    text += StringifyFlatbufferValue(*it).value_or("(field absent)");
    text += epilogue;
  }
  if (vec.cbegin() != vec.cend()) {
    text.resize(text.size() - epilogue.size());
  }
  text += '\n' + indent + ']';
  return text;
}

template<typename T> std::string StringifyArithmeticType(T val) {
  return flatbuffers::NumToString(val);
}

}  // namespace detail

/*******************************************************************************
** Take any flatbuffer type (table, struct, Vector, int...) and stringify it.
*******************************************************************************/
template<typename T>
std::optional<std::string> StringifyFlatbufferValue(T &&val,
                                                    const std::string &indent) {
  (void)indent;
  constexpr bool is_pointer = std::is_pointer_v<std::remove_reference_t<T>>;
  if constexpr (is_pointer) {
    if (val == nullptr) return std::nullopt;  // Field is absent.
  }
  using decayed =
      std::decay_t<std::remove_pointer_t<std::remove_reference_t<T>>>;

  // Is it a Flatbuffers Table or Struct?
  if constexpr (detail::is_flatbuffers_table_or_struct_v<decayed>) {
    // We have a nested table or struct; use recursion!
    if constexpr (is_pointer)
      return detail::StringifyTableOrStruct(*val, indent);
    else
      return detail::StringifyTableOrStruct(val, indent);
  }

  // Is it an 8-bit number?  If so, print it like an int (not char).
  else if constexpr (std::is_same_v<decayed, int8_t> ||
                     std::is_same_v<decayed, uint8_t>) {
    return detail::StringifyArithmeticType(static_cast<int>(val));
  }

  // Is it an enum? If so, print it like an int, since Flatbuffers doesn't yet
  // have type-based reflection for enums, so we can't print the enum's name :(
  else if constexpr (std::is_enum_v<decayed>) {
    return StringifyFlatbufferValue(
        static_cast<std::underlying_type_t<decayed>>(val), indent);
  }

  // Is it an int, double, float, uint32_t, etc.?
  else if constexpr (std::is_arithmetic_v<decayed>) {
    return detail::StringifyArithmeticType(val);
  }

  // Is it a Flatbuffers string?
  else if constexpr (std::is_same_v<decayed, flatbuffers::String>) {
    return '"' + val->str() + '"';
  }

  // Is it a Flatbuffers Vector?
  else if constexpr (detail::is_flatbuffers_vector_v<decayed>) {
    return detail::StringifyVector(*val, indent);
  }

  // Is it a void pointer?
  else if constexpr (std::is_same_v<decayed, void>) {
    // Can't format it.
    return std::nullopt;
  }

  else {
    // Not sure how to format this type, whatever it is.
    static_assert(sizeof(T) != sizeof(T),
                  "Do not know how to format this type T (the compiler error "
                  "should tell you nearby what T is).");
  }
}

}  // namespace cpp17