From a5d9d0f7d368054fd1691aedf1db4116efcc233e Mon Sep 17 00:00:00 2001 From: "David P. Sicilia" Date: Thu, 2 Jan 2020 13:12:14 -0500 Subject: [PATCH] [C++17] Add Traits class for Tables and Factory function within it. (#5678) * Include flattests_cpp17 in unit tests when C++17 build is enabled. * [C++17] Generate generic table factory function. 1. For each table, generate a convenient free-standing factory function that allows creating the table in a generic way by specifying only the type. This is the first change in a series of changes to make Flatbuffers generated C++ code more friendly to code bases that make use of C++ template metaprogramming techniques to manage the serialization process. Example: Before :( // The name of the Flatbuffers type (and namespace) must // be hard-coded when writing the factory function. auto monster = MyGame::Example::CreateMonster(fbb, ...); After :) using type_to_create = MyGame::Example::Monster; // No namespace needed on CreateByTagType. auto monster = CreateByTagType((type_to_create*)nullptr, fbb, ...); This feature requires building with C++14 or greater, and thus it is guarded behind --cpp-std >= c++17 in the flatbuffers C++ generator. 2. Fix a CMake bug to include C++17 unit tests in test suite. * [C++17] Replace standalone variadic factory function with type_traits. Add a `type_traits` to each table class. This `type_traits` can be populated with various compile-time info about the table. Initially, we have the Create* function and type, but is extensible in the future. * Remove empty line and fix stale comments. * Rename type_traits to Traits and move fwd declaration. * Fix parameter evaluation order issue and use lambda for scope. --- CMakeLists.txt | 3 ++ src/idl_gen_cpp.cpp | 13 ++++++ .../generated_cpp17/monster_test_generated.h | 42 +++++++++++++++++++ tests/cpp17/test_cpp17.cpp | 25 ++++++++++- 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d4ecc810..2fed20197 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -530,6 +530,9 @@ if(FLATBUFFERS_BUILD_TESTS) file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/tests" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") add_test(NAME flattests COMMAND flattests) + if(FLATBUFFERS_BUILD_CPP17) + add_test(NAME flattests_cpp17 COMMAND flattests_cpp17) + endif() if(FLATBUFFERS_BUILD_GRPCTEST) add_test(NAME grpctest COMMAND grpctest) endif() diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 12e433754..f74abbf82 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -1837,6 +1837,9 @@ 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_.mini_reflect != IDLOptions::kNone) { code_ += " static const flatbuffers::TypeTable *MiniReflectTypeTable() {"; @@ -2198,6 +2201,16 @@ 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. + if (opts_.g_cpp_std >= cpp::CPP_STD_17) { + code_ += "struct {{STRUCT_NAME}}::Traits {"; + code_ += " using type = {{STRUCT_NAME}};"; + code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};"; + code_ += "};"; + code_ += ""; + } + // Generate a CreateXDirect function with vector types as parameters if (has_string_or_vector_fields) { code_ += diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index ea29cfc5d..a43c12ff1 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -621,6 +621,7 @@ struct InParentNamespaceT : public flatbuffers::NativeTable { struct InParentNamespace FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef InParentNamespaceT NativeTableType; typedef InParentNamespaceBuilder Builder; + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return InParentNamespaceTypeTable(); } @@ -655,6 +656,11 @@ inline flatbuffers::Offset CreateInParentNamespace( return builder_.Finish(); } +struct InParentNamespace::Traits { + using type = InParentNamespace; + static auto constexpr Create = CreateInParentNamespace; +}; + flatbuffers::Offset CreateInParentNamespace(flatbuffers::FlatBufferBuilder &_fbb, const InParentNamespaceT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); namespace Example2 { @@ -668,6 +674,7 @@ struct MonsterT : public flatbuffers::NativeTable { struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef MonsterT NativeTableType; typedef MonsterBuilder Builder; + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return MonsterTypeTable(); } @@ -702,6 +709,11 @@ inline flatbuffers::Offset CreateMonster( return builder_.Finish(); } +struct Monster::Traits { + using type = Monster; + static auto constexpr Create = CreateMonster; +}; + flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); } // namespace Example2 @@ -719,6 +731,7 @@ struct TestSimpleTableWithEnumT : public flatbuffers::NativeTable { struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef TestSimpleTableWithEnumT NativeTableType; typedef TestSimpleTableWithEnumBuilder Builder; + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return TestSimpleTableWithEnumTypeTable(); } @@ -768,6 +781,11 @@ inline flatbuffers::Offset CreateTestSimpleTableWithEnu return builder_.Finish(); } +struct TestSimpleTableWithEnum::Traits { + using type = TestSimpleTableWithEnum; + static auto constexpr Create = CreateTestSimpleTableWithEnum; +}; + flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); struct StatT : public flatbuffers::NativeTable { @@ -784,6 +802,7 @@ struct StatT : public flatbuffers::NativeTable { struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef StatT NativeTableType; typedef StatBuilder Builder; + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return StatTypeTable(); } @@ -860,6 +879,11 @@ inline flatbuffers::Offset CreateStat( return builder_.Finish(); } +struct Stat::Traits { + using type = Stat; + static auto constexpr Create = CreateStat; +}; + inline flatbuffers::Offset CreateStatDirect( flatbuffers::FlatBufferBuilder &_fbb, const char *id = nullptr, @@ -886,6 +910,7 @@ struct ReferrableT : public flatbuffers::NativeTable { struct Referrable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef ReferrableT NativeTableType; typedef ReferrableBuilder Builder; + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return ReferrableTypeTable(); } @@ -941,6 +966,11 @@ inline flatbuffers::Offset CreateReferrable( return builder_.Finish(); } +struct Referrable::Traits { + using type = Referrable; + static auto constexpr Create = CreateReferrable; +}; + flatbuffers::Offset CreateReferrable(flatbuffers::FlatBufferBuilder &_fbb, const ReferrableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); struct MonsterT : public flatbuffers::NativeTable { @@ -1017,6 +1047,7 @@ struct MonsterT : public flatbuffers::NativeTable { struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef MonsterT NativeTableType; typedef MonsterBuilder Builder; + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return MonsterTypeTable(); } @@ -1766,6 +1797,11 @@ inline flatbuffers::Offset CreateMonster( return builder_.Finish(); } +struct Monster::Traits { + using type = Monster; + static auto constexpr Create = CreateMonster; +}; + inline flatbuffers::Offset CreateMonsterDirect( flatbuffers::FlatBufferBuilder &_fbb, const MyGame::Example::Vec3 *pos = 0, @@ -1920,6 +1956,7 @@ struct TypeAliasesT : public flatbuffers::NativeTable { struct TypeAliases FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef TypeAliasesT NativeTableType; typedef TypeAliasesBuilder Builder; + struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { return TypeAliasesTypeTable(); } @@ -2114,6 +2151,11 @@ inline flatbuffers::Offset CreateTypeAliases( return builder_.Finish(); } +struct TypeAliases::Traits { + using type = TypeAliases; + static auto constexpr Create = CreateTypeAliases; +}; + inline flatbuffers::Offset CreateTypeAliasesDirect( flatbuffers::FlatBufferBuilder &_fbb, int8_t i8 = 0, diff --git a/tests/cpp17/test_cpp17.cpp b/tests/cpp17/test_cpp17.cpp index 291aa8389..91390bda9 100644 --- a/tests/cpp17/test_cpp17.cpp +++ b/tests/cpp17/test_cpp17.cpp @@ -36,8 +36,31 @@ namespace cpp11 { #include "../monster_test_generated.h" } // namespace cpp11 +void CreateTableByTypeTest() { + flatbuffers::FlatBufferBuilder builder; + + // We will create an object of this type using only the type. + using type_to_create_t = cpp17::MyGame::Example::Stat; + + [&builder] { + auto id_str = builder.CreateString("my_id"); + auto table = type_to_create_t::Traits::Create(builder, id_str, 42, 7); + // Be sure that the correct return type was inferred. + static_assert( + std::is_same_v>); + builder.Finish(table); + }(); + + // Access it. + auto stat = + flatbuffers::GetRoot(builder.GetBufferPointer()); + TEST_EQ_STR(stat->id()->c_str(), "my_id"); + TEST_EQ(stat->val(), 42); + TEST_EQ(stat->count(), 7); +} + int FlatBufferCpp17Tests() { - TEST_ASSERT(true); + CreateTableByTypeTest(); return 0; }