xref: /aosp_15_r20/external/flatbuffers/tests/cpp17/stringify_util.h (revision 890232f25432b36107d06881e0a25aaa6b473652)
1*890232f2SAndroid Build Coastguard Worker /*
2*890232f2SAndroid Build Coastguard Worker  * Copyright 2020 Google Inc. All rights reserved.
3*890232f2SAndroid Build Coastguard Worker  *
4*890232f2SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*890232f2SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*890232f2SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*890232f2SAndroid Build Coastguard Worker  *
8*890232f2SAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
9*890232f2SAndroid Build Coastguard Worker  *
10*890232f2SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*890232f2SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*890232f2SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*890232f2SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*890232f2SAndroid Build Coastguard Worker  * limitations under the License.
15*890232f2SAndroid Build Coastguard Worker  */
16*890232f2SAndroid Build Coastguard Worker 
17*890232f2SAndroid Build Coastguard Worker // This contains some utilities/examples for how to leverage the static reflec-
18*890232f2SAndroid Build Coastguard Worker // tion features of tables and structs in the C++17 code generation to recur-
19*890232f2SAndroid Build Coastguard Worker // sively produce a string representation of any Flatbuffer table or struct use
20*890232f2SAndroid Build Coastguard Worker // compile-time iteration over the fields. Note that this code is completely
21*890232f2SAndroid Build Coastguard Worker // generic in that it makes no reference to any particular Flatbuffer type.
22*890232f2SAndroid Build Coastguard Worker 
23*890232f2SAndroid Build Coastguard Worker #include <optional>
24*890232f2SAndroid Build Coastguard Worker #include <string>
25*890232f2SAndroid Build Coastguard Worker #include <type_traits>
26*890232f2SAndroid Build Coastguard Worker #include <utility>
27*890232f2SAndroid Build Coastguard Worker #include <vector>
28*890232f2SAndroid Build Coastguard Worker 
29*890232f2SAndroid Build Coastguard Worker #include "flatbuffers/flatbuffers.h"
30*890232f2SAndroid Build Coastguard Worker #include "flatbuffers/util.h"
31*890232f2SAndroid Build Coastguard Worker 
32*890232f2SAndroid Build Coastguard Worker namespace cpp17 {
33*890232f2SAndroid Build Coastguard Worker 
34*890232f2SAndroid Build Coastguard Worker // User calls this; need to forward declare it since it is called recursively.
35*890232f2SAndroid Build Coastguard Worker template<typename T>
36*890232f2SAndroid Build Coastguard Worker std::optional<std::string> StringifyFlatbufferValue(
37*890232f2SAndroid Build Coastguard Worker     T &&val, const std::string &indent = "");
38*890232f2SAndroid Build Coastguard Worker 
39*890232f2SAndroid Build Coastguard Worker namespace detail {
40*890232f2SAndroid Build Coastguard Worker 
41*890232f2SAndroid Build Coastguard Worker /*******************************************************************************
42*890232f2SAndroid Build Coastguard Worker ** Metaprogramming helpers for detecting Flatbuffers Tables, Structs, & Vectors.
43*890232f2SAndroid Build Coastguard Worker *******************************************************************************/
44*890232f2SAndroid Build Coastguard Worker template<typename FBS, typename = void>
45*890232f2SAndroid Build Coastguard Worker struct is_flatbuffers_table_or_struct : std::false_type {};
46*890232f2SAndroid Build Coastguard Worker 
47*890232f2SAndroid Build Coastguard Worker // We know it's a table or struct when it has a Traits subclass.
48*890232f2SAndroid Build Coastguard Worker template<typename FBS>
49*890232f2SAndroid Build Coastguard Worker struct is_flatbuffers_table_or_struct<FBS, std::void_t<typename FBS::Traits>>
50*890232f2SAndroid Build Coastguard Worker     : std::true_type {};
51*890232f2SAndroid Build Coastguard Worker 
52*890232f2SAndroid Build Coastguard Worker template<typename FBS>
53*890232f2SAndroid Build Coastguard Worker inline constexpr bool is_flatbuffers_table_or_struct_v =
54*890232f2SAndroid Build Coastguard Worker     is_flatbuffers_table_or_struct<FBS>::value;
55*890232f2SAndroid Build Coastguard Worker 
56*890232f2SAndroid Build Coastguard Worker template<typename T> struct is_flatbuffers_vector : std::false_type {};
57*890232f2SAndroid Build Coastguard Worker 
58*890232f2SAndroid Build Coastguard Worker template<typename T>
59*890232f2SAndroid Build Coastguard Worker struct is_flatbuffers_vector<flatbuffers::Vector<T>> : std::true_type {};
60*890232f2SAndroid Build Coastguard Worker 
61*890232f2SAndroid Build Coastguard Worker template<typename T>
62*890232f2SAndroid Build Coastguard Worker inline constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector<T>::value;
63*890232f2SAndroid Build Coastguard Worker 
64*890232f2SAndroid Build Coastguard Worker /*******************************************************************************
65*890232f2SAndroid Build Coastguard Worker ** Compile-time Iteration & Recursive Stringification over Flatbuffers types.
66*890232f2SAndroid Build Coastguard Worker *******************************************************************************/
67*890232f2SAndroid Build Coastguard Worker template<size_t Index, typename FBS>
68*890232f2SAndroid Build Coastguard Worker std::string AddStringifiedField(const FBS &fbs, const std::string &indent) {
69*890232f2SAndroid Build Coastguard Worker   auto value_string =
70*890232f2SAndroid Build Coastguard Worker       StringifyFlatbufferValue(fbs.template get_field<Index>(), indent);
71*890232f2SAndroid Build Coastguard Worker   if (!value_string) { return ""; }
72*890232f2SAndroid Build Coastguard Worker   return indent + FBS::Traits::field_names[Index] + " = " + *value_string +
73*890232f2SAndroid Build Coastguard Worker          "\n";
74*890232f2SAndroid Build Coastguard Worker }
75*890232f2SAndroid Build Coastguard Worker 
76*890232f2SAndroid Build Coastguard Worker template<typename FBS, size_t... Indexes>
77*890232f2SAndroid Build Coastguard Worker std::string StringifyTableOrStructImpl(const FBS &fbs,
78*890232f2SAndroid Build Coastguard Worker                                        const std::string &indent,
79*890232f2SAndroid Build Coastguard Worker                                        std::index_sequence<Indexes...>) {
80*890232f2SAndroid Build Coastguard Worker   // This line is where the compile-time iteration happens!
81*890232f2SAndroid Build Coastguard Worker   return (AddStringifiedField<Indexes>(fbs, indent) + ...);
82*890232f2SAndroid Build Coastguard Worker }
83*890232f2SAndroid Build Coastguard Worker 
84*890232f2SAndroid Build Coastguard Worker template<typename FBS>
85*890232f2SAndroid Build Coastguard Worker std::string StringifyTableOrStruct(const FBS &fbs, const std::string &indent) {
86*890232f2SAndroid Build Coastguard Worker   (void)fbs;
87*890232f2SAndroid Build Coastguard Worker   (void)indent;
88*890232f2SAndroid Build Coastguard Worker   static constexpr size_t field_count = FBS::Traits::fields_number;
89*890232f2SAndroid Build Coastguard Worker   std::string out;
90*890232f2SAndroid Build Coastguard Worker   if constexpr (field_count > 0) {
91*890232f2SAndroid Build Coastguard Worker     out = std::string(FBS::Traits::fully_qualified_name) + "{\n" +
92*890232f2SAndroid Build Coastguard Worker           StringifyTableOrStructImpl(fbs, indent + "  ",
93*890232f2SAndroid Build Coastguard Worker                                      std::make_index_sequence<field_count>{}) +
94*890232f2SAndroid Build Coastguard Worker           indent + '}';
95*890232f2SAndroid Build Coastguard Worker   }
96*890232f2SAndroid Build Coastguard Worker   return out;
97*890232f2SAndroid Build Coastguard Worker }
98*890232f2SAndroid Build Coastguard Worker 
99*890232f2SAndroid Build Coastguard Worker template<typename T>
100*890232f2SAndroid Build Coastguard Worker std::string StringifyVector(const flatbuffers::Vector<T> &vec,
101*890232f2SAndroid Build Coastguard Worker                             const std::string &indent) {
102*890232f2SAndroid Build Coastguard Worker   const auto prologue = indent + std::string("  ");
103*890232f2SAndroid Build Coastguard Worker   const auto epilogue = std::string(",\n");
104*890232f2SAndroid Build Coastguard Worker   std::string text;
105*890232f2SAndroid Build Coastguard Worker   text += "[\n";
106*890232f2SAndroid Build Coastguard Worker   for (auto it = vec.cbegin(), end = vec.cend(); it != end; ++it) {
107*890232f2SAndroid Build Coastguard Worker     text += prologue;
108*890232f2SAndroid Build Coastguard Worker     text += StringifyFlatbufferValue(*it).value_or("(field absent)");
109*890232f2SAndroid Build Coastguard Worker     text += epilogue;
110*890232f2SAndroid Build Coastguard Worker   }
111*890232f2SAndroid Build Coastguard Worker   if (vec.cbegin() != vec.cend()) {
112*890232f2SAndroid Build Coastguard Worker     text.resize(text.size() - epilogue.size());
113*890232f2SAndroid Build Coastguard Worker   }
114*890232f2SAndroid Build Coastguard Worker   text += '\n' + indent + ']';
115*890232f2SAndroid Build Coastguard Worker   return text;
116*890232f2SAndroid Build Coastguard Worker }
117*890232f2SAndroid Build Coastguard Worker 
118*890232f2SAndroid Build Coastguard Worker template<typename T> std::string StringifyArithmeticType(T val) {
119*890232f2SAndroid Build Coastguard Worker   return flatbuffers::NumToString(val);
120*890232f2SAndroid Build Coastguard Worker }
121*890232f2SAndroid Build Coastguard Worker 
122*890232f2SAndroid Build Coastguard Worker }  // namespace detail
123*890232f2SAndroid Build Coastguard Worker 
124*890232f2SAndroid Build Coastguard Worker /*******************************************************************************
125*890232f2SAndroid Build Coastguard Worker ** Take any flatbuffer type (table, struct, Vector, int...) and stringify it.
126*890232f2SAndroid Build Coastguard Worker *******************************************************************************/
127*890232f2SAndroid Build Coastguard Worker template<typename T>
128*890232f2SAndroid Build Coastguard Worker std::optional<std::string> StringifyFlatbufferValue(T &&val,
129*890232f2SAndroid Build Coastguard Worker                                                     const std::string &indent) {
130*890232f2SAndroid Build Coastguard Worker   (void)indent;
131*890232f2SAndroid Build Coastguard Worker   constexpr bool is_pointer = std::is_pointer_v<std::remove_reference_t<T>>;
132*890232f2SAndroid Build Coastguard Worker   if constexpr (is_pointer) {
133*890232f2SAndroid Build Coastguard Worker     if (val == nullptr) return std::nullopt;  // Field is absent.
134*890232f2SAndroid Build Coastguard Worker   }
135*890232f2SAndroid Build Coastguard Worker   using decayed =
136*890232f2SAndroid Build Coastguard Worker       std::decay_t<std::remove_pointer_t<std::remove_reference_t<T>>>;
137*890232f2SAndroid Build Coastguard Worker 
138*890232f2SAndroid Build Coastguard Worker   // Is it a Flatbuffers Table or Struct?
139*890232f2SAndroid Build Coastguard Worker   if constexpr (detail::is_flatbuffers_table_or_struct_v<decayed>) {
140*890232f2SAndroid Build Coastguard Worker     // We have a nested table or struct; use recursion!
141*890232f2SAndroid Build Coastguard Worker     if constexpr (is_pointer)
142*890232f2SAndroid Build Coastguard Worker       return detail::StringifyTableOrStruct(*val, indent);
143*890232f2SAndroid Build Coastguard Worker     else
144*890232f2SAndroid Build Coastguard Worker       return detail::StringifyTableOrStruct(val, indent);
145*890232f2SAndroid Build Coastguard Worker   }
146*890232f2SAndroid Build Coastguard Worker 
147*890232f2SAndroid Build Coastguard Worker   // Is it an 8-bit number?  If so, print it like an int (not char).
148*890232f2SAndroid Build Coastguard Worker   else if constexpr (std::is_same_v<decayed, int8_t> ||
149*890232f2SAndroid Build Coastguard Worker                      std::is_same_v<decayed, uint8_t>) {
150*890232f2SAndroid Build Coastguard Worker     return detail::StringifyArithmeticType(static_cast<int>(val));
151*890232f2SAndroid Build Coastguard Worker   }
152*890232f2SAndroid Build Coastguard Worker 
153*890232f2SAndroid Build Coastguard Worker   // Is it an enum? If so, print it like an int, since Flatbuffers doesn't yet
154*890232f2SAndroid Build Coastguard Worker   // have type-based reflection for enums, so we can't print the enum's name :(
155*890232f2SAndroid Build Coastguard Worker   else if constexpr (std::is_enum_v<decayed>) {
156*890232f2SAndroid Build Coastguard Worker     return StringifyFlatbufferValue(
157*890232f2SAndroid Build Coastguard Worker         static_cast<std::underlying_type_t<decayed>>(val), indent);
158*890232f2SAndroid Build Coastguard Worker   }
159*890232f2SAndroid Build Coastguard Worker 
160*890232f2SAndroid Build Coastguard Worker   // Is it an int, double, float, uint32_t, etc.?
161*890232f2SAndroid Build Coastguard Worker   else if constexpr (std::is_arithmetic_v<decayed>) {
162*890232f2SAndroid Build Coastguard Worker     return detail::StringifyArithmeticType(val);
163*890232f2SAndroid Build Coastguard Worker   }
164*890232f2SAndroid Build Coastguard Worker 
165*890232f2SAndroid Build Coastguard Worker   // Is it a Flatbuffers string?
166*890232f2SAndroid Build Coastguard Worker   else if constexpr (std::is_same_v<decayed, flatbuffers::String>) {
167*890232f2SAndroid Build Coastguard Worker     return '"' + val->str() + '"';
168*890232f2SAndroid Build Coastguard Worker   }
169*890232f2SAndroid Build Coastguard Worker 
170*890232f2SAndroid Build Coastguard Worker   // Is it a Flatbuffers Vector?
171*890232f2SAndroid Build Coastguard Worker   else if constexpr (detail::is_flatbuffers_vector_v<decayed>) {
172*890232f2SAndroid Build Coastguard Worker     return detail::StringifyVector(*val, indent);
173*890232f2SAndroid Build Coastguard Worker   }
174*890232f2SAndroid Build Coastguard Worker 
175*890232f2SAndroid Build Coastguard Worker   // Is it a void pointer?
176*890232f2SAndroid Build Coastguard Worker   else if constexpr (std::is_same_v<decayed, void>) {
177*890232f2SAndroid Build Coastguard Worker     // Can't format it.
178*890232f2SAndroid Build Coastguard Worker     return std::nullopt;
179*890232f2SAndroid Build Coastguard Worker   }
180*890232f2SAndroid Build Coastguard Worker 
181*890232f2SAndroid Build Coastguard Worker   else {
182*890232f2SAndroid Build Coastguard Worker     // Not sure how to format this type, whatever it is.
183*890232f2SAndroid Build Coastguard Worker     static_assert(sizeof(T) != sizeof(T),
184*890232f2SAndroid Build Coastguard Worker                   "Do not know how to format this type T (the compiler error "
185*890232f2SAndroid Build Coastguard Worker                   "should tell you nearby what T is).");
186*890232f2SAndroid Build Coastguard Worker   }
187*890232f2SAndroid Build Coastguard Worker }
188*890232f2SAndroid Build Coastguard Worker 
189*890232f2SAndroid Build Coastguard Worker }  // namespace cpp17
190