From aa39ddf3f38ca41e1720731ec1fae71bfb119658 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Wed, 9 Dec 2020 21:07:05 -0500 Subject: [PATCH 01/21] [C++17] Add compile-time reflection for fields. Included in this commit is the following: - The C++ generator has been modified so that, when in C++17 mode, it will emit Table and Struct field traits that can be used at com- pile time as a form of static reflection. This includes field types, field names, and a tuple of field getter results. - Diffs to the cpp17 generated files. No other generated files are affected. - A unit test that also serves as an example. It demonstrates how to use the full power of this reflection to implement a full recursive JSON-like stringifier for Flatbuffers types, but without needing any runtime access to the *.fbs definition files; the computation is done using only static reflection. Tested on Linux with gcc 10.2.0. Fixes #6285. --- src/idl_gen_cpp.cpp | 165 +++++++++ .../generated_cpp17/monster_test_generated.h | 329 ++++++++++++++++++ .../optional_scalars_generated.h | 119 +++++++ tests/cpp17/stringify_util.h | 194 +++++++++++ tests/cpp17/test_cpp17.cpp | 134 +++++++ 5 files changed, 941 insertions(+) create mode 100644 tests/cpp17/stringify_util.h diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 742587a4836..48127f051ec 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -317,6 +317,11 @@ class CppGenerator : public BaseGenerator { if (opts_.gen_nullable) { code_ += "#pragma clang system_header\n\n"; } + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + code_ += "#include "; + code_ += ""; + } + code_ += "#include \"flatbuffers/flatbuffers.h\""; if (parser_.uses_flexbuffers_) { code_ += "#include \"flatbuffers/flexbuffers.h\""; @@ -2042,6 +2047,140 @@ class CppGenerator : public BaseGenerator { if (type.base_type == BASE_TYPE_UNION) { GenTableUnionAsGetters(field); } } + void GenTableFieldType(const FieldDef &field) { + const auto &type = field.value.type; + const auto offset_str = GenFieldOffsetName(field); + if (!field.IsScalarOptional()) { + std::string afterptr = " *" + NullableExtension(); + code_.SetValue("FIELD_TYPE", + GenTypeGet(type, "", "const ", afterptr.c_str(), true)); + code_ += " {{FIELD_TYPE}}\\"; + } else { + code_.SetValue("FIELD_TYPE", GenOptionalDecl(type)); + code_ += " {{FIELD_TYPE}}\\"; + } + } + + void GenStructFieldType(const FieldDef &field) { + const auto is_array = IsArray(field.value.type); + std::string field_type = GenTypeGet(field.value.type, "", + is_array ? "" : "const ", is_array ? "" : " &", true); + code_.SetValue("FIELD_TYPE", field_type); + code_ += " {{FIELD_TYPE}}\\"; + } + + // Sample for Vec3: + // + // using FieldTypes = std::tuple< + // float, + // float, + // float + // >; + // + void GenFieldTypes(const StructDef &struct_def, bool is_struct) { + code_ += " using FieldTypes = std::tuple<\\"; + if (struct_def.fields.vec.empty()) { + code_ += ">;"; + return; + } + code_ += ""; + // Generate the std::tuple elements. + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) { + // Deprecated fields won't be accessible. + continue; + } + if (is_struct) { + GenStructFieldType(field); + } else { + GenTableFieldType(field); + } + if (it+1 != struct_def.fields.vec.end() ) { + code_ += ","; + } + } + code_ += "\n >;"; + } + + // Sample for Vec3: + // + // FieldTypes fields_pack() const { + // return { + // x(), + // y(), + // z() + // }; + // } + // + void GenFieldsPack(const StructDef &struct_def) { + code_ += " FieldTypes fields_pack() const {"; + code_ += " return {\\"; + if (struct_def.fields.vec.empty()) { + code_ += "};"; + code_ += " }"; + return; + } + code_ += ""; + // Generate the fields_pack elements. + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) { + // Deprecated fields won't be accessible. + continue; + } + code_.SetValue("FIELD_NAME", Name(field)); + code_ += " {{FIELD_NAME}}()\\"; + if (it+1 != struct_def.fields.vec.end() ) { + code_ += ","; + } + } + code_ += "\n };"; + code_ += " }"; + } + + // Sample for Vec3: + // + // static constexpr std::array field_names = { + // "x", + // "y", + // "z" + // }; + // + void GenFieldNames(const StructDef &struct_def) { + int non_deprecated_field_count = std::count_if( + struct_def.fields.vec.begin(), struct_def.fields.vec.end(), + [](const FieldDef *field) { + return !field->deprecated; + }); + code_ += " static constexpr std::array<\\"; + code_.SetValue("FIELD_COUNT", + std::to_string(non_deprecated_field_count)); + code_ += "const char *, {{FIELD_COUNT}}> field_names = {\\"; + if (struct_def.fields.vec.empty()) { + code_ += "};"; + return; + } + code_ += ""; + // Generate the field_names elements. + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) { + // Deprecated fields won't be accessible. + continue; + } + code_.SetValue("FIELD_NAME", Name(field)); + code_ += " \"{{FIELD_NAME}}\"\\"; + if (it+1 != struct_def.fields.vec.end() ) { + code_ += ","; + } + } + code_ += "\n };"; + } + void GenTableFieldSetter(const FieldDef &field) { const auto &type = field.value.type; const bool is_scalar = IsScalar(type.base_type); @@ -2182,6 +2321,11 @@ class CppGenerator : public BaseGenerator { if (field.key) { GenKeyFieldMethods(field); } } + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + GenFieldTypes(struct_def, /*is_struct=*/false); + GenFieldsPack(struct_def); + } + // Generate a verifier function that can check a buffer from an untrusted // source will never cause reads outside the buffer. code_ += " bool Verify(flatbuffers::Verifier &verifier) const {"; @@ -2393,6 +2537,8 @@ class CppGenerator : public BaseGenerator { code_ += "struct {{STRUCT_NAME}}::Traits {"; code_ += " using type = {{STRUCT_NAME}};"; code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};"; + code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; + GenFieldNames(struct_def); code_ += "};"; code_ += ""; } @@ -3172,6 +3318,8 @@ class CppGenerator : public BaseGenerator { code_ += ""; code_ += " public:"; + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { code_ += " struct Traits;"; } + // Make TypeTable accessible via the generated struct. if (opts_.mini_reflect != IDLOptions::kNone) { code_ += @@ -3257,12 +3405,29 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("NATIVE_NAME", Name(struct_def)); GenOperatorNewDelete(struct_def); + + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + GenFieldTypes(struct_def, /*is_struct=*/true); + GenFieldsPack(struct_def); + } + code_ += "};"; code_.SetValue("STRUCT_BYTE_SIZE", NumToString(struct_def.bytesize)); code_ += "FLATBUFFERS_STRUCT_END({{STRUCT_NAME}}, {{STRUCT_BYTE_SIZE}});"; if (opts_.gen_compare) GenCompareOperator(struct_def, "()"); code_ += ""; + + // Definition for type traits for this struct type. This al- + // lows querying various compile-time traits of the struct. + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + code_ += "struct {{STRUCT_NAME}}::Traits {"; + code_ += " using type = {{STRUCT_NAME}};"; + code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; + GenFieldNames(struct_def); + code_ += "};"; + code_ += ""; + } } // Set up the correct namespace. Only open a namespace if the existing one is diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 77bc011bc76..26de0f2792c 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -4,6 +4,8 @@ #ifndef FLATBUFFERS_GENERATED_MONSTERTEST_MYGAME_EXAMPLE_H_ #define FLATBUFFERS_GENERATED_MONSTERTEST_MYGAME_EXAMPLE_H_ +#include + #include "flatbuffers/flatbuffers.h" #include "flatbuffers/flexbuffers.h" @@ -474,6 +476,7 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS { int8_t padding0__; public: + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return TestTypeTable(); } @@ -501,9 +504,28 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS { void mutate_b(int8_t _b) { flatbuffers::WriteScalar(&b_, _b); } + using FieldTypes = std::tuple< + int16_t, + int8_t + >; + FieldTypes fields_pack() const { + return { + a(), + b() + }; + } }; FLATBUFFERS_STRUCT_END(Test, 4); +struct Test::Traits { + using type = Test; + static constexpr auto name = "Test"; + static constexpr std::array field_names = { + "a", + "b" + }; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { private: float x_; @@ -517,6 +539,7 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { int16_t padding2__; public: + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return Vec3TypeTable(); } @@ -584,15 +607,47 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { MyGame::Example::Test &mutable_test3() { return test3_; } + using FieldTypes = std::tuple< + float, + float, + float, + double, + MyGame::Example::Color, + const MyGame::Example::Test & + >; + FieldTypes fields_pack() const { + return { + x(), + y(), + z(), + test1(), + test2(), + test3() + }; + } }; FLATBUFFERS_STRUCT_END(Vec3, 32); +struct Vec3::Traits { + using type = Vec3; + static constexpr auto name = "Vec3"; + static constexpr std::array field_names = { + "x", + "y", + "z", + "test1", + "test2", + "test3" + }; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { private: uint32_t id_; uint32_t distance_; public: + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return AbilityTypeTable(); } @@ -622,9 +677,28 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { void mutate_distance(uint32_t _distance) { flatbuffers::WriteScalar(&distance_, _distance); } + using FieldTypes = std::tuple< + uint32_t, + uint32_t + >; + FieldTypes fields_pack() const { + return { + id(), + distance() + }; + } }; FLATBUFFERS_STRUCT_END(Ability, 8); +struct Ability::Traits { + using type = Ability; + static constexpr auto name = "Ability"; + static constexpr std::array field_names = { + "id", + "distance" + }; +}; + } // namespace Example struct InParentNamespaceT : public flatbuffers::NativeTable { @@ -638,6 +712,10 @@ struct InParentNamespace FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { static const flatbuffers::TypeTable *MiniReflectTypeTable() { return InParentNamespaceTypeTable(); } + using FieldTypes = std::tuple<>; + FieldTypes fields_pack() const { + return {}; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && verifier.EndTable(); @@ -671,6 +749,8 @@ inline flatbuffers::Offset CreateInParentNamespace( struct InParentNamespace::Traits { using type = InParentNamespace; static auto constexpr Create = CreateInParentNamespace; + static constexpr auto name = "InParentNamespace"; + static constexpr std::array field_names = {}; }; flatbuffers::Offset CreateInParentNamespace(flatbuffers::FlatBufferBuilder &_fbb, const InParentNamespaceT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -688,6 +768,10 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { static const flatbuffers::TypeTable *MiniReflectTypeTable() { return MonsterTypeTable(); } + using FieldTypes = std::tuple<>; + FieldTypes fields_pack() const { + return {}; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && verifier.EndTable(); @@ -721,6 +805,8 @@ inline flatbuffers::Offset CreateMonster( struct Monster::Traits { using type = Monster; static auto constexpr Create = CreateMonster; + static constexpr auto name = "Monster"; + static constexpr std::array field_names = {}; }; flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -750,6 +836,14 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta bool mutate_color(MyGame::Example::Color _color) { return SetField(VT_COLOR, static_cast(_color), 2); } + using FieldTypes = std::tuple< + MyGame::Example::Color + >; + FieldTypes fields_pack() const { + return { + color() + }; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_COLOR) && @@ -789,6 +883,10 @@ inline flatbuffers::Offset CreateTestSimpleTableWithEnu struct TestSimpleTableWithEnum::Traits { using type = TestSimpleTableWithEnum; static auto constexpr Create = CreateTestSimpleTableWithEnum; + static constexpr auto name = "TestSimpleTableWithEnum"; + static constexpr std::array field_names = { + "color" + }; }; flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -830,6 +928,18 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool mutate_count(uint16_t _count) { return SetField(VT_COUNT, _count, 0); } + using FieldTypes = std::tuple< + const flatbuffers::String *, + int64_t, + uint16_t + >; + FieldTypes fields_pack() const { + return { + id(), + val(), + count() + }; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_ID) && @@ -882,6 +992,12 @@ inline flatbuffers::Offset CreateStat( struct Stat::Traits { using type = Stat; static auto constexpr Create = CreateStat; + static constexpr auto name = "Stat"; + static constexpr std::array field_names = { + "id", + "val", + "count" + }; }; inline flatbuffers::Offset CreateStatDirect( @@ -926,6 +1042,14 @@ struct Referrable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { int KeyCompareWithValue(uint64_t val) const { return static_cast(id() > val) - static_cast(id() < val); } + using FieldTypes = std::tuple< + uint64_t + >; + FieldTypes fields_pack() const { + return { + id() + }; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_ID) && @@ -965,6 +1089,10 @@ inline flatbuffers::Offset CreateReferrable( struct Referrable::Traits { using type = Referrable; static auto constexpr Create = CreateReferrable; + static constexpr auto name = "Referrable"; + static constexpr std::array field_names = { + "id" + }; }; flatbuffers::Offset CreateReferrable(flatbuffers::FlatBufferBuilder &_fbb, const ReferrableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -1409,6 +1537,110 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const MyGame::Example::Monster *testrequirednestedflatbuffer_nested_root() const { return flatbuffers::GetRoot(testrequirednestedflatbuffer()->Data()); } + using FieldTypes = std::tuple< + const MyGame::Example::Vec3 *, + int16_t, + int16_t, + const flatbuffers::String *, + const flatbuffers::Vector *, + MyGame::Example::Color, + MyGame::Example::Any, + const void *, + const flatbuffers::Vector *, + const flatbuffers::Vector> *, + const flatbuffers::Vector> *, + const MyGame::Example::Monster *, + const flatbuffers::Vector *, + const MyGame::Example::Stat *, + bool, + int32_t, + uint32_t, + int64_t, + uint64_t, + int32_t, + uint32_t, + int64_t, + uint64_t, + const flatbuffers::Vector *, + float, + float, + float, + const flatbuffers::Vector> *, + const flatbuffers::Vector *, + const flatbuffers::Vector *, + const flatbuffers::Vector *, + const flatbuffers::Vector *, + const flatbuffers::Vector *, + const MyGame::InParentNamespace *, + const flatbuffers::Vector> *, + uint64_t, + const flatbuffers::Vector *, + const flatbuffers::Vector> *, + uint64_t, + const flatbuffers::Vector *, + uint64_t, + const flatbuffers::Vector *, + MyGame::Example::AnyUniqueAliases, + const void *, + MyGame::Example::AnyAmbiguousAliases, + const void *, + const flatbuffers::Vector *, + MyGame::Example::Race, + const flatbuffers::Vector * + >; + FieldTypes fields_pack() const { + return { + pos(), + mana(), + hp(), + name(), + inventory(), + color(), + test_type(), + test(), + test4(), + testarrayofstring(), + testarrayoftables(), + enemy(), + testnestedflatbuffer(), + testempty(), + testbool(), + testhashs32_fnv1(), + testhashu32_fnv1(), + testhashs64_fnv1(), + testhashu64_fnv1(), + testhashs32_fnv1a(), + testhashu32_fnv1a(), + testhashs64_fnv1a(), + testhashu64_fnv1a(), + testarrayofbools(), + testf(), + testf2(), + testf3(), + testarrayofstring2(), + testarrayofsortedstruct(), + flex(), + test5(), + vector_of_longs(), + vector_of_doubles(), + parent_namespace_test(), + vector_of_referrables(), + single_weak_reference(), + vector_of_weak_references(), + vector_of_strong_referrables(), + co_owning_reference(), + vector_of_co_owning_references(), + non_owning_reference(), + vector_of_non_owning_references(), + any_unique_type(), + any_unique(), + any_ambiguous_type(), + any_ambiguous(), + vector_of_enums(), + signed_enum(), + testrequirednestedflatbuffer() + }; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_POS) && @@ -1792,6 +2024,58 @@ inline flatbuffers::Offset CreateMonster( struct Monster::Traits { using type = Monster; static auto constexpr Create = CreateMonster; + static constexpr auto name = "Monster"; + static constexpr std::array field_names = { + "pos", + "mana", + "hp", + "name", + "inventory", + "color", + "test_type", + "test", + "test4", + "testarrayofstring", + "testarrayoftables", + "enemy", + "testnestedflatbuffer", + "testempty", + "testbool", + "testhashs32_fnv1", + "testhashu32_fnv1", + "testhashs64_fnv1", + "testhashu64_fnv1", + "testhashs32_fnv1a", + "testhashu32_fnv1a", + "testhashs64_fnv1a", + "testhashu64_fnv1a", + "testarrayofbools", + "testf", + "testf2", + "testf3", + "testarrayofstring2", + "testarrayofsortedstruct", + "flex", + "test5", + "vector_of_longs", + "vector_of_doubles", + "parent_namespace_test", + "vector_of_referrables", + "single_weak_reference", + "vector_of_weak_references", + "vector_of_strong_referrables", + "co_owning_reference", + "vector_of_co_owning_references", + "non_owning_reference", + "vector_of_non_owning_references", + "any_unique_type", + "any_unique", + "any_ambiguous_type", + "any_ambiguous", + "vector_of_enums", + "signed_enum", + "testrequirednestedflatbuffer" + }; }; inline flatbuffers::Offset CreateMonsterDirect( @@ -2029,6 +2313,36 @@ struct TypeAliases FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { flatbuffers::Vector *mutable_vf64() { return GetPointer *>(VT_VF64); } + using FieldTypes = std::tuple< + int8_t, + uint8_t, + int16_t, + uint16_t, + int32_t, + uint32_t, + int64_t, + uint64_t, + float, + double, + const flatbuffers::Vector *, + const flatbuffers::Vector * + >; + FieldTypes fields_pack() const { + return { + i8(), + u8(), + i16(), + u16(), + i32(), + u32(), + i64(), + u64(), + f32(), + f64(), + v8(), + vf64() + }; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_I8) && @@ -2136,6 +2450,21 @@ inline flatbuffers::Offset CreateTypeAliases( struct TypeAliases::Traits { using type = TypeAliases; static auto constexpr Create = CreateTypeAliases; + static constexpr auto name = "TypeAliases"; + static constexpr std::array field_names = { + "i8", + "u8", + "i16", + "u16", + "i32", + "u32", + "i64", + "u64", + "f32", + "f64", + "v8", + "vf64" + }; }; inline flatbuffers::Offset CreateTypeAliasesDirect( diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index 7581e383b75..ebc76b3ec1e 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -4,6 +4,8 @@ #ifndef FLATBUFFERS_GENERATED_OPTIONALSCALARS_OPTIONAL_SCALARS_H_ #define FLATBUFFERS_GENERATED_OPTIONALSCALARS_OPTIONAL_SCALARS_H_ +#include + #include "flatbuffers/flatbuffers.h" namespace optional_scalars { @@ -348,6 +350,84 @@ struct ScalarStuff FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool mutate_default_enum(optional_scalars::OptionalByte _default_enum) { return SetField(VT_DEFAULT_ENUM, static_cast(_default_enum), 1); } + using FieldTypes = std::tuple< + int8_t, + flatbuffers::Optional, + int8_t, + uint8_t, + flatbuffers::Optional, + uint8_t, + int16_t, + flatbuffers::Optional, + int16_t, + uint16_t, + flatbuffers::Optional, + uint16_t, + int32_t, + flatbuffers::Optional, + int32_t, + uint32_t, + flatbuffers::Optional, + uint32_t, + int64_t, + flatbuffers::Optional, + int64_t, + uint64_t, + flatbuffers::Optional, + uint64_t, + float, + flatbuffers::Optional, + float, + double, + flatbuffers::Optional, + double, + bool, + flatbuffers::Optional, + bool, + optional_scalars::OptionalByte, + flatbuffers::Optional, + optional_scalars::OptionalByte + >; + FieldTypes fields_pack() const { + return { + just_i8(), + maybe_i8(), + default_i8(), + just_u8(), + maybe_u8(), + default_u8(), + just_i16(), + maybe_i16(), + default_i16(), + just_u16(), + maybe_u16(), + default_u16(), + just_i32(), + maybe_i32(), + default_i32(), + just_u32(), + maybe_u32(), + default_u32(), + just_i64(), + maybe_i64(), + default_i64(), + just_u64(), + maybe_u64(), + default_u64(), + just_f32(), + maybe_f32(), + default_f32(), + just_f64(), + maybe_f64(), + default_f64(), + just_bool(), + maybe_bool(), + default_bool(), + just_enum(), + maybe_enum(), + default_enum() + }; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_JUST_I8) && @@ -597,6 +677,45 @@ inline flatbuffers::Offset CreateScalarStuff( struct ScalarStuff::Traits { using type = ScalarStuff; static auto constexpr Create = CreateScalarStuff; + static constexpr auto name = "ScalarStuff"; + static constexpr std::array field_names = { + "just_i8", + "maybe_i8", + "default_i8", + "just_u8", + "maybe_u8", + "default_u8", + "just_i16", + "maybe_i16", + "default_i16", + "just_u16", + "maybe_u16", + "default_u16", + "just_i32", + "maybe_i32", + "default_i32", + "just_u32", + "maybe_u32", + "default_u32", + "just_i64", + "maybe_i64", + "default_i64", + "just_u64", + "maybe_u64", + "default_u64", + "just_f32", + "maybe_f32", + "default_f32", + "just_f64", + "maybe_f64", + "default_f64", + "just_bool", + "maybe_bool", + "default_bool", + "just_enum", + "maybe_enum", + "default_enum" + }; }; flatbuffers::Offset CreateScalarStuff(flatbuffers::FlatBufferBuilder &_fbb, const ScalarStuffT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); diff --git a/tests/cpp17/stringify_util.h b/tests/cpp17/stringify_util.h new file mode 100644 index 00000000000..32d1c4d328e --- /dev/null +++ b/tests/cpp17/stringify_util.h @@ -0,0 +1,194 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "flatbuffers/flatbuffers.h" + +namespace cpp17 { + +// User calls this; need to forward declare it since it is called recursively. +template +std::optional +StringifyFlatbufferValue(T&& val, const std::string &indent = ""); + +namespace detail { + +/******************************************************************************* +** Metaprogramming helpers for detecting Flatbuffers Tables, Structs, & Vectors. +*******************************************************************************/ +template +struct is_fb_table_or_struct : std::false_type {}; + +// We know it's a table or struct when it has a Traits subclass. +template +struct is_fb_table_or_struct> + : std::true_type {}; + +template +constexpr bool is_fb_table_or_struct_v = is_fb_table_or_struct::value; + +template +struct is_fb_vector : std::false_type {}; + +// We know it's a table or struct when it has a Traits subclass. +template +struct is_fb_vector> : std::true_type {}; + +template +constexpr bool is_fb_vector_v = is_fb_vector::value; + +/******************************************************************************* +** Compile-time Iteration & Recursive Stringification over Flatbuffers types. +*******************************************************************************/ +template +std::string StringifyTableOrStructImpl(const FB &flatbuff, + const std::string &indent, + std::index_sequence) { + // Getting the fields_pack should be a relatively light-weight operation. It + // will copy a std::tuple of primitive types, pointers, and references; no + // deep copies of anything will be made. + auto fields_pack = flatbuff.fields_pack(); + std::ostringstream oss; + auto add_field = [&](auto&& field_value, size_t index) { + auto value_string = StringifyFlatbufferValue(field_value, indent); + if (!value_string) { + return; + } + oss << indent + << FB::Traits::field_names[index] + << " = " + << *value_string + << "\n"; + }; + // Prevents unused-var warning when object has no fields. + (void)fields_pack; (void)add_field; + // This line is where the compile-time iteration happens! + (add_field(std::get(fields_pack), Indexes), ...); + return oss.str(); +} + +template +std::string StringifyTableOrStruct(const FB &flatbuff, + const std::string &indent) { + constexpr size_t field_count = + std::tuple_size_v; + std::ostringstream oss; + oss << FB::Traits::name << "{\n" + << StringifyTableOrStructImpl( + flatbuff, indent+" ", std::make_index_sequence{}) + << indent + << "}"; + return oss.str(); +} + +template +std::string StringifyVector(const flatbuffers::Vector &vec, + const std::string &indent) { + std::ostringstream oss; + oss << "[\n"; + for (size_t i = 0; i < vec.size(); ++i) { + if (auto value_string = StringifyFlatbufferValue(vec.Get(i))) { + oss << indent+" " << *value_string; + if (i != vec.size()-1) { + oss << ","; + } + oss << "\n"; + } + } + oss << indent << "]"; + return oss.str(); +} + +template +std::string StringifyArithmeticType(T val) { + std::ostringstream oss; + oss << val; + return oss.str(); +} + +} // namespace detail + +/******************************************************************************* +** Take any flatbuffer type (table, struct, Vector, int...) and stringify it. +*******************************************************************************/ +template +std::optional StringifyFlatbufferValue(T&& val, + const std::string &indent) { + constexpr bool is_pointer = std::is_pointer_v>; + if constexpr (is_pointer) { + if (val == nullptr) { + return std::nullopt; // Field is absent. + } + } + using decayed = + std::decay_t>>; + + // Is it a Flatbuffers Table or Struct? + if constexpr (detail::is_fb_table_or_struct_v) { + // 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). + if constexpr (std::is_same_v || + std::is_same_v) { + return detail::StringifyArithmeticType(static_cast(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 :( + if constexpr (std::is_enum_v) { + return StringifyFlatbufferValue( + static_cast>(val), + indent); + } + + // Is it an int, double, float, uint32_t, etc.? + if constexpr (std::is_arithmetic_v) { + return detail::StringifyArithmeticType(val); + } + + // Is it a Flatbuffers string? + if constexpr (std::is_same_v) { + return "\"" + val->str() + "\""; + } + + // Is it a Flatbuffers Vector? + if constexpr (detail::is_fb_vector_v) { + return detail::StringifyVector(*val, indent); + } + + // Not sure how to format this type, whatever it is. + return std::nullopt; +} + +} // namespace cpp17 \ No newline at end of file diff --git a/tests/cpp17/test_cpp17.cpp b/tests/cpp17/test_cpp17.cpp index 9b47c10a7b5..1cbf98262d1 100644 --- a/tests/cpp17/test_cpp17.cpp +++ b/tests/cpp17/test_cpp17.cpp @@ -25,6 +25,7 @@ #include "flatbuffers/minireflect.h" #include "flatbuffers/registry.h" #include "flatbuffers/util.h" +#include "stringify_util.h" #include "test_assert.h" // Embed generated code into an isolated namespace. @@ -38,6 +39,138 @@ namespace cpp11 { #include "../optional_scalars_generated.h" } // namespace cpp11 +using ::cpp17::MyGame::Example::Monster; +using ::cpp17::MyGame::Example::Vec3; + +/******************************************************************************* +** Build some FB objects. +*******************************************************************************/ +Vec3 BuildVec3() { + using ::cpp17::MyGame::Example::Color; + using ::cpp17::MyGame::Example::Test; + return Vec3( + /*x=*/1.1, + /*y=*/2.2, + /*z=*/3.3, + /*test1=*/6.6, + /*test2=*/Color::Green, + /*test3=*/Test( + /*a=*/11, + /*b=*/90 + ) + ); +} + +const Monster* BuildMonster(flatbuffers::FlatBufferBuilder& fbb) { + using ::cpp17::MyGame::Example::MonsterBuilder; + MonsterBuilder builder(fbb); + Vec3 vec3 = BuildVec3(); + builder.add_pos(&vec3); + auto name = fbb.CreateString("my_monster"); + builder.add_name(name); + builder.add_mana(1); + builder.add_hp(2); + builder.add_testbool(true); + builder.add_testhashs32_fnv1(4); + builder.add_testhashu32_fnv1(5); + builder.add_testhashs64_fnv1(6); + builder.add_testhashu64_fnv1(7); + builder.add_testhashs32_fnv1a(8); + builder.add_testhashu32_fnv1a(9); + builder.add_testhashs64_fnv1a(10); + builder.add_testhashu64_fnv1a(11); + builder.add_testf(12.1); + builder.add_testf2(13.1); + builder.add_testf3(14.1); + builder.add_single_weak_reference(15); + builder.add_co_owning_reference(16); + builder.add_non_owning_reference(17); + auto inventory = fbb.CreateVector(std::vector{4, 5, 6, 7}); + builder.add_inventory(inventory); + fbb.Finish( builder.Finish() ); + const Monster* monster = + flatbuffers::GetRoot(fbb.GetBufferPointer()); + return monster; +} + +/******************************************************************************* +** Test Case: Static Field Reflection Traits for Table & Structs. +*******************************************************************************/ +// This test tests & demonstrates the power of the static reflection. Using it, +// we can given any Flatbuffer type to a generic function and it will be able to +// produce is full recursive string representation of it. +// +// This test covers all types: primitive types, structs, tables, Vectors, etc. +// +void StringifyAnyFlatbuffersTypeTest() { + flatbuffers::FlatBufferBuilder fbb; + // We are using a Monster here, but we could have used any type, because the + // code that follows is totally generic! + const auto* monster = BuildMonster(fbb); + + std::string expected = R"(Monster{ + pos = Vec3{ + x = 1.1 + y = 2.2 + z = 3.3 + test1 = 6.6 + test2 = 2 + test3 = Test{ + a = 11 + b = 90 + } + } + mana = 1 + hp = 2 + name = "my_monster" + inventory = [ + 4, + 5, + 6, + 7 + ] + color = 8 + test_type = 0 + testbool = 1 + testhashs32_fnv1 = 4 + testhashu32_fnv1 = 5 + testhashs64_fnv1 = 6 + testhashu64_fnv1 = 7 + testhashs32_fnv1a = 8 + testhashu32_fnv1a = 9 + testhashs64_fnv1a = 10 + testhashu64_fnv1a = 11 + testf = 12.1 + testf2 = 13.1 + testf3 = 14.1 + single_weak_reference = 15 + co_owning_reference = 16 + non_owning_reference = 17 + any_unique_type = 0 + any_ambiguous_type = 0 + signed_enum = -1 + })"; + + // Call a generic function that has no specific knowledge of the flatbuffer we + // are passing in; it should use only static reflection to produce a string + // representations of the field names and values recursively. We give it an + // initial indentation so that the result can be compared with our raw string + // above, which we wanted to indent so that it will look nicer in this code. + // + // A note about JSON: as can be seen from the string above, this produces a + // JSON-like notation, but we are not using any of Flatbuffers' JSON infra to + // produce this! It is produced entirely using compile-time reflection, and + // thus does not require any runtime access to the *.fbs definition files! + std::optional result = + cpp17::StringifyFlatbufferValue(*monster, /*indent=*/" "); + + TEST_ASSERT(result.has_value()); + TEST_EQ_STR(expected.c_str(), result->c_str()); +} + +/******************************************************************************* +** Generic Create Function Test. +*******************************************************************************/ void CreateTableByTypeTest() { flatbuffers::FlatBufferBuilder builder; @@ -105,6 +238,7 @@ void OptionalScalarsTest() { int FlatBufferCpp17Tests() { CreateTableByTypeTest(); OptionalScalarsTest(); + StringifyAnyFlatbuffersTypeTest(); return 0; } From a1f366c149b1ba872e93a034e24fec528a2f22d5 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Wed, 9 Dec 2020 21:47:10 -0500 Subject: [PATCH 02/21] Fix int-conversion warning on MSVC. --- src/idl_gen_cpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 48127f051ec..65fa8242b3b 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2150,7 +2150,7 @@ class CppGenerator : public BaseGenerator { // }; // void GenFieldNames(const StructDef &struct_def) { - int non_deprecated_field_count = std::count_if( + auto non_deprecated_field_count = std::count_if( struct_def.fields.vec.begin(), struct_def.fields.vec.end(), [](const FieldDef *field) { return !field->deprecated; From c662260e0a0192137df4b1edc63b88b6c8d3a151 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Wed, 9 Dec 2020 22:25:39 -0500 Subject: [PATCH 03/21] Try to fix std::to_string ambiguity on MSVC. --- src/idl_gen_cpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 65fa8242b3b..af3e18a25e5 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2157,7 +2157,7 @@ class CppGenerator : public BaseGenerator { }); code_ += " static constexpr std::array<\\"; code_.SetValue("FIELD_COUNT", - std::to_string(non_deprecated_field_count)); + std::to_string(static_cast(non_deprecated_field_count))); code_ += "const char *, {{FIELD_COUNT}}> field_names = {\\"; if (struct_def.fields.vec.empty()) { code_ += "};"; From 94790e277fbf9cc211625bb29d4d9e5114d901c0 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sat, 12 Dec 2020 14:18:47 -0500 Subject: [PATCH 04/21] Fix clang-format diffs. --- src/idl_gen_cpp.cpp | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index af3e18a25e5..5b76f4af0e6 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2063,8 +2063,9 @@ class CppGenerator : public BaseGenerator { void GenStructFieldType(const FieldDef &field) { const auto is_array = IsArray(field.value.type); - std::string field_type = GenTypeGet(field.value.type, "", - is_array ? "" : "const ", is_array ? "" : " &", true); + std::string field_type = + GenTypeGet(field.value.type, "", is_array ? "" : "const ", + is_array ? "" : " &", true); code_.SetValue("FIELD_TYPE", field_type); code_ += " {{FIELD_TYPE}}\\"; } @@ -2097,9 +2098,7 @@ class CppGenerator : public BaseGenerator { } else { GenTableFieldType(field); } - if (it+1 != struct_def.fields.vec.end() ) { - code_ += ","; - } + if (it+1 != struct_def.fields.vec.end() ) { code_ += ","; } } code_ += "\n >;"; } @@ -2133,9 +2132,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("FIELD_NAME", Name(field)); code_ += " {{FIELD_NAME}}()\\"; - if (it+1 != struct_def.fields.vec.end() ) { - code_ += ","; - } + if (it+1 != struct_def.fields.vec.end() ) { code_ += ","; } } code_ += "\n };"; code_ += " }"; @@ -2151,12 +2148,11 @@ class CppGenerator : public BaseGenerator { // void GenFieldNames(const StructDef &struct_def) { auto non_deprecated_field_count = std::count_if( - struct_def.fields.vec.begin(), struct_def.fields.vec.end(), - [](const FieldDef *field) { - return !field->deprecated; - }); + struct_def.fields.vec.begin(), struct_def.fields.vec.end(), + [](const FieldDef *field) { return !field->deprecated; }); code_ += " static constexpr std::array<\\"; - code_.SetValue("FIELD_COUNT", + code_.SetValue( + "FIELD_COUNT", std::to_string(static_cast(non_deprecated_field_count))); code_ += "const char *, {{FIELD_COUNT}}> field_names = {\\"; if (struct_def.fields.vec.empty()) { @@ -2174,9 +2170,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("FIELD_NAME", Name(field)); code_ += " \"{{FIELD_NAME}}\"\\"; - if (it+1 != struct_def.fields.vec.end() ) { - code_ += ","; - } + if (it+1 != struct_def.fields.vec.end() ) { code_ += ","; } } code_ += "\n };"; } From 3f066972acfa9ab745a973c79570e368e47c1c71 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sat, 12 Dec 2020 18:10:12 -0500 Subject: [PATCH 05/21] Fix more clang-format diffs. --- src/idl_gen_cpp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 5b76f4af0e6..881c9f5a479 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2098,7 +2098,7 @@ class CppGenerator : public BaseGenerator { } else { GenTableFieldType(field); } - if (it+1 != struct_def.fields.vec.end() ) { code_ += ","; } + if (it + 1 != struct_def.fields.vec.end() ) { code_ += ","; } } code_ += "\n >;"; } @@ -2132,7 +2132,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("FIELD_NAME", Name(field)); code_ += " {{FIELD_NAME}}()\\"; - if (it+1 != struct_def.fields.vec.end() ) { code_ += ","; } + if (it + 1 != struct_def.fields.vec.end() ) { code_ += ","; } } code_ += "\n };"; code_ += " }"; @@ -2170,7 +2170,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("FIELD_NAME", Name(field)); code_ += " \"{{FIELD_NAME}}\"\\"; - if (it+1 != struct_def.fields.vec.end() ) { code_ += ","; } + if (it + 1 != struct_def.fields.vec.end() ) { code_ += ","; } } code_ += "\n };"; } From 7575a2c7b26d6d6979cd967db4dca4d651388a73 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sat, 12 Dec 2020 20:26:45 -0500 Subject: [PATCH 06/21] Fix last clang-format diff. --- src/idl_gen_cpp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 881c9f5a479..276a0b576bf 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2098,7 +2098,7 @@ class CppGenerator : public BaseGenerator { } else { GenTableFieldType(field); } - if (it + 1 != struct_def.fields.vec.end() ) { code_ += ","; } + if (it + 1 != struct_def.fields.vec.end()) { code_ += ","; } } code_ += "\n >;"; } @@ -2132,7 +2132,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("FIELD_NAME", Name(field)); code_ += " {{FIELD_NAME}}()\\"; - if (it + 1 != struct_def.fields.vec.end() ) { code_ += ","; } + if (it + 1 != struct_def.fields.vec.end()) { code_ += ","; } } code_ += "\n };"; code_ += " }"; @@ -2170,7 +2170,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("FIELD_NAME", Name(field)); code_ += " \"{{FIELD_NAME}}\"\\"; - if (it + 1 != struct_def.fields.vec.end() ) { code_ += ","; } + if (it + 1 != struct_def.fields.vec.end()) { code_ += ","; } } code_ += "\n };"; } From 181220dd6d1e322f894bd35b52bfc9fa8a1c443a Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sun, 13 Dec 2020 09:49:41 -0500 Subject: [PATCH 07/21] Enable C++17 build/test for VC 19 platform in CI. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a814bfcc78d..0fb6b9922b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - name: cmake - run: cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release . + run: cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_CPP17 . - name: build run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64 - name: test From 5a42f2d704902205f9df93c0965aec3e84cbc9d2 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sun, 13 Dec 2020 09:56:05 -0500 Subject: [PATCH 08/21] Forgot to add value to cmake command line variable. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0fb6b9922b0..35c9e3a74ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - name: cmake - run: cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_CPP17 . + run: cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_CPP17=ON . - name: build run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64 - name: test From 9f8ff70a721006fe959c46ca6403b854ec98dce8 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sun, 13 Dec 2020 10:32:45 -0500 Subject: [PATCH 09/21] Various fixes/changes in response to @vglavnyy's feedback. --- src/idl_gen_cpp.cpp | 12 ++ .../generated_cpp17/monster_test_generated.h | 10 ++ .../optional_scalars_generated.h | 1 + tests/cpp17/stringify_util.h | 107 +++++++++--------- tests/cpp17/test_cpp17.cpp | 53 ++++----- 5 files changed, 101 insertions(+), 82 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 276a0b576bf..322f2a48646 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2528,10 +2528,16 @@ class CppGenerator : public BaseGenerator { // Definition for type traits for this table type. This allows querying var- // ious compile-time traits of the table. if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + code_.SetValue("FULLY_QUALIFIED_NAME", + struct_def.defined_namespace->GetFullyQualifiedName( + Name(struct_def))); code_ += "struct {{STRUCT_NAME}}::Traits {"; code_ += " using type = {{STRUCT_NAME}};"; code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};"; code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; + code_ += + " static constexpr auto fully_qualified_name = " + "\"{{FULLY_QUALIFIED_NAME}}\";"; GenFieldNames(struct_def); code_ += "};"; code_ += ""; @@ -3415,9 +3421,15 @@ class CppGenerator : public BaseGenerator { // Definition for type traits for this struct type. This al- // lows querying various compile-time traits of the struct. if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + code_.SetValue("FULLY_QUALIFIED_NAME", + struct_def.defined_namespace->GetFullyQualifiedName( + Name(struct_def))); code_ += "struct {{STRUCT_NAME}}::Traits {"; code_ += " using type = {{STRUCT_NAME}};"; code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; + code_ += + " static constexpr auto fully_qualified_name = " + "\"{{FULLY_QUALIFIED_NAME}}\";"; GenFieldNames(struct_def); code_ += "};"; code_ += ""; diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 26de0f2792c..c6ed6df2643 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -520,6 +520,7 @@ FLATBUFFERS_STRUCT_END(Test, 4); struct Test::Traits { using type = Test; static constexpr auto name = "Test"; + static constexpr auto fully_qualified_name = "MyGame.Example.Test"; static constexpr std::array field_names = { "a", "b" @@ -631,6 +632,7 @@ FLATBUFFERS_STRUCT_END(Vec3, 32); struct Vec3::Traits { using type = Vec3; static constexpr auto name = "Vec3"; + static constexpr auto fully_qualified_name = "MyGame.Example.Vec3"; static constexpr std::array field_names = { "x", "y", @@ -693,6 +695,7 @@ FLATBUFFERS_STRUCT_END(Ability, 8); struct Ability::Traits { using type = Ability; static constexpr auto name = "Ability"; + static constexpr auto fully_qualified_name = "MyGame.Example.Ability"; static constexpr std::array field_names = { "id", "distance" @@ -750,6 +753,7 @@ struct InParentNamespace::Traits { using type = InParentNamespace; static auto constexpr Create = CreateInParentNamespace; static constexpr auto name = "InParentNamespace"; + static constexpr auto fully_qualified_name = "MyGame.InParentNamespace"; static constexpr std::array field_names = {}; }; @@ -806,6 +810,7 @@ struct Monster::Traits { using type = Monster; static auto constexpr Create = CreateMonster; static constexpr auto name = "Monster"; + static constexpr auto fully_qualified_name = "MyGame.Example2.Monster"; static constexpr std::array field_names = {}; }; @@ -884,6 +889,7 @@ struct TestSimpleTableWithEnum::Traits { using type = TestSimpleTableWithEnum; static auto constexpr Create = CreateTestSimpleTableWithEnum; static constexpr auto name = "TestSimpleTableWithEnum"; + static constexpr auto fully_qualified_name = "MyGame.Example.TestSimpleTableWithEnum"; static constexpr std::array field_names = { "color" }; @@ -993,6 +999,7 @@ struct Stat::Traits { using type = Stat; static auto constexpr Create = CreateStat; static constexpr auto name = "Stat"; + static constexpr auto fully_qualified_name = "MyGame.Example.Stat"; static constexpr std::array field_names = { "id", "val", @@ -1090,6 +1097,7 @@ struct Referrable::Traits { using type = Referrable; static auto constexpr Create = CreateReferrable; static constexpr auto name = "Referrable"; + static constexpr auto fully_qualified_name = "MyGame.Example.Referrable"; static constexpr std::array field_names = { "id" }; @@ -2025,6 +2033,7 @@ struct Monster::Traits { using type = Monster; static auto constexpr Create = CreateMonster; static constexpr auto name = "Monster"; + static constexpr auto fully_qualified_name = "MyGame.Example.Monster"; static constexpr std::array field_names = { "pos", "mana", @@ -2451,6 +2460,7 @@ struct TypeAliases::Traits { using type = TypeAliases; static auto constexpr Create = CreateTypeAliases; static constexpr auto name = "TypeAliases"; + static constexpr auto fully_qualified_name = "MyGame.Example.TypeAliases"; static constexpr std::array field_names = { "i8", "u8", diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index ebc76b3ec1e..e20d84fc68c 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -678,6 +678,7 @@ struct ScalarStuff::Traits { using type = ScalarStuff; static auto constexpr Create = CreateScalarStuff; static constexpr auto name = "ScalarStuff"; + static constexpr auto fully_qualified_name = "optional_scalars.ScalarStuff"; static constexpr std::array field_names = { "just_i8", "maybe_i8", diff --git a/tests/cpp17/stringify_util.h b/tests/cpp17/stringify_util.h index 32d1c4d328e..307462858f0 100644 --- a/tests/cpp17/stringify_util.h +++ b/tests/cpp17/stringify_util.h @@ -33,8 +33,8 @@ namespace cpp17 { // User calls this; need to forward declare it since it is called recursively. template -std::optional -StringifyFlatbufferValue(T&& val, const std::string &indent = ""); +std::optional StringifyFlatbufferValue( + T &&val, const std::string &indent = ""); namespace detail { @@ -47,20 +47,18 @@ struct is_fb_table_or_struct : std::false_type {}; // We know it's a table or struct when it has a Traits subclass. template struct is_fb_table_or_struct> - : std::true_type {}; + : std::true_type {}; template constexpr bool is_fb_table_or_struct_v = is_fb_table_or_struct::value; -template -struct is_fb_vector : std::false_type {}; +template struct is_fb_vector : std::false_type {}; // We know it's a table or struct when it has a Traits subclass. template struct is_fb_vector> : std::true_type {}; -template -constexpr bool is_fb_vector_v = is_fb_vector::value; +template constexpr bool is_fb_vector_v = is_fb_vector::value; /******************************************************************************* ** Compile-time Iteration & Recursive Stringification over Flatbuffers types. @@ -74,19 +72,15 @@ std::string StringifyTableOrStructImpl(const FB &flatbuff, // deep copies of anything will be made. auto fields_pack = flatbuff.fields_pack(); std::ostringstream oss; - auto add_field = [&](auto&& field_value, size_t index) { + auto add_field = [&](auto &&field_value, size_t index) { auto value_string = StringifyFlatbufferValue(field_value, indent); - if (!value_string) { - return; - } - oss << indent - << FB::Traits::field_names[index] - << " = " - << *value_string + if (!value_string) { return; } + oss << indent << FB::Traits::field_names[index] << " = " << *value_string << "\n"; }; // Prevents unused-var warning when object has no fields. - (void)fields_pack; (void)add_field; + (void)fields_pack; + (void)add_field; // This line is where the compile-time iteration happens! (add_field(std::get(fields_pack), Indexes), ...); return oss.str(); @@ -95,37 +89,35 @@ std::string StringifyTableOrStructImpl(const FB &flatbuff, template std::string StringifyTableOrStruct(const FB &flatbuff, const std::string &indent) { - constexpr size_t field_count = - std::tuple_size_v; + constexpr size_t field_count = std::tuple_size_v; std::ostringstream oss; - oss << FB::Traits::name << "{\n" - << StringifyTableOrStructImpl( - flatbuff, indent+" ", std::make_index_sequence{}) - << indent - << "}"; + oss << FB::Traits::fully_qualified_name << "{\n" + << StringifyTableOrStructImpl(flatbuff, indent + " ", + std::make_index_sequence{}) + << indent << "}"; return oss.str(); } template std::string StringifyVector(const flatbuffers::Vector &vec, const std::string &indent) { - std::ostringstream oss; - oss << "[\n"; - for (size_t i = 0; i < vec.size(); ++i) { - if (auto value_string = StringifyFlatbufferValue(vec.Get(i))) { - oss << indent+" " << *value_string; - if (i != vec.size()-1) { - oss << ","; - } - oss << "\n"; - } + 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; } - oss << indent << "]"; - return oss.str(); + if (vec.cbegin() != vec.cend()) { + text.resize(text.size() - epilogue.size()); + } + text += std::string("\n") + indent + "]"; + return text; } -template -std::string StringifyArithmeticType(T val) { +template std::string StringifyArithmeticType(T val) { std::ostringstream oss; oss << val; return oss.str(); @@ -137,21 +129,21 @@ std::string StringifyArithmeticType(T val) { ** Take any flatbuffer type (table, struct, Vector, int...) and stringify it. *******************************************************************************/ template -std::optional StringifyFlatbufferValue(T&& val, - const std::string &indent) { +std::optional StringifyFlatbufferValue(T &&val, + const std::string &indent) { constexpr bool is_pointer = std::is_pointer_v>; if constexpr (is_pointer) { if (val == nullptr) { - return std::nullopt; // Field is absent. + return std::nullopt; // Field is absent. } } using decayed = - std::decay_t>>; + std::decay_t>>; // Is it a Flatbuffers Table or Struct? if constexpr (detail::is_fb_table_or_struct_v) { // We have a nested table or struct; use recursion! - if constexpr(is_pointer) { + if constexpr (is_pointer) { return detail::StringifyTableOrStruct(*val, indent); } else { return detail::StringifyTableOrStruct(val, indent); @@ -159,36 +151,45 @@ std::optional StringifyFlatbufferValue(T&& val, } // Is it an 8-bit number? If so, print it like an int (not char). - if constexpr (std::is_same_v || - std::is_same_v) { + else if constexpr (std::is_same_v || + std::is_same_v) { return detail::StringifyArithmeticType(static_cast(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 :( - if constexpr (std::is_enum_v) { + else if constexpr (std::is_enum_v) { return StringifyFlatbufferValue( - static_cast>(val), - indent); + static_cast>(val), indent); } // Is it an int, double, float, uint32_t, etc.? - if constexpr (std::is_arithmetic_v) { + else if constexpr (std::is_arithmetic_v) { return detail::StringifyArithmeticType(val); } // Is it a Flatbuffers string? - if constexpr (std::is_same_v) { + else if constexpr (std::is_same_v) { return "\"" + val->str() + "\""; } // Is it a Flatbuffers Vector? - if constexpr (detail::is_fb_vector_v) { + else if constexpr (detail::is_fb_vector_v) { return detail::StringifyVector(*val, indent); } - // Not sure how to format this type, whatever it is. - return std::nullopt; + // Is it a void pointer? + else if constexpr (std::is_same_v) { + // 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 \ No newline at end of file +} // namespace cpp17 diff --git a/tests/cpp17/test_cpp17.cpp b/tests/cpp17/test_cpp17.cpp index 1cbf98262d1..34219391ab2 100644 --- a/tests/cpp17/test_cpp17.cpp +++ b/tests/cpp17/test_cpp17.cpp @@ -45,28 +45,23 @@ using ::cpp17::MyGame::Example::Vec3; /******************************************************************************* ** Build some FB objects. *******************************************************************************/ -Vec3 BuildVec3() { +const Monster *BuildMonster(flatbuffers::FlatBufferBuilder &fbb) { using ::cpp17::MyGame::Example::Color; - using ::cpp17::MyGame::Example::Test; - return Vec3( - /*x=*/1.1, - /*y=*/2.2, - /*z=*/3.3, - /*test1=*/6.6, - /*test2=*/Color::Green, - /*test3=*/Test( - /*a=*/11, - /*b=*/90 - ) - ); -} - -const Monster* BuildMonster(flatbuffers::FlatBufferBuilder& fbb) { using ::cpp17::MyGame::Example::MonsterBuilder; + using ::cpp17::MyGame::Example::Test; + auto name = fbb.CreateString("my_monster"); + auto inventory = fbb.CreateVector(std::vector{ 4, 5, 6, 7 }); MonsterBuilder builder(fbb); - Vec3 vec3 = BuildVec3(); + auto vec3 = Vec3{ /*x=*/1.1f, + /*y=*/2.2f, + /*z=*/3.3f, + /*test1=*/6.6, + /*test2=*/Color::Green, + /*test3=*/ + Test( + /*a=*/11, + /*b=*/90) }; builder.add_pos(&vec3); - auto name = fbb.CreateString("my_monster"); builder.add_name(name); builder.add_mana(1); builder.add_hp(2); @@ -79,16 +74,15 @@ const Monster* BuildMonster(flatbuffers::FlatBufferBuilder& fbb) { builder.add_testhashu32_fnv1a(9); builder.add_testhashs64_fnv1a(10); builder.add_testhashu64_fnv1a(11); - builder.add_testf(12.1); - builder.add_testf2(13.1); - builder.add_testf3(14.1); + builder.add_testf(12.1f); + builder.add_testf2(13.1f); + builder.add_testf3(14.1f); builder.add_single_weak_reference(15); builder.add_co_owning_reference(16); builder.add_non_owning_reference(17); - auto inventory = fbb.CreateVector(std::vector{4, 5, 6, 7}); builder.add_inventory(inventory); - fbb.Finish( builder.Finish() ); - const Monster* monster = + fbb.Finish(builder.Finish()); + const Monster *monster = flatbuffers::GetRoot(fbb.GetBufferPointer()); return monster; } @@ -106,16 +100,16 @@ void StringifyAnyFlatbuffersTypeTest() { flatbuffers::FlatBufferBuilder fbb; // We are using a Monster here, but we could have used any type, because the // code that follows is totally generic! - const auto* monster = BuildMonster(fbb); + const auto *monster = BuildMonster(fbb); - std::string expected = R"(Monster{ - pos = Vec3{ + std::string expected = R"(MyGame.Example.Monster{ + pos = MyGame.Example.Vec3{ x = 1.1 y = 2.2 z = 3.3 test1 = 6.6 test2 = 2 - test3 = Test{ + test3 = MyGame.Example.Test{ a = 11 b = 90 } @@ -195,7 +189,8 @@ void CreateTableByTypeTest() { } void OptionalScalarsTest() { - static_assert(std::is_same, std::optional>::value); + static_assert( + std::is_same, std::optional>::value); static_assert(std::is_same::value); // test C++ nullable From 44302e66c030c02ea746ca7aaecc6b3e7ae00ff1 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Fri, 8 Jan 2021 13:02:06 -0500 Subject: [PATCH 10/21] Replace "fields pack" with index-based getters. --- src/idl_gen_cpp.cpp | 42 ++-- .../generated_cpp17/monster_test_generated.h | 237 +++++++++--------- .../optional_scalars_generated.h | 78 +++--- tests/cpp17/stringify_util.h | 80 +++--- tests/cpp17/test_cpp17.cpp | 14 +- 5 files changed, 221 insertions(+), 230 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 322f2a48646..cb24295c726 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2103,26 +2103,17 @@ class CppGenerator : public BaseGenerator { code_ += "\n >;"; } - // Sample for Vec3: - // - // FieldTypes fields_pack() const { - // return { - // x(), - // y(), - // z() - // }; - // } - // - void GenFieldsPack(const StructDef &struct_def) { - code_ += " FieldTypes fields_pack() const {"; - code_ += " return {\\"; + void GenIndexBasedFieldGetter(const StructDef &struct_def) { if (struct_def.fields.vec.empty()) { - code_ += "};"; - code_ += " }"; return; } - code_ += ""; - // Generate the fields_pack elements. + code_ += " template"; + code_ += " static constexpr auto getter_for() {"; + + code_.SetValue("STRUCT_NAME", Name(struct_def)); + size_t index = 0; + bool need_else = false; + // Generate one index-based getter for each field. for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto &field = **it; @@ -2131,10 +2122,17 @@ class CppGenerator : public BaseGenerator { continue; } code_.SetValue("FIELD_NAME", Name(field)); - code_ += " {{FIELD_NAME}}()\\"; - if (it + 1 != struct_def.fields.vec.end()) { code_ += ","; } + code_.SetValue("FIELD_INDEX", std::to_string(index++)); + if (need_else) { + code_ += " else \\"; + } else { + code_ += " \\"; + } + need_else = true; + code_ += "if constexpr (Index == {{FIELD_INDEX}}) \\"; + code_ += "return &{{STRUCT_NAME}}::{{FIELD_NAME}};"; } - code_ += "\n };"; + code_ += " else static_assert(Index != Index, \"Invalid Field Index\");"; code_ += " }"; } @@ -2317,7 +2315,7 @@ class CppGenerator : public BaseGenerator { if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenFieldTypes(struct_def, /*is_struct=*/false); - GenFieldsPack(struct_def); + GenIndexBasedFieldGetter(struct_def); } // Generate a verifier function that can check a buffer from an untrusted @@ -3408,7 +3406,7 @@ class CppGenerator : public BaseGenerator { if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenFieldTypes(struct_def, /*is_struct=*/true); - GenFieldsPack(struct_def); + GenIndexBasedFieldGetter(struct_def); } code_ += "};"; diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 59fe5e49da4..4470aba1097 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -508,11 +508,11 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS { int16_t, int8_t >; - FieldTypes fields_pack() const { - return { - a(), - b() - }; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &Test::a; + else if constexpr (Index == 1) return &Test::b; + else static_assert(Index != Index, "Invalid Field Index"); } }; FLATBUFFERS_STRUCT_END(Test, 4); @@ -616,15 +616,15 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { MyGame::Example::Color, const MyGame::Example::Test & >; - FieldTypes fields_pack() const { - return { - x(), - y(), - z(), - test1(), - test2(), - test3() - }; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &Vec3::x; + else if constexpr (Index == 1) return &Vec3::y; + else if constexpr (Index == 2) return &Vec3::z; + else if constexpr (Index == 3) return &Vec3::test1; + else if constexpr (Index == 4) return &Vec3::test2; + else if constexpr (Index == 5) return &Vec3::test3; + else static_assert(Index != Index, "Invalid Field Index"); } }; FLATBUFFERS_STRUCT_END(Vec3, 32); @@ -683,11 +683,11 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { uint32_t, uint32_t >; - FieldTypes fields_pack() const { - return { - id(), - distance() - }; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &Ability::id; + else if constexpr (Index == 1) return &Ability::distance; + else static_assert(Index != Index, "Invalid Field Index"); } }; FLATBUFFERS_STRUCT_END(Ability, 8); @@ -716,9 +716,6 @@ struct InParentNamespace FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return InParentNamespaceTypeTable(); } using FieldTypes = std::tuple<>; - FieldTypes fields_pack() const { - return {}; - } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && verifier.EndTable(); @@ -773,9 +770,6 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return MonsterTypeTable(); } using FieldTypes = std::tuple<>; - FieldTypes fields_pack() const { - return {}; - } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && verifier.EndTable(); @@ -844,10 +838,10 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta using FieldTypes = std::tuple< MyGame::Example::Color >; - FieldTypes fields_pack() const { - return { - color() - }; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &TestSimpleTableWithEnum::color; + else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && @@ -934,23 +928,24 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool mutate_count(uint16_t _count) { return SetField(VT_COUNT, _count, 0); } - using FieldTypes = std::tuple< - const flatbuffers::String *, - int64_t, - uint16_t - >; - FieldTypes fields_pack() const { - return { - id(), - val(), - count() - }; bool KeyCompareLessThan(const Stat *o) const { return count() < o->count(); } int KeyCompareWithValue(uint16_t val) const { return static_cast(count() > val) - static_cast(count() < val); } + using FieldTypes = std::tuple< + const flatbuffers::String *, + int64_t, + uint16_t + >; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &Stat::id; + else if constexpr (Index == 1) return &Stat::val; + else if constexpr (Index == 2) return &Stat::count; + else static_assert(Index != Index, "Invalid Field Index"); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_ID) && @@ -1057,10 +1052,10 @@ struct Referrable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { using FieldTypes = std::tuple< uint64_t >; - FieldTypes fields_pack() const { - return { - id() - }; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &Referrable::id; + else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && @@ -1552,6 +1547,12 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const MyGame::Example::Monster *testrequirednestedflatbuffer_nested_root() const { return flatbuffers::GetRoot(testrequirednestedflatbuffer()->Data()); } + const flatbuffers::Vector> *scalar_key_sorted_tables() const { + return GetPointer> *>(VT_SCALAR_KEY_SORTED_TABLES); + } + flatbuffers::Vector> *mutable_scalar_key_sorted_tables() { + return GetPointer> *>(VT_SCALAR_KEY_SORTED_TABLES); + } using FieldTypes = std::tuple< const MyGame::Example::Vec3 *, int16_t, @@ -1601,65 +1602,62 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const void *, const flatbuffers::Vector *, MyGame::Example::Race, - const flatbuffers::Vector * + const flatbuffers::Vector *, + const flatbuffers::Vector> * >; - FieldTypes fields_pack() const { - return { - pos(), - mana(), - hp(), - name(), - inventory(), - color(), - test_type(), - test(), - test4(), - testarrayofstring(), - testarrayoftables(), - enemy(), - testnestedflatbuffer(), - testempty(), - testbool(), - testhashs32_fnv1(), - testhashu32_fnv1(), - testhashs64_fnv1(), - testhashu64_fnv1(), - testhashs32_fnv1a(), - testhashu32_fnv1a(), - testhashs64_fnv1a(), - testhashu64_fnv1a(), - testarrayofbools(), - testf(), - testf2(), - testf3(), - testarrayofstring2(), - testarrayofsortedstruct(), - flex(), - test5(), - vector_of_longs(), - vector_of_doubles(), - parent_namespace_test(), - vector_of_referrables(), - single_weak_reference(), - vector_of_weak_references(), - vector_of_strong_referrables(), - co_owning_reference(), - vector_of_co_owning_references(), - non_owning_reference(), - vector_of_non_owning_references(), - any_unique_type(), - any_unique(), - any_ambiguous_type(), - any_ambiguous(), - vector_of_enums(), - signed_enum(), - testrequirednestedflatbuffer() - }; - const flatbuffers::Vector> *scalar_key_sorted_tables() const { - return GetPointer> *>(VT_SCALAR_KEY_SORTED_TABLES); - } - flatbuffers::Vector> *mutable_scalar_key_sorted_tables() { - return GetPointer> *>(VT_SCALAR_KEY_SORTED_TABLES); + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &Monster::pos; + else if constexpr (Index == 1) return &Monster::mana; + else if constexpr (Index == 2) return &Monster::hp; + else if constexpr (Index == 3) return &Monster::name; + else if constexpr (Index == 4) return &Monster::inventory; + else if constexpr (Index == 5) return &Monster::color; + else if constexpr (Index == 6) return &Monster::test_type; + else if constexpr (Index == 7) return &Monster::test; + else if constexpr (Index == 8) return &Monster::test4; + else if constexpr (Index == 9) return &Monster::testarrayofstring; + else if constexpr (Index == 10) return &Monster::testarrayoftables; + else if constexpr (Index == 11) return &Monster::enemy; + else if constexpr (Index == 12) return &Monster::testnestedflatbuffer; + else if constexpr (Index == 13) return &Monster::testempty; + else if constexpr (Index == 14) return &Monster::testbool; + else if constexpr (Index == 15) return &Monster::testhashs32_fnv1; + else if constexpr (Index == 16) return &Monster::testhashu32_fnv1; + else if constexpr (Index == 17) return &Monster::testhashs64_fnv1; + else if constexpr (Index == 18) return &Monster::testhashu64_fnv1; + else if constexpr (Index == 19) return &Monster::testhashs32_fnv1a; + else if constexpr (Index == 20) return &Monster::testhashu32_fnv1a; + else if constexpr (Index == 21) return &Monster::testhashs64_fnv1a; + else if constexpr (Index == 22) return &Monster::testhashu64_fnv1a; + else if constexpr (Index == 23) return &Monster::testarrayofbools; + else if constexpr (Index == 24) return &Monster::testf; + else if constexpr (Index == 25) return &Monster::testf2; + else if constexpr (Index == 26) return &Monster::testf3; + else if constexpr (Index == 27) return &Monster::testarrayofstring2; + else if constexpr (Index == 28) return &Monster::testarrayofsortedstruct; + else if constexpr (Index == 29) return &Monster::flex; + else if constexpr (Index == 30) return &Monster::test5; + else if constexpr (Index == 31) return &Monster::vector_of_longs; + else if constexpr (Index == 32) return &Monster::vector_of_doubles; + else if constexpr (Index == 33) return &Monster::parent_namespace_test; + else if constexpr (Index == 34) return &Monster::vector_of_referrables; + else if constexpr (Index == 35) return &Monster::single_weak_reference; + else if constexpr (Index == 36) return &Monster::vector_of_weak_references; + else if constexpr (Index == 37) return &Monster::vector_of_strong_referrables; + else if constexpr (Index == 38) return &Monster::co_owning_reference; + else if constexpr (Index == 39) return &Monster::vector_of_co_owning_references; + else if constexpr (Index == 40) return &Monster::non_owning_reference; + else if constexpr (Index == 41) return &Monster::vector_of_non_owning_references; + else if constexpr (Index == 42) return &Monster::any_unique_type; + else if constexpr (Index == 43) return &Monster::any_unique; + else if constexpr (Index == 44) return &Monster::any_ambiguous_type; + else if constexpr (Index == 45) return &Monster::any_ambiguous; + else if constexpr (Index == 46) return &Monster::vector_of_enums; + else if constexpr (Index == 47) return &Monster::signed_enum; + else if constexpr (Index == 48) return &Monster::testrequirednestedflatbuffer; + else if constexpr (Index == 49) return &Monster::scalar_key_sorted_tables; + else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && @@ -2054,7 +2052,7 @@ struct Monster::Traits { static auto constexpr Create = CreateMonster; static constexpr auto name = "Monster"; static constexpr auto fully_qualified_name = "MyGame.Example.Monster"; - static constexpr std::array field_names = { + static constexpr std::array field_names = { "pos", "mana", "hp", @@ -2103,7 +2101,8 @@ struct Monster::Traits { "any_ambiguous", "vector_of_enums", "signed_enum", - "testrequirednestedflatbuffer" + "testrequirednestedflatbuffer", + "scalar_key_sorted_tables" }; }; @@ -2359,21 +2358,21 @@ struct TypeAliases FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector *, const flatbuffers::Vector * >; - FieldTypes fields_pack() const { - return { - i8(), - u8(), - i16(), - u16(), - i32(), - u32(), - i64(), - u64(), - f32(), - f64(), - v8(), - vf64() - }; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &TypeAliases::i8; + else if constexpr (Index == 1) return &TypeAliases::u8; + else if constexpr (Index == 2) return &TypeAliases::i16; + else if constexpr (Index == 3) return &TypeAliases::u16; + else if constexpr (Index == 4) return &TypeAliases::i32; + else if constexpr (Index == 5) return &TypeAliases::u32; + else if constexpr (Index == 6) return &TypeAliases::i64; + else if constexpr (Index == 7) return &TypeAliases::u64; + else if constexpr (Index == 8) return &TypeAliases::f32; + else if constexpr (Index == 9) return &TypeAliases::f64; + else if constexpr (Index == 10) return &TypeAliases::v8; + else if constexpr (Index == 11) return &TypeAliases::vf64; + else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index e20d84fc68c..8257c387e46 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -388,45 +388,45 @@ struct ScalarStuff FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { flatbuffers::Optional, optional_scalars::OptionalByte >; - FieldTypes fields_pack() const { - return { - just_i8(), - maybe_i8(), - default_i8(), - just_u8(), - maybe_u8(), - default_u8(), - just_i16(), - maybe_i16(), - default_i16(), - just_u16(), - maybe_u16(), - default_u16(), - just_i32(), - maybe_i32(), - default_i32(), - just_u32(), - maybe_u32(), - default_u32(), - just_i64(), - maybe_i64(), - default_i64(), - just_u64(), - maybe_u64(), - default_u64(), - just_f32(), - maybe_f32(), - default_f32(), - just_f64(), - maybe_f64(), - default_f64(), - just_bool(), - maybe_bool(), - default_bool(), - just_enum(), - maybe_enum(), - default_enum() - }; + template + static constexpr auto getter_for() { + if constexpr (Index == 0) return &ScalarStuff::just_i8; + else if constexpr (Index == 1) return &ScalarStuff::maybe_i8; + else if constexpr (Index == 2) return &ScalarStuff::default_i8; + else if constexpr (Index == 3) return &ScalarStuff::just_u8; + else if constexpr (Index == 4) return &ScalarStuff::maybe_u8; + else if constexpr (Index == 5) return &ScalarStuff::default_u8; + else if constexpr (Index == 6) return &ScalarStuff::just_i16; + else if constexpr (Index == 7) return &ScalarStuff::maybe_i16; + else if constexpr (Index == 8) return &ScalarStuff::default_i16; + else if constexpr (Index == 9) return &ScalarStuff::just_u16; + else if constexpr (Index == 10) return &ScalarStuff::maybe_u16; + else if constexpr (Index == 11) return &ScalarStuff::default_u16; + else if constexpr (Index == 12) return &ScalarStuff::just_i32; + else if constexpr (Index == 13) return &ScalarStuff::maybe_i32; + else if constexpr (Index == 14) return &ScalarStuff::default_i32; + else if constexpr (Index == 15) return &ScalarStuff::just_u32; + else if constexpr (Index == 16) return &ScalarStuff::maybe_u32; + else if constexpr (Index == 17) return &ScalarStuff::default_u32; + else if constexpr (Index == 18) return &ScalarStuff::just_i64; + else if constexpr (Index == 19) return &ScalarStuff::maybe_i64; + else if constexpr (Index == 20) return &ScalarStuff::default_i64; + else if constexpr (Index == 21) return &ScalarStuff::just_u64; + else if constexpr (Index == 22) return &ScalarStuff::maybe_u64; + else if constexpr (Index == 23) return &ScalarStuff::default_u64; + else if constexpr (Index == 24) return &ScalarStuff::just_f32; + else if constexpr (Index == 25) return &ScalarStuff::maybe_f32; + else if constexpr (Index == 26) return &ScalarStuff::default_f32; + else if constexpr (Index == 27) return &ScalarStuff::just_f64; + else if constexpr (Index == 28) return &ScalarStuff::maybe_f64; + else if constexpr (Index == 29) return &ScalarStuff::default_f64; + else if constexpr (Index == 30) return &ScalarStuff::just_bool; + else if constexpr (Index == 31) return &ScalarStuff::maybe_bool; + else if constexpr (Index == 32) return &ScalarStuff::default_bool; + else if constexpr (Index == 33) return &ScalarStuff::just_enum; + else if constexpr (Index == 34) return &ScalarStuff::maybe_enum; + else if constexpr (Index == 35) return &ScalarStuff::default_enum; + else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && diff --git a/tests/cpp17/stringify_util.h b/tests/cpp17/stringify_util.h index 307462858f0..ee5b67aa7e8 100644 --- a/tests/cpp17/stringify_util.h +++ b/tests/cpp17/stringify_util.h @@ -21,7 +21,6 @@ // generic in that it makes no reference to any particular Flatbuffer type. #include -#include #include #include #include @@ -41,61 +40,58 @@ namespace detail { /******************************************************************************* ** Metaprogramming helpers for detecting Flatbuffers Tables, Structs, & Vectors. *******************************************************************************/ -template -struct is_fb_table_or_struct : std::false_type {}; +template +struct is_flatbuffers_table_or_struct : std::false_type {}; // We know it's a table or struct when it has a Traits subclass. -template -struct is_fb_table_or_struct> +template +struct is_flatbuffers_table_or_struct> : std::true_type {}; -template -constexpr bool is_fb_table_or_struct_v = is_fb_table_or_struct::value; +template +constexpr bool is_flatbuffers_table_or_struct_v = + is_flatbuffers_table_or_struct::value; -template struct is_fb_vector : std::false_type {}; +template struct is_flatbuffers_vector : std::false_type {}; // We know it's a table or struct when it has a Traits subclass. template -struct is_fb_vector> : std::true_type {}; +struct is_flatbuffers_vector> : std::true_type {}; -template constexpr bool is_fb_vector_v = is_fb_vector::value; +template +constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector::value; /******************************************************************************* ** Compile-time Iteration & Recursive Stringification over Flatbuffers types. *******************************************************************************/ -template -std::string StringifyTableOrStructImpl(const FB &flatbuff, +template +std::string AddStringifiedField(const FBS &fbs, const std::string &indent) { + auto field_getter = FBS::template getter_for(); + auto value_string = StringifyFlatbufferValue((fbs.*field_getter)(), indent); + if (!value_string) { return ""; } + return indent + FBS::Traits::field_names[Index] + " = " + *value_string + + "\n"; +} + +template +std::string StringifyTableOrStructImpl(const FBS &fbs, const std::string &indent, std::index_sequence) { - // Getting the fields_pack should be a relatively light-weight operation. It - // will copy a std::tuple of primitive types, pointers, and references; no - // deep copies of anything will be made. - auto fields_pack = flatbuff.fields_pack(); - std::ostringstream oss; - auto add_field = [&](auto &&field_value, size_t index) { - auto value_string = StringifyFlatbufferValue(field_value, indent); - if (!value_string) { return; } - oss << indent << FB::Traits::field_names[index] << " = " << *value_string - << "\n"; - }; - // Prevents unused-var warning when object has no fields. - (void)fields_pack; - (void)add_field; // This line is where the compile-time iteration happens! - (add_field(std::get(fields_pack), Indexes), ...); - return oss.str(); + return (AddStringifiedField(fbs, indent) + ...); } -template -std::string StringifyTableOrStruct(const FB &flatbuff, - const std::string &indent) { - constexpr size_t field_count = std::tuple_size_v; - std::ostringstream oss; - oss << FB::Traits::fully_qualified_name << "{\n" - << StringifyTableOrStructImpl(flatbuff, indent + " ", - std::make_index_sequence{}) - << indent << "}"; - return oss.str(); +template +std::string StringifyTableOrStruct(const FBS &fbs, const std::string &indent) { + constexpr size_t field_count = std::tuple_size_v; + std::string out; + if constexpr (field_count > 0) { + out = std::string(FBS::Traits::fully_qualified_name) + "{\n" + + StringifyTableOrStructImpl(fbs, indent + " ", + std::make_index_sequence{}) + + indent + "}"; + } + return out; } template @@ -118,9 +114,7 @@ std::string StringifyVector(const flatbuffers::Vector &vec, } template std::string StringifyArithmeticType(T val) { - std::ostringstream oss; - oss << val; - return oss.str(); + return std::to_string(val); } } // namespace detail @@ -141,7 +135,7 @@ std::optional StringifyFlatbufferValue(T &&val, std::decay_t>>; // Is it a Flatbuffers Table or Struct? - if constexpr (detail::is_fb_table_or_struct_v) { + if constexpr (detail::is_flatbuffers_table_or_struct_v) { // We have a nested table or struct; use recursion! if constexpr (is_pointer) { return detail::StringifyTableOrStruct(*val, indent); @@ -174,7 +168,7 @@ std::optional StringifyFlatbufferValue(T &&val, } // Is it a Flatbuffers Vector? - else if constexpr (detail::is_fb_vector_v) { + else if constexpr (detail::is_flatbuffers_vector_v) { return detail::StringifyVector(*val, indent); } diff --git a/tests/cpp17/test_cpp17.cpp b/tests/cpp17/test_cpp17.cpp index 34219391ab2..e5c35d1ee86 100644 --- a/tests/cpp17/test_cpp17.cpp +++ b/tests/cpp17/test_cpp17.cpp @@ -104,10 +104,10 @@ void StringifyAnyFlatbuffersTypeTest() { std::string expected = R"(MyGame.Example.Monster{ pos = MyGame.Example.Vec3{ - x = 1.1 - y = 2.2 - z = 3.3 - test1 = 6.6 + x = 1.100000 + y = 2.200000 + z = 3.300000 + test1 = 6.600000 test2 = 2 test3 = MyGame.Example.Test{ a = 11 @@ -134,9 +134,9 @@ void StringifyAnyFlatbuffersTypeTest() { testhashu32_fnv1a = 9 testhashs64_fnv1a = 10 testhashu64_fnv1a = 11 - testf = 12.1 - testf2 = 13.1 - testf3 = 14.1 + testf = 12.100000 + testf2 = 13.100000 + testf3 = 14.100000 single_weak_reference = 15 co_owning_reference = 16 non_owning_reference = 17 From 36ee3b09486385262058a9eb61033a02d94feed5 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Fri, 8 Jan 2021 13:39:30 -0500 Subject: [PATCH 11/21] Fix MSVC error. --- src/idl_gen_cpp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index cb24295c726..e6371f65834 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2122,7 +2122,8 @@ class CppGenerator : public BaseGenerator { continue; } code_.SetValue("FIELD_NAME", Name(field)); - code_.SetValue("FIELD_INDEX", std::to_string(index++)); + code_.SetValue("FIELD_INDEX", + std::to_string(static_cast(index++))); if (need_else) { code_ += " else \\"; } else { From 09e46b24bbdb8d2fa717689d081086a0f63a9e60 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Fri, 8 Jan 2021 15:02:20 -0500 Subject: [PATCH 12/21] Fix clang-format diffs. --- src/idl_gen_cpp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index e6371f65834..41bc80e750d 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2104,9 +2104,7 @@ class CppGenerator : public BaseGenerator { } void GenIndexBasedFieldGetter(const StructDef &struct_def) { - if (struct_def.fields.vec.empty()) { - return; - } + if (struct_def.fields.vec.empty()) { return; } code_ += " template"; code_ += " static constexpr auto getter_for() {"; @@ -2123,7 +2121,7 @@ class CppGenerator : public BaseGenerator { } code_.SetValue("FIELD_NAME", Name(field)); code_.SetValue("FIELD_INDEX", - std::to_string(static_cast(index++))); + std::to_string(static_cast(index++))); if (need_else) { code_ += " else \\"; } else { From 68ad486ea7a2a86ea0e1d7d70d831d72b531975c Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sat, 9 Jan 2021 11:32:22 -0500 Subject: [PATCH 13/21] getter_for method returns result instead of address-of-getter. --- src/idl_gen_cpp.cpp | 5 +- .../generated_cpp17/monster_test_generated.h | 170 +++++++++--------- .../optional_scalars_generated.h | 74 ++++---- tests/cpp17/stringify_util.h | 4 +- 4 files changed, 126 insertions(+), 127 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 41bc80e750d..988582bcd3d 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2106,9 +2106,8 @@ class CppGenerator : public BaseGenerator { void GenIndexBasedFieldGetter(const StructDef &struct_def) { if (struct_def.fields.vec.empty()) { return; } code_ += " template"; - code_ += " static constexpr auto getter_for() {"; + code_ += " auto getter_for() const {"; - code_.SetValue("STRUCT_NAME", Name(struct_def)); size_t index = 0; bool need_else = false; // Generate one index-based getter for each field. @@ -2129,7 +2128,7 @@ class CppGenerator : public BaseGenerator { } need_else = true; code_ += "if constexpr (Index == {{FIELD_INDEX}}) \\"; - code_ += "return &{{STRUCT_NAME}}::{{FIELD_NAME}};"; + code_ += "return {{FIELD_NAME}}();"; } code_ += " else static_assert(Index != Index, \"Invalid Field Index\");"; code_ += " }"; diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 4470aba1097..699f4fa48ae 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -509,9 +509,9 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS { int8_t >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &Test::a; - else if constexpr (Index == 1) return &Test::b; + auto getter_for() const { + if constexpr (Index == 0) return a(); + else if constexpr (Index == 1) return b(); else static_assert(Index != Index, "Invalid Field Index"); } }; @@ -617,13 +617,13 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { const MyGame::Example::Test & >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &Vec3::x; - else if constexpr (Index == 1) return &Vec3::y; - else if constexpr (Index == 2) return &Vec3::z; - else if constexpr (Index == 3) return &Vec3::test1; - else if constexpr (Index == 4) return &Vec3::test2; - else if constexpr (Index == 5) return &Vec3::test3; + auto getter_for() const { + if constexpr (Index == 0) return x(); + else if constexpr (Index == 1) return y(); + else if constexpr (Index == 2) return z(); + else if constexpr (Index == 3) return test1(); + else if constexpr (Index == 4) return test2(); + else if constexpr (Index == 5) return test3(); else static_assert(Index != Index, "Invalid Field Index"); } }; @@ -684,9 +684,9 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { uint32_t >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &Ability::id; - else if constexpr (Index == 1) return &Ability::distance; + auto getter_for() const { + if constexpr (Index == 0) return id(); + else if constexpr (Index == 1) return distance(); else static_assert(Index != Index, "Invalid Field Index"); } }; @@ -839,8 +839,8 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta MyGame::Example::Color >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &TestSimpleTableWithEnum::color; + auto getter_for() const { + if constexpr (Index == 0) return color(); else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { @@ -940,10 +940,10 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { uint16_t >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &Stat::id; - else if constexpr (Index == 1) return &Stat::val; - else if constexpr (Index == 2) return &Stat::count; + auto getter_for() const { + if constexpr (Index == 0) return id(); + else if constexpr (Index == 1) return val(); + else if constexpr (Index == 2) return count(); else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { @@ -1053,8 +1053,8 @@ struct Referrable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { uint64_t >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &Referrable::id; + auto getter_for() const { + if constexpr (Index == 0) return id(); else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { @@ -1606,57 +1606,57 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector> * >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &Monster::pos; - else if constexpr (Index == 1) return &Monster::mana; - else if constexpr (Index == 2) return &Monster::hp; - else if constexpr (Index == 3) return &Monster::name; - else if constexpr (Index == 4) return &Monster::inventory; - else if constexpr (Index == 5) return &Monster::color; - else if constexpr (Index == 6) return &Monster::test_type; - else if constexpr (Index == 7) return &Monster::test; - else if constexpr (Index == 8) return &Monster::test4; - else if constexpr (Index == 9) return &Monster::testarrayofstring; - else if constexpr (Index == 10) return &Monster::testarrayoftables; - else if constexpr (Index == 11) return &Monster::enemy; - else if constexpr (Index == 12) return &Monster::testnestedflatbuffer; - else if constexpr (Index == 13) return &Monster::testempty; - else if constexpr (Index == 14) return &Monster::testbool; - else if constexpr (Index == 15) return &Monster::testhashs32_fnv1; - else if constexpr (Index == 16) return &Monster::testhashu32_fnv1; - else if constexpr (Index == 17) return &Monster::testhashs64_fnv1; - else if constexpr (Index == 18) return &Monster::testhashu64_fnv1; - else if constexpr (Index == 19) return &Monster::testhashs32_fnv1a; - else if constexpr (Index == 20) return &Monster::testhashu32_fnv1a; - else if constexpr (Index == 21) return &Monster::testhashs64_fnv1a; - else if constexpr (Index == 22) return &Monster::testhashu64_fnv1a; - else if constexpr (Index == 23) return &Monster::testarrayofbools; - else if constexpr (Index == 24) return &Monster::testf; - else if constexpr (Index == 25) return &Monster::testf2; - else if constexpr (Index == 26) return &Monster::testf3; - else if constexpr (Index == 27) return &Monster::testarrayofstring2; - else if constexpr (Index == 28) return &Monster::testarrayofsortedstruct; - else if constexpr (Index == 29) return &Monster::flex; - else if constexpr (Index == 30) return &Monster::test5; - else if constexpr (Index == 31) return &Monster::vector_of_longs; - else if constexpr (Index == 32) return &Monster::vector_of_doubles; - else if constexpr (Index == 33) return &Monster::parent_namespace_test; - else if constexpr (Index == 34) return &Monster::vector_of_referrables; - else if constexpr (Index == 35) return &Monster::single_weak_reference; - else if constexpr (Index == 36) return &Monster::vector_of_weak_references; - else if constexpr (Index == 37) return &Monster::vector_of_strong_referrables; - else if constexpr (Index == 38) return &Monster::co_owning_reference; - else if constexpr (Index == 39) return &Monster::vector_of_co_owning_references; - else if constexpr (Index == 40) return &Monster::non_owning_reference; - else if constexpr (Index == 41) return &Monster::vector_of_non_owning_references; - else if constexpr (Index == 42) return &Monster::any_unique_type; - else if constexpr (Index == 43) return &Monster::any_unique; - else if constexpr (Index == 44) return &Monster::any_ambiguous_type; - else if constexpr (Index == 45) return &Monster::any_ambiguous; - else if constexpr (Index == 46) return &Monster::vector_of_enums; - else if constexpr (Index == 47) return &Monster::signed_enum; - else if constexpr (Index == 48) return &Monster::testrequirednestedflatbuffer; - else if constexpr (Index == 49) return &Monster::scalar_key_sorted_tables; + auto getter_for() const { + if constexpr (Index == 0) return pos(); + else if constexpr (Index == 1) return mana(); + else if constexpr (Index == 2) return hp(); + else if constexpr (Index == 3) return name(); + else if constexpr (Index == 4) return inventory(); + else if constexpr (Index == 5) return color(); + else if constexpr (Index == 6) return test_type(); + else if constexpr (Index == 7) return test(); + else if constexpr (Index == 8) return test4(); + else if constexpr (Index == 9) return testarrayofstring(); + else if constexpr (Index == 10) return testarrayoftables(); + else if constexpr (Index == 11) return enemy(); + else if constexpr (Index == 12) return testnestedflatbuffer(); + else if constexpr (Index == 13) return testempty(); + else if constexpr (Index == 14) return testbool(); + else if constexpr (Index == 15) return testhashs32_fnv1(); + else if constexpr (Index == 16) return testhashu32_fnv1(); + else if constexpr (Index == 17) return testhashs64_fnv1(); + else if constexpr (Index == 18) return testhashu64_fnv1(); + else if constexpr (Index == 19) return testhashs32_fnv1a(); + else if constexpr (Index == 20) return testhashu32_fnv1a(); + else if constexpr (Index == 21) return testhashs64_fnv1a(); + else if constexpr (Index == 22) return testhashu64_fnv1a(); + else if constexpr (Index == 23) return testarrayofbools(); + else if constexpr (Index == 24) return testf(); + else if constexpr (Index == 25) return testf2(); + else if constexpr (Index == 26) return testf3(); + else if constexpr (Index == 27) return testarrayofstring2(); + else if constexpr (Index == 28) return testarrayofsortedstruct(); + else if constexpr (Index == 29) return flex(); + else if constexpr (Index == 30) return test5(); + else if constexpr (Index == 31) return vector_of_longs(); + else if constexpr (Index == 32) return vector_of_doubles(); + else if constexpr (Index == 33) return parent_namespace_test(); + else if constexpr (Index == 34) return vector_of_referrables(); + else if constexpr (Index == 35) return single_weak_reference(); + else if constexpr (Index == 36) return vector_of_weak_references(); + else if constexpr (Index == 37) return vector_of_strong_referrables(); + else if constexpr (Index == 38) return co_owning_reference(); + else if constexpr (Index == 39) return vector_of_co_owning_references(); + else if constexpr (Index == 40) return non_owning_reference(); + else if constexpr (Index == 41) return vector_of_non_owning_references(); + else if constexpr (Index == 42) return any_unique_type(); + else if constexpr (Index == 43) return any_unique(); + else if constexpr (Index == 44) return any_ambiguous_type(); + else if constexpr (Index == 45) return any_ambiguous(); + else if constexpr (Index == 46) return vector_of_enums(); + else if constexpr (Index == 47) return signed_enum(); + else if constexpr (Index == 48) return testrequirednestedflatbuffer(); + else if constexpr (Index == 49) return scalar_key_sorted_tables(); else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { @@ -2359,19 +2359,19 @@ struct TypeAliases FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector * >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &TypeAliases::i8; - else if constexpr (Index == 1) return &TypeAliases::u8; - else if constexpr (Index == 2) return &TypeAliases::i16; - else if constexpr (Index == 3) return &TypeAliases::u16; - else if constexpr (Index == 4) return &TypeAliases::i32; - else if constexpr (Index == 5) return &TypeAliases::u32; - else if constexpr (Index == 6) return &TypeAliases::i64; - else if constexpr (Index == 7) return &TypeAliases::u64; - else if constexpr (Index == 8) return &TypeAliases::f32; - else if constexpr (Index == 9) return &TypeAliases::f64; - else if constexpr (Index == 10) return &TypeAliases::v8; - else if constexpr (Index == 11) return &TypeAliases::vf64; + auto getter_for() const { + if constexpr (Index == 0) return i8(); + else if constexpr (Index == 1) return u8(); + else if constexpr (Index == 2) return i16(); + else if constexpr (Index == 3) return u16(); + else if constexpr (Index == 4) return i32(); + else if constexpr (Index == 5) return u32(); + else if constexpr (Index == 6) return i64(); + else if constexpr (Index == 7) return u64(); + else if constexpr (Index == 8) return f32(); + else if constexpr (Index == 9) return f64(); + else if constexpr (Index == 10) return v8(); + else if constexpr (Index == 11) return vf64(); else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index 8257c387e46..c0b2ce5e56c 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -389,43 +389,43 @@ struct ScalarStuff FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { optional_scalars::OptionalByte >; template - static constexpr auto getter_for() { - if constexpr (Index == 0) return &ScalarStuff::just_i8; - else if constexpr (Index == 1) return &ScalarStuff::maybe_i8; - else if constexpr (Index == 2) return &ScalarStuff::default_i8; - else if constexpr (Index == 3) return &ScalarStuff::just_u8; - else if constexpr (Index == 4) return &ScalarStuff::maybe_u8; - else if constexpr (Index == 5) return &ScalarStuff::default_u8; - else if constexpr (Index == 6) return &ScalarStuff::just_i16; - else if constexpr (Index == 7) return &ScalarStuff::maybe_i16; - else if constexpr (Index == 8) return &ScalarStuff::default_i16; - else if constexpr (Index == 9) return &ScalarStuff::just_u16; - else if constexpr (Index == 10) return &ScalarStuff::maybe_u16; - else if constexpr (Index == 11) return &ScalarStuff::default_u16; - else if constexpr (Index == 12) return &ScalarStuff::just_i32; - else if constexpr (Index == 13) return &ScalarStuff::maybe_i32; - else if constexpr (Index == 14) return &ScalarStuff::default_i32; - else if constexpr (Index == 15) return &ScalarStuff::just_u32; - else if constexpr (Index == 16) return &ScalarStuff::maybe_u32; - else if constexpr (Index == 17) return &ScalarStuff::default_u32; - else if constexpr (Index == 18) return &ScalarStuff::just_i64; - else if constexpr (Index == 19) return &ScalarStuff::maybe_i64; - else if constexpr (Index == 20) return &ScalarStuff::default_i64; - else if constexpr (Index == 21) return &ScalarStuff::just_u64; - else if constexpr (Index == 22) return &ScalarStuff::maybe_u64; - else if constexpr (Index == 23) return &ScalarStuff::default_u64; - else if constexpr (Index == 24) return &ScalarStuff::just_f32; - else if constexpr (Index == 25) return &ScalarStuff::maybe_f32; - else if constexpr (Index == 26) return &ScalarStuff::default_f32; - else if constexpr (Index == 27) return &ScalarStuff::just_f64; - else if constexpr (Index == 28) return &ScalarStuff::maybe_f64; - else if constexpr (Index == 29) return &ScalarStuff::default_f64; - else if constexpr (Index == 30) return &ScalarStuff::just_bool; - else if constexpr (Index == 31) return &ScalarStuff::maybe_bool; - else if constexpr (Index == 32) return &ScalarStuff::default_bool; - else if constexpr (Index == 33) return &ScalarStuff::just_enum; - else if constexpr (Index == 34) return &ScalarStuff::maybe_enum; - else if constexpr (Index == 35) return &ScalarStuff::default_enum; + auto getter_for() const { + if constexpr (Index == 0) return just_i8(); + else if constexpr (Index == 1) return maybe_i8(); + else if constexpr (Index == 2) return default_i8(); + else if constexpr (Index == 3) return just_u8(); + else if constexpr (Index == 4) return maybe_u8(); + else if constexpr (Index == 5) return default_u8(); + else if constexpr (Index == 6) return just_i16(); + else if constexpr (Index == 7) return maybe_i16(); + else if constexpr (Index == 8) return default_i16(); + else if constexpr (Index == 9) return just_u16(); + else if constexpr (Index == 10) return maybe_u16(); + else if constexpr (Index == 11) return default_u16(); + else if constexpr (Index == 12) return just_i32(); + else if constexpr (Index == 13) return maybe_i32(); + else if constexpr (Index == 14) return default_i32(); + else if constexpr (Index == 15) return just_u32(); + else if constexpr (Index == 16) return maybe_u32(); + else if constexpr (Index == 17) return default_u32(); + else if constexpr (Index == 18) return just_i64(); + else if constexpr (Index == 19) return maybe_i64(); + else if constexpr (Index == 20) return default_i64(); + else if constexpr (Index == 21) return just_u64(); + else if constexpr (Index == 22) return maybe_u64(); + else if constexpr (Index == 23) return default_u64(); + else if constexpr (Index == 24) return just_f32(); + else if constexpr (Index == 25) return maybe_f32(); + else if constexpr (Index == 26) return default_f32(); + else if constexpr (Index == 27) return just_f64(); + else if constexpr (Index == 28) return maybe_f64(); + else if constexpr (Index == 29) return default_f64(); + else if constexpr (Index == 30) return just_bool(); + else if constexpr (Index == 31) return maybe_bool(); + else if constexpr (Index == 32) return default_bool(); + else if constexpr (Index == 33) return just_enum(); + else if constexpr (Index == 34) return maybe_enum(); + else if constexpr (Index == 35) return default_enum(); else static_assert(Index != Index, "Invalid Field Index"); } bool Verify(flatbuffers::Verifier &verifier) const { diff --git a/tests/cpp17/stringify_util.h b/tests/cpp17/stringify_util.h index ee5b67aa7e8..8161d4c8b28 100644 --- a/tests/cpp17/stringify_util.h +++ b/tests/cpp17/stringify_util.h @@ -66,8 +66,8 @@ constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector::value; *******************************************************************************/ template std::string AddStringifiedField(const FBS &fbs, const std::string &indent) { - auto field_getter = FBS::template getter_for(); - auto value_string = StringifyFlatbufferValue((fbs.*field_getter)(), indent); + auto value_string = + StringifyFlatbufferValue(fbs.template getter_for(), indent); if (!value_string) { return ""; } return indent + FBS::Traits::field_names[Index] + " = " + *value_string + "\n"; From dcdb4e7f83fe51cf994d909b959c2703514a03c3 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sat, 20 Feb 2021 16:50:18 -0500 Subject: [PATCH 14/21] Next round of reviewer suggestions. --- src/idl_gen_cpp.cpp | 94 +++++-------- .../generated_cpp17/monster_test_generated.h | 127 ++++-------------- .../optional_scalars_generated.h | 42 +----- tests/cpp17/stringify_util.h | 27 ++-- tests/cpp17/test_cpp17.cpp | 14 +- 5 files changed, 78 insertions(+), 226 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index fcaff5bdceb..51f844976a3 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2069,43 +2069,18 @@ class CppGenerator : public BaseGenerator { code_ += " {{FIELD_TYPE}}\\"; } - // Sample for Vec3: - // - // using FieldTypes = std::tuple< - // float, - // float, - // float - // >; - // - void GenFieldTypes(const StructDef &struct_def, bool is_struct) { - code_ += " using FieldTypes = std::tuple<\\"; - if (struct_def.fields.vec.empty()) { - code_ += ">;"; - return; - } - code_ += ""; - // Generate the std::tuple elements. - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated) { - // Deprecated fields won't be accessible. - continue; - } - if (is_struct) { - GenStructFieldType(field); - } else { - GenTableFieldType(field); - } - if (it + 1 != struct_def.fields.vec.end()) { code_ += ","; } - } - code_ += "\n >;"; + void GenFieldTypeHelper(const StructDef &struct_def) { + if (struct_def.fields.vec.empty()) { return; } + code_.SetValue("STRUCT_NAME", Name(struct_def)); + code_ += " template"; + code_ += " using FieldType = \\"; + code_ += "decltype(std::declval<{{STRUCT_NAME}}>().get_field());"; } void GenIndexBasedFieldGetter(const StructDef &struct_def) { if (struct_def.fields.vec.empty()) { return; } code_ += " template"; - code_ += " auto getter_for() const {"; + code_ += " auto get_field() const {"; size_t index = 0; bool need_else = false; @@ -2170,6 +2145,26 @@ class CppGenerator : public BaseGenerator { code_ += "\n };"; } + void GenTraitsStruct(const StructDef &struct_def) { + code_.SetValue( + "FULLY_QUALIFIED_NAME", + struct_def.defined_namespace->GetFullyQualifiedName(Name(struct_def))); + code_ += "struct {{STRUCT_NAME}}::Traits {"; + code_ += " using type = {{STRUCT_NAME}};"; + if (!struct_def.fixed) { + // We have a table and not a struct. + code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};"; + } + code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; + code_ += + " static constexpr auto fully_qualified_name = " + "\"{{FULLY_QUALIFIED_NAME}}\";"; + GenFieldNames(struct_def); + GenFieldTypeHelper(struct_def); + code_ += "};"; + code_ += ""; + } + void GenTableFieldSetter(const FieldDef &field) { const auto &type = field.value.type; const bool is_scalar = IsScalar(type.base_type); @@ -2311,7 +2306,6 @@ class CppGenerator : public BaseGenerator { } if (opts_.g_cpp_std >= cpp::CPP_STD_17) { - GenFieldTypes(struct_def, /*is_struct=*/false); GenIndexBasedFieldGetter(struct_def); } @@ -2520,22 +2514,10 @@ class CppGenerator : public BaseGenerator { code_ += "}"; code_ += ""; - // Definition for type traits for this table type. This allows querying var- - // ious compile-time traits of the table. + // Definition for type traits for this table type. This al- + // lows querying various compile-time traits of the table. if (opts_.g_cpp_std >= cpp::CPP_STD_17) { - code_.SetValue("FULLY_QUALIFIED_NAME", - struct_def.defined_namespace->GetFullyQualifiedName( - Name(struct_def))); - code_ += "struct {{STRUCT_NAME}}::Traits {"; - code_ += " using type = {{STRUCT_NAME}};"; - code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};"; - code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; - code_ += - " static constexpr auto fully_qualified_name = " - "\"{{FULLY_QUALIFIED_NAME}}\";"; - GenFieldNames(struct_def); - code_ += "};"; - code_ += ""; + GenTraitsStruct(struct_def); } // Generate a CreateXDirect function with vector types as parameters @@ -3399,7 +3381,6 @@ class CppGenerator : public BaseGenerator { GenOperatorNewDelete(struct_def); if (opts_.g_cpp_std >= cpp::CPP_STD_17) { - GenFieldTypes(struct_def, /*is_struct=*/true); GenIndexBasedFieldGetter(struct_def); } @@ -3411,20 +3392,9 @@ class CppGenerator : public BaseGenerator { code_ += ""; // Definition for type traits for this struct type. This al- - // lows querying various compile-time traits of the struct. + // lows querying various compile-time traits of the table. if (opts_.g_cpp_std >= cpp::CPP_STD_17) { - code_.SetValue("FULLY_QUALIFIED_NAME", - struct_def.defined_namespace->GetFullyQualifiedName( - Name(struct_def))); - code_ += "struct {{STRUCT_NAME}}::Traits {"; - code_ += " using type = {{STRUCT_NAME}};"; - code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";"; - code_ += - " static constexpr auto fully_qualified_name = " - "\"{{FULLY_QUALIFIED_NAME}}\";"; - GenFieldNames(struct_def); - code_ += "};"; - code_ += ""; + GenTraitsStruct(struct_def); } } diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 699f4fa48ae..46b0859f16c 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -504,12 +504,8 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS { void mutate_b(int8_t _b) { flatbuffers::WriteScalar(&b_, _b); } - using FieldTypes = std::tuple< - int16_t, - int8_t - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return a(); else if constexpr (Index == 1) return b(); else static_assert(Index != Index, "Invalid Field Index"); @@ -525,6 +521,8 @@ struct Test::Traits { "a", "b" }; + template + using FieldType = decltype(std::declval().get_field()); }; FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { @@ -608,16 +606,8 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { MyGame::Example::Test &mutable_test3() { return test3_; } - using FieldTypes = std::tuple< - float, - float, - float, - double, - MyGame::Example::Color, - const MyGame::Example::Test & - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return x(); else if constexpr (Index == 1) return y(); else if constexpr (Index == 2) return z(); @@ -641,6 +631,8 @@ struct Vec3::Traits { "test2", "test3" }; + template + using FieldType = decltype(std::declval().get_field()); }; FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { @@ -679,12 +671,8 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { void mutate_distance(uint32_t _distance) { flatbuffers::WriteScalar(&distance_, _distance); } - using FieldTypes = std::tuple< - uint32_t, - uint32_t - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return id(); else if constexpr (Index == 1) return distance(); else static_assert(Index != Index, "Invalid Field Index"); @@ -700,6 +688,8 @@ struct Ability::Traits { "id", "distance" }; + template + using FieldType = decltype(std::declval().get_field()); }; } // namespace Example @@ -715,7 +705,6 @@ struct InParentNamespace FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { static const flatbuffers::TypeTable *MiniReflectTypeTable() { return InParentNamespaceTypeTable(); } - using FieldTypes = std::tuple<>; bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && verifier.EndTable(); @@ -769,7 +758,6 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { static const flatbuffers::TypeTable *MiniReflectTypeTable() { return MonsterTypeTable(); } - using FieldTypes = std::tuple<>; bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && verifier.EndTable(); @@ -835,11 +823,8 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta bool mutate_color(MyGame::Example::Color _color) { return SetField(VT_COLOR, static_cast(_color), 2); } - using FieldTypes = std::tuple< - MyGame::Example::Color - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return color(); else static_assert(Index != Index, "Invalid Field Index"); } @@ -887,6 +872,8 @@ struct TestSimpleTableWithEnum::Traits { static constexpr std::array field_names = { "color" }; + template + using FieldType = decltype(std::declval().get_field()); }; flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -934,13 +921,8 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { int KeyCompareWithValue(uint16_t val) const { return static_cast(count() > val) - static_cast(count() < val); } - using FieldTypes = std::tuple< - const flatbuffers::String *, - int64_t, - uint16_t - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return id(); else if constexpr (Index == 1) return val(); else if constexpr (Index == 2) return count(); @@ -1005,6 +987,8 @@ struct Stat::Traits { "val", "count" }; + template + using FieldType = decltype(std::declval().get_field()); }; inline flatbuffers::Offset CreateStatDirect( @@ -1049,11 +1033,8 @@ struct Referrable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { int KeyCompareWithValue(uint64_t val) const { return static_cast(id() > val) - static_cast(id() < val); } - using FieldTypes = std::tuple< - uint64_t - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return id(); else static_assert(Index != Index, "Invalid Field Index"); } @@ -1101,6 +1082,8 @@ struct Referrable::Traits { static constexpr std::array field_names = { "id" }; + template + using FieldType = decltype(std::declval().get_field()); }; flatbuffers::Offset CreateReferrable(flatbuffers::FlatBufferBuilder &_fbb, const ReferrableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -1553,60 +1536,8 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { flatbuffers::Vector> *mutable_scalar_key_sorted_tables() { return GetPointer> *>(VT_SCALAR_KEY_SORTED_TABLES); } - using FieldTypes = std::tuple< - const MyGame::Example::Vec3 *, - int16_t, - int16_t, - const flatbuffers::String *, - const flatbuffers::Vector *, - MyGame::Example::Color, - MyGame::Example::Any, - const void *, - const flatbuffers::Vector *, - const flatbuffers::Vector> *, - const flatbuffers::Vector> *, - const MyGame::Example::Monster *, - const flatbuffers::Vector *, - const MyGame::Example::Stat *, - bool, - int32_t, - uint32_t, - int64_t, - uint64_t, - int32_t, - uint32_t, - int64_t, - uint64_t, - const flatbuffers::Vector *, - float, - float, - float, - const flatbuffers::Vector> *, - const flatbuffers::Vector *, - const flatbuffers::Vector *, - const flatbuffers::Vector *, - const flatbuffers::Vector *, - const flatbuffers::Vector *, - const MyGame::InParentNamespace *, - const flatbuffers::Vector> *, - uint64_t, - const flatbuffers::Vector *, - const flatbuffers::Vector> *, - uint64_t, - const flatbuffers::Vector *, - uint64_t, - const flatbuffers::Vector *, - MyGame::Example::AnyUniqueAliases, - const void *, - MyGame::Example::AnyAmbiguousAliases, - const void *, - const flatbuffers::Vector *, - MyGame::Example::Race, - const flatbuffers::Vector *, - const flatbuffers::Vector> * - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return pos(); else if constexpr (Index == 1) return mana(); else if constexpr (Index == 2) return hp(); @@ -2104,6 +2035,8 @@ struct Monster::Traits { "testrequirednestedflatbuffer", "scalar_key_sorted_tables" }; + template + using FieldType = decltype(std::declval().get_field()); }; inline flatbuffers::Offset CreateMonsterDirect( @@ -2344,22 +2277,8 @@ struct TypeAliases FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { flatbuffers::Vector *mutable_vf64() { return GetPointer *>(VT_VF64); } - using FieldTypes = std::tuple< - int8_t, - uint8_t, - int16_t, - uint16_t, - int32_t, - uint32_t, - int64_t, - uint64_t, - float, - double, - const flatbuffers::Vector *, - const flatbuffers::Vector * - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return i8(); else if constexpr (Index == 1) return u8(); else if constexpr (Index == 2) return i16(); @@ -2497,6 +2416,8 @@ struct TypeAliases::Traits { "v8", "vf64" }; + template + using FieldType = decltype(std::declval().get_field()); }; inline flatbuffers::Offset CreateTypeAliasesDirect( diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index c0b2ce5e56c..dc003ac7b10 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -350,46 +350,8 @@ struct ScalarStuff FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool mutate_default_enum(optional_scalars::OptionalByte _default_enum) { return SetField(VT_DEFAULT_ENUM, static_cast(_default_enum), 1); } - using FieldTypes = std::tuple< - int8_t, - flatbuffers::Optional, - int8_t, - uint8_t, - flatbuffers::Optional, - uint8_t, - int16_t, - flatbuffers::Optional, - int16_t, - uint16_t, - flatbuffers::Optional, - uint16_t, - int32_t, - flatbuffers::Optional, - int32_t, - uint32_t, - flatbuffers::Optional, - uint32_t, - int64_t, - flatbuffers::Optional, - int64_t, - uint64_t, - flatbuffers::Optional, - uint64_t, - float, - flatbuffers::Optional, - float, - double, - flatbuffers::Optional, - double, - bool, - flatbuffers::Optional, - bool, - optional_scalars::OptionalByte, - flatbuffers::Optional, - optional_scalars::OptionalByte - >; template - auto getter_for() const { + auto get_field() const { if constexpr (Index == 0) return just_i8(); else if constexpr (Index == 1) return maybe_i8(); else if constexpr (Index == 2) return default_i8(); @@ -717,6 +679,8 @@ struct ScalarStuff::Traits { "maybe_enum", "default_enum" }; + template + using FieldType = decltype(std::declval().get_field()); }; flatbuffers::Offset CreateScalarStuff(flatbuffers::FlatBufferBuilder &_fbb, const ScalarStuffT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); diff --git a/tests/cpp17/stringify_util.h b/tests/cpp17/stringify_util.h index 8161d4c8b28..fd2fc3f69f0 100644 --- a/tests/cpp17/stringify_util.h +++ b/tests/cpp17/stringify_util.h @@ -27,6 +27,7 @@ #include #include "flatbuffers/flatbuffers.h" +#include "flatbuffers/util.h" namespace cpp17 { @@ -49,17 +50,16 @@ struct is_flatbuffers_table_or_struct> : std::true_type {}; template -constexpr bool is_flatbuffers_table_or_struct_v = +inline constexpr bool is_flatbuffers_table_or_struct_v = is_flatbuffers_table_or_struct::value; template struct is_flatbuffers_vector : std::false_type {}; -// We know it's a table or struct when it has a Traits subclass. template struct is_flatbuffers_vector> : std::true_type {}; template -constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector::value; +inline constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector::value; /******************************************************************************* ** Compile-time Iteration & Recursive Stringification over Flatbuffers types. @@ -67,7 +67,7 @@ constexpr bool is_flatbuffers_vector_v = is_flatbuffers_vector::value; template std::string AddStringifiedField(const FBS &fbs, const std::string &indent) { auto value_string = - StringifyFlatbufferValue(fbs.template getter_for(), indent); + StringifyFlatbufferValue(fbs.template get_field(), indent); if (!value_string) { return ""; } return indent + FBS::Traits::field_names[Index] + " = " + *value_string + "\n"; @@ -83,13 +83,13 @@ std::string StringifyTableOrStructImpl(const FBS &fbs, template std::string StringifyTableOrStruct(const FBS &fbs, const std::string &indent) { - constexpr size_t field_count = std::tuple_size_v; + constexpr size_t field_count = FBS::Traits::field_names.size(); std::string out; if constexpr (field_count > 0) { out = std::string(FBS::Traits::fully_qualified_name) + "{\n" + StringifyTableOrStructImpl(fbs, indent + " ", std::make_index_sequence{}) + - indent + "}"; + indent + '}'; } return out; } @@ -109,12 +109,12 @@ std::string StringifyVector(const flatbuffers::Vector &vec, if (vec.cbegin() != vec.cend()) { text.resize(text.size() - epilogue.size()); } - text += std::string("\n") + indent + "]"; + text += '\n' + indent + ']'; return text; } template std::string StringifyArithmeticType(T val) { - return std::to_string(val); + return flatbuffers::NumToString(val); } } // namespace detail @@ -127,9 +127,7 @@ std::optional StringifyFlatbufferValue(T &&val, const std::string &indent) { constexpr bool is_pointer = std::is_pointer_v>; if constexpr (is_pointer) { - if (val == nullptr) { - return std::nullopt; // Field is absent. - } + if (val == nullptr) return std::nullopt; // Field is absent. } using decayed = std::decay_t>>; @@ -137,11 +135,10 @@ std::optional StringifyFlatbufferValue(T &&val, // Is it a Flatbuffers Table or Struct? if constexpr (detail::is_flatbuffers_table_or_struct_v) { // We have a nested table or struct; use recursion! - if constexpr (is_pointer) { + if constexpr (is_pointer) return detail::StringifyTableOrStruct(*val, indent); - } else { + else return detail::StringifyTableOrStruct(val, indent); - } } // Is it an 8-bit number? If so, print it like an int (not char). @@ -164,7 +161,7 @@ std::optional StringifyFlatbufferValue(T &&val, // Is it a Flatbuffers string? else if constexpr (std::is_same_v) { - return "\"" + val->str() + "\""; + return '"' + val->str() + '"'; } // Is it a Flatbuffers Vector? diff --git a/tests/cpp17/test_cpp17.cpp b/tests/cpp17/test_cpp17.cpp index e5c35d1ee86..34219391ab2 100644 --- a/tests/cpp17/test_cpp17.cpp +++ b/tests/cpp17/test_cpp17.cpp @@ -104,10 +104,10 @@ void StringifyAnyFlatbuffersTypeTest() { std::string expected = R"(MyGame.Example.Monster{ pos = MyGame.Example.Vec3{ - x = 1.100000 - y = 2.200000 - z = 3.300000 - test1 = 6.600000 + x = 1.1 + y = 2.2 + z = 3.3 + test1 = 6.6 test2 = 2 test3 = MyGame.Example.Test{ a = 11 @@ -134,9 +134,9 @@ void StringifyAnyFlatbuffersTypeTest() { testhashu32_fnv1a = 9 testhashs64_fnv1a = 10 testhashu64_fnv1a = 11 - testf = 12.100000 - testf2 = 13.100000 - testf3 = 14.100000 + testf = 12.1 + testf2 = 13.1 + testf3 = 14.1 single_weak_reference = 15 co_owning_reference = 16 non_owning_reference = 17 From d4ed8a42faf0100f7c79d3311440b2f48bda9123 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sat, 20 Feb 2021 16:55:38 -0500 Subject: [PATCH 15/21] Use type instead of hardcoded struct name. --- src/idl_gen_cpp.cpp | 11 +++++------ .../generated_cpp17/monster_test_generated.h | 16 ++++++++-------- .../generated_cpp17/optional_scalars_generated.h | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 51f844976a3..8ebba7089a9 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2071,10 +2071,9 @@ class CppGenerator : public BaseGenerator { void GenFieldTypeHelper(const StructDef &struct_def) { if (struct_def.fields.vec.empty()) { return; } - code_.SetValue("STRUCT_NAME", Name(struct_def)); code_ += " template"; code_ += " using FieldType = \\"; - code_ += "decltype(std::declval<{{STRUCT_NAME}}>().get_field());"; + code_ += "decltype(std::declval().get_field());"; } void GenIndexBasedFieldGetter(const StructDef &struct_def) { @@ -2514,8 +2513,8 @@ class CppGenerator : public BaseGenerator { code_ += "}"; code_ += ""; - // Definition for type traits for this table type. This al- - // lows querying various compile-time traits of the table. + // Definition for type traits for this table type. This allows querying var- + // ious compile-time traits of the table. if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } @@ -3391,8 +3390,8 @@ class CppGenerator : public BaseGenerator { if (opts_.gen_compare) GenCompareOperator(struct_def, "()"); code_ += ""; - // Definition for type traits for this struct type. This al- - // lows querying various compile-time traits of the table. + // Definition for type traits for this table type. This allows querying var- + // ious compile-time traits of the table. if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 46b0859f16c..256830be215 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -522,7 +522,7 @@ struct Test::Traits { "b" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { @@ -632,7 +632,7 @@ struct Vec3::Traits { "test3" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { @@ -689,7 +689,7 @@ struct Ability::Traits { "distance" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; } // namespace Example @@ -873,7 +873,7 @@ struct TestSimpleTableWithEnum::Traits { "color" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -988,7 +988,7 @@ struct Stat::Traits { "count" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; inline flatbuffers::Offset CreateStatDirect( @@ -1083,7 +1083,7 @@ struct Referrable::Traits { "id" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; flatbuffers::Offset CreateReferrable(flatbuffers::FlatBufferBuilder &_fbb, const ReferrableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -2036,7 +2036,7 @@ struct Monster::Traits { "scalar_key_sorted_tables" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; inline flatbuffers::Offset CreateMonsterDirect( @@ -2417,7 +2417,7 @@ struct TypeAliases::Traits { "vf64" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; inline flatbuffers::Offset CreateTypeAliasesDirect( diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index dc003ac7b10..cd0a7b11c7d 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -680,7 +680,7 @@ struct ScalarStuff::Traits { "default_enum" }; template - using FieldType = decltype(std::declval().get_field()); + using FieldType = decltype(std::declval().get_field()); }; flatbuffers::Offset CreateScalarStuff(flatbuffers::FlatBufferBuilder &_fbb, const ScalarStuffT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); From 191029a7cb6423c4b5ed15909b9faa582f0cd4a3 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sat, 20 Feb 2021 17:43:37 -0500 Subject: [PATCH 16/21] Fix clang-format diff. --- src/idl_gen_cpp.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 8ebba7089a9..47af07847f3 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2515,9 +2515,7 @@ class CppGenerator : public BaseGenerator { // Definition for type traits for this table type. This allows querying var- // ious compile-time traits of the table. - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { - GenTraitsStruct(struct_def); - } + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } // Generate a CreateXDirect function with vector types as parameters if (opts_.cpp_direct_copy && has_string_or_vector_fields) { @@ -3392,9 +3390,7 @@ class CppGenerator : public BaseGenerator { // Definition for type traits for this table type. This allows querying var- // ious compile-time traits of the table. - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { - GenTraitsStruct(struct_def); - } + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } } // Set up the correct namespace. Only open a namespace if the existing one is From db4fa279b18931da4ab4c611c01f715f64be5a9b Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sun, 21 Feb 2021 10:44:38 -0500 Subject: [PATCH 17/21] Add test for FieldType since it is not used in the stringify test. --- tests/cpp17/test_cpp17.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/cpp17/test_cpp17.cpp b/tests/cpp17/test_cpp17.cpp index 34219391ab2..47083d383ef 100644 --- a/tests/cpp17/test_cpp17.cpp +++ b/tests/cpp17/test_cpp17.cpp @@ -162,6 +162,18 @@ void StringifyAnyFlatbuffersTypeTest() { TEST_EQ_STR(expected.c_str(), result->c_str()); } +/******************************************************************************* +** Test Traits::FieldType +*******************************************************************************/ +using pos_type = Monster::Traits::FieldType<0>; +static_assert(std::is_same_v); + +using mana_type = Monster::Traits::FieldType<1>; +static_assert(std::is_same_v); + +using name_type = Monster::Traits::FieldType<3>; +static_assert(std::is_same_v); + /******************************************************************************* ** Generic Create Function Test. *******************************************************************************/ From 17e75b560cf8fe2ed7dff069226a85f3500f68a3 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Sun, 28 Feb 2021 12:29:41 -0500 Subject: [PATCH 18/21] Add fields_number field to Traits struct. --- src/idl_gen_cpp.cpp | 11 +++++++++++ tests/cpp17/generated_cpp17/monster_test_generated.h | 10 ++++++++++ .../generated_cpp17/optional_scalars_generated.h | 1 + tests/cpp17/stringify_util.h | 2 +- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 47af07847f3..02ba69985a9 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2144,6 +2144,16 @@ class CppGenerator : public BaseGenerator { code_ += "\n };"; } + void GenFieldsNumber(const StructDef &struct_def) { + auto non_deprecated_field_count = std::count_if( + struct_def.fields.vec.begin(), struct_def.fields.vec.end(), + [](const FieldDef *field) { return !field->deprecated; }); + code_.SetValue( + "FIELD_COUNT", + std::to_string(static_cast(non_deprecated_field_count))); + code_ += " static constexpr size_t fields_number = {{FIELD_COUNT}};"; + } + void GenTraitsStruct(const StructDef &struct_def) { code_.SetValue( "FULLY_QUALIFIED_NAME", @@ -2160,6 +2170,7 @@ class CppGenerator : public BaseGenerator { "\"{{FULLY_QUALIFIED_NAME}}\";"; GenFieldNames(struct_def); GenFieldTypeHelper(struct_def); + GenFieldsNumber(struct_def); code_ += "};"; code_ += ""; } diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 256830be215..94c5daca836 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -523,6 +523,7 @@ struct Test::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 2; }; FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { @@ -633,6 +634,7 @@ struct Vec3::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 6; }; FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { @@ -690,6 +692,7 @@ struct Ability::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 2; }; } // namespace Example @@ -741,6 +744,7 @@ struct InParentNamespace::Traits { static constexpr auto name = "InParentNamespace"; static constexpr auto fully_qualified_name = "MyGame.InParentNamespace"; static constexpr std::array field_names = {}; + static constexpr size_t fields_number = 0; }; flatbuffers::Offset CreateInParentNamespace(flatbuffers::FlatBufferBuilder &_fbb, const InParentNamespaceT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -794,6 +798,7 @@ struct Monster::Traits { static constexpr auto name = "Monster"; static constexpr auto fully_qualified_name = "MyGame.Example2.Monster"; static constexpr std::array field_names = {}; + static constexpr size_t fields_number = 0; }; flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -874,6 +879,7 @@ struct TestSimpleTableWithEnum::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 1; }; flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -989,6 +995,7 @@ struct Stat::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 3; }; inline flatbuffers::Offset CreateStatDirect( @@ -1084,6 +1091,7 @@ struct Referrable::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 1; }; flatbuffers::Offset CreateReferrable(flatbuffers::FlatBufferBuilder &_fbb, const ReferrableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -2037,6 +2045,7 @@ struct Monster::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 50; }; inline flatbuffers::Offset CreateMonsterDirect( @@ -2418,6 +2427,7 @@ struct TypeAliases::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 12; }; inline flatbuffers::Offset CreateTypeAliasesDirect( diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index cd0a7b11c7d..30d865e4da3 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -681,6 +681,7 @@ struct ScalarStuff::Traits { }; template using FieldType = decltype(std::declval().get_field()); + static constexpr size_t fields_number = 36; }; flatbuffers::Offset CreateScalarStuff(flatbuffers::FlatBufferBuilder &_fbb, const ScalarStuffT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); diff --git a/tests/cpp17/stringify_util.h b/tests/cpp17/stringify_util.h index fd2fc3f69f0..dc94722406f 100644 --- a/tests/cpp17/stringify_util.h +++ b/tests/cpp17/stringify_util.h @@ -83,7 +83,7 @@ std::string StringifyTableOrStructImpl(const FBS &fbs, template std::string StringifyTableOrStruct(const FBS &fbs, const std::string &indent) { - constexpr size_t field_count = FBS::Traits::field_names.size(); + 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" + From 4bc3ca96515ea43523b4fcca82a49d8473dd9936 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Fri, 5 Mar 2021 09:36:22 -0500 Subject: [PATCH 19/21] Add --cpp-static-reflection flag and put those features behind it. --- include/flatbuffers/idl.h | 2 ++ src/flatc.cpp | 5 +++++ src/idl_gen_cpp.cpp | 19 ++++++++++++------- tests/generate_code.sh | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 0e9a3bcd5cc..d9ef95b4954 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -588,6 +588,7 @@ struct IDLOptions { bool cs_gen_json_serializer; std::vector cpp_includes; std::string cpp_std; + bool cpp_static_reflection; std::string proto_namespace_suffix; std::string filename_suffix; std::string filename_extension; @@ -673,6 +674,7 @@ struct IDLOptions { force_defaults(false), java_primitive_has_method(false), cs_gen_json_serializer(false), + cpp_static_reflection(false), filename_suffix("_generated"), filename_extension(), no_warnings(false), diff --git a/src/flatc.cpp b/src/flatc.cpp index 6eeefab654d..221b88676df 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -126,6 +126,9 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " * 'c++0x' - generate code compatible with old compilers;\n" " * 'c++11' - use C++11 code generator (default);\n" " * 'c++17' - use C++17 features in generated code (experimental).\n" + " --cpp-static-reflection When using C++17, generate extra code to provide compile-time\n" + " (static) reflection of Flatbuffers types. Requires --cpp-std\n" + " to be \"c++17\" or higher.\n" " --object-prefix Customise class prefix for C++ object-based API.\n" " --object-suffix Customise class suffix for C++ object-based API.\n" " Default value is \"T\".\n" @@ -360,6 +363,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.cpp_std = argv[argi]; } else if (arg.rfind("--cpp-std=", 0) == 0) { opts.cpp_std = arg.substr(std::string("--cpp-std=").size()); + } else if (arg == "--cpp-static-reflection") { + opts.cpp_static_reflection = true; } else { for (size_t i = 0; i < params_.num_generators; ++i) { if (arg == params_.generators[i].generator_opt_long || diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 02ba69985a9..f4d902c5f37 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -317,7 +317,7 @@ class CppGenerator : public BaseGenerator { if (opts_.gen_nullable) { code_ += "#pragma clang system_header\n\n"; } - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + if (opts_.cpp_static_reflection) { code_ += "#include "; code_ += ""; } @@ -2232,7 +2232,7 @@ class CppGenerator : public BaseGenerator { code_ += " typedef {{NATIVE_NAME}} NativeTableType;"; } code_ += " typedef {{STRUCT_NAME}}Builder Builder;"; - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { code_ += " struct Traits;"; } + if (opts_.cpp_static_reflection) { code_ += " struct Traits;"; } if (opts_.mini_reflect != IDLOptions::kNone) { code_ += " static const flatbuffers::TypeTable *MiniReflectTypeTable() {"; @@ -2315,7 +2315,7 @@ class CppGenerator : public BaseGenerator { if (field.key) { GenKeyFieldMethods(field); } } - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + if (opts_.cpp_static_reflection) { GenIndexBasedFieldGetter(struct_def); } @@ -2526,7 +2526,7 @@ class CppGenerator : public BaseGenerator { // Definition for type traits for this table type. This allows querying var- // ious compile-time traits of the table. - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } + if (opts_.cpp_static_reflection) { GenTraitsStruct(struct_def); } // Generate a CreateXDirect function with vector types as parameters if (opts_.cpp_direct_copy && has_string_or_vector_fields) { @@ -3300,7 +3300,7 @@ class CppGenerator : public BaseGenerator { code_ += ""; code_ += " public:"; - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { code_ += " struct Traits;"; } + if (opts_.cpp_static_reflection) { code_ += " struct Traits;"; } // Make TypeTable accessible via the generated struct. if (opts_.mini_reflect != IDLOptions::kNone) { @@ -3388,7 +3388,7 @@ class CppGenerator : public BaseGenerator { code_.SetValue("NATIVE_NAME", Name(struct_def)); GenOperatorNewDelete(struct_def); - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + if (opts_.cpp_static_reflection) { GenIndexBasedFieldGetter(struct_def); } @@ -3401,7 +3401,7 @@ class CppGenerator : public BaseGenerator { // Definition for type traits for this table type. This allows querying var- // ious compile-time traits of the table. - if (opts_.g_cpp_std >= cpp::CPP_STD_17) { GenTraitsStruct(struct_def); } + if (opts_.cpp_static_reflection) { GenTraitsStruct(struct_def); } } // Set up the correct namespace. Only open a namespace if the existing one is @@ -3474,6 +3474,11 @@ bool GenerateCPP(const Parser &parser, const std::string &path, // The opts.scoped_enums has priority. opts.g_only_fixed_enums |= opts.scoped_enums; + if (opts.cpp_static_reflection && opts.g_cpp_std < cpp::CPP_STD_17) { + LogCompilerError("--cpp-static-reflection requires using --cpp-std at \"C++17\" or higher."); + return false; + } + cpp::CppGenerator generator(parser, path, file_name, opts); return generator.generate(); } diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 45c7ccd9bf9..4f6b43747d6 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -88,7 +88,7 @@ else fi # Flag c++17 requires Clang6, GCC7, MSVC2017 (_MSC_VER >= 1914) or higher. -TEST_CPP17_FLAGS="--cpp --cpp-std c++17 -o ./cpp17/generated_cpp17 $TEST_NOINCL_FLAGS" +TEST_CPP17_FLAGS="--cpp --cpp-std c++17 --cpp-static-reflection -o ./cpp17/generated_cpp17 $TEST_NOINCL_FLAGS" ../flatc $TEST_CPP17_FLAGS -I include_test monster_test.fbs ../flatc $TEST_CPP17_FLAGS optional_scalars.fbs From 8d7b9dceaee9d439f4a159b97f8cbed24a06cb18 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Fri, 5 Mar 2021 10:22:50 -0500 Subject: [PATCH 20/21] Fix clang-format diffs. --- src/idl_gen_cpp.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index f4d902c5f37..f5dbe418dcb 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2315,9 +2315,7 @@ class CppGenerator : public BaseGenerator { if (field.key) { GenKeyFieldMethods(field); } } - if (opts_.cpp_static_reflection) { - GenIndexBasedFieldGetter(struct_def); - } + if (opts_.cpp_static_reflection) { GenIndexBasedFieldGetter(struct_def); } // Generate a verifier function that can check a buffer from an untrusted // source will never cause reads outside the buffer. @@ -3388,9 +3386,7 @@ class CppGenerator : public BaseGenerator { code_.SetValue("NATIVE_NAME", Name(struct_def)); GenOperatorNewDelete(struct_def); - if (opts_.cpp_static_reflection) { - GenIndexBasedFieldGetter(struct_def); - } + if (opts_.cpp_static_reflection) { GenIndexBasedFieldGetter(struct_def); } code_ += "};"; @@ -3475,7 +3471,9 @@ bool GenerateCPP(const Parser &parser, const std::string &path, opts.g_only_fixed_enums |= opts.scoped_enums; if (opts.cpp_static_reflection && opts.g_cpp_std < cpp::CPP_STD_17) { - LogCompilerError("--cpp-static-reflection requires using --cpp-std at \"C++17\" or higher."); + LogCompilerError( + "--cpp-static-reflection requires using --cpp-std at \"C++17\" or " + "higher."); return false; } From 51021080e7a4117f22e3cd56c56d930222b461e3 Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Fri, 5 Mar 2021 11:45:26 -0500 Subject: [PATCH 21/21] Remove include. --- src/idl_gen_cpp.cpp | 5 ----- tests/cpp17/generated_cpp17/monster_test_generated.h | 2 -- tests/cpp17/generated_cpp17/optional_scalars_generated.h | 2 -- 3 files changed, 9 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index f5dbe418dcb..d9f495b0509 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -317,11 +317,6 @@ class CppGenerator : public BaseGenerator { if (opts_.gen_nullable) { code_ += "#pragma clang system_header\n\n"; } - if (opts_.cpp_static_reflection) { - code_ += "#include "; - code_ += ""; - } - code_ += "#include \"flatbuffers/flatbuffers.h\""; if (parser_.uses_flexbuffers_) { code_ += "#include \"flatbuffers/flexbuffers.h\""; diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index 94c5daca836..30aee2272dc 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -4,8 +4,6 @@ #ifndef FLATBUFFERS_GENERATED_MONSTERTEST_MYGAME_EXAMPLE_H_ #define FLATBUFFERS_GENERATED_MONSTERTEST_MYGAME_EXAMPLE_H_ -#include - #include "flatbuffers/flatbuffers.h" #include "flatbuffers/flexbuffers.h" diff --git a/tests/cpp17/generated_cpp17/optional_scalars_generated.h b/tests/cpp17/generated_cpp17/optional_scalars_generated.h index 30d865e4da3..4e99e7c299a 100644 --- a/tests/cpp17/generated_cpp17/optional_scalars_generated.h +++ b/tests/cpp17/generated_cpp17/optional_scalars_generated.h @@ -4,8 +4,6 @@ #ifndef FLATBUFFERS_GENERATED_OPTIONALSCALARS_OPTIONAL_SCALARS_H_ #define FLATBUFFERS_GENERATED_OPTIONALSCALARS_OPTIONAL_SCALARS_H_ -#include - #include "flatbuffers/flatbuffers.h" namespace optional_scalars {