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