forked from BigfootDev/flatbuffers
[C++17] Add compile-time reflection for fields. (#6324)
* [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.
* Fix int-conversion warning on MSVC.
* Try to fix std::to_string ambiguity on MSVC.
* Fix clang-format diffs.
* Fix more clang-format diffs.
* Fix last clang-format diff.
* Enable C++17 build/test for VC 19 platform in CI.
* Forgot to add value to cmake command line variable.
* Various fixes/changes in response to @vglavnyy's feedback.
* Replace "fields pack" with index-based getters.
* Fix MSVC error.
* Fix clang-format diffs.
* getter_for method returns result instead of address-of-getter.
* Next round of reviewer suggestions.
* Use type instead of hardcoded struct name.
* Fix clang-format diff.
* Add test for FieldType since it is not used in the stringify test.
* Add fields_number field to Traits struct.
* Add --cpp-static-reflection flag and put those features behind it.
* Fix clang-format diffs.
* Remove <tuple> include.
This commit is contained in:
@@ -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,144 @@ namespace cpp11 {
|
||||
#include "../optional_scalars_generated.h"
|
||||
} // namespace cpp11
|
||||
|
||||
using ::cpp17::MyGame::Example::Monster;
|
||||
using ::cpp17::MyGame::Example::Vec3;
|
||||
|
||||
/*******************************************************************************
|
||||
** Build some FB objects.
|
||||
*******************************************************************************/
|
||||
const Monster *BuildMonster(flatbuffers::FlatBufferBuilder &fbb) {
|
||||
using ::cpp17::MyGame::Example::Color;
|
||||
using ::cpp17::MyGame::Example::MonsterBuilder;
|
||||
using ::cpp17::MyGame::Example::Test;
|
||||
auto name = fbb.CreateString("my_monster");
|
||||
auto inventory = fbb.CreateVector(std::vector<uint8_t>{ 4, 5, 6, 7 });
|
||||
MonsterBuilder builder(fbb);
|
||||
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);
|
||||
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.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);
|
||||
builder.add_inventory(inventory);
|
||||
fbb.Finish(builder.Finish());
|
||||
const Monster *monster =
|
||||
flatbuffers::GetRoot<Monster>(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"(MyGame.Example.Monster{
|
||||
pos = MyGame.Example.Vec3{
|
||||
x = 1.1
|
||||
y = 2.2
|
||||
z = 3.3
|
||||
test1 = 6.6
|
||||
test2 = 2
|
||||
test3 = MyGame.Example.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<std::string> result =
|
||||
cpp17::StringifyFlatbufferValue(*monster, /*indent=*/" ");
|
||||
|
||||
TEST_ASSERT(result.has_value());
|
||||
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<pos_type, const Vec3*>);
|
||||
|
||||
using mana_type = Monster::Traits::FieldType<1>;
|
||||
static_assert(std::is_same_v<mana_type, int16_t>);
|
||||
|
||||
using name_type = Monster::Traits::FieldType<3>;
|
||||
static_assert(std::is_same_v<name_type, const flatbuffers::String*>);
|
||||
|
||||
/*******************************************************************************
|
||||
** Generic Create Function Test.
|
||||
*******************************************************************************/
|
||||
void CreateTableByTypeTest() {
|
||||
flatbuffers::FlatBufferBuilder builder;
|
||||
|
||||
@@ -62,7 +201,8 @@ void CreateTableByTypeTest() {
|
||||
}
|
||||
|
||||
void OptionalScalarsTest() {
|
||||
static_assert(std::is_same<flatbuffers::Optional<float>, std::optional<float>>::value);
|
||||
static_assert(
|
||||
std::is_same<flatbuffers::Optional<float>, std::optional<float>>::value);
|
||||
static_assert(std::is_same<flatbuffers::nullopt_t, std::nullopt_t>::value);
|
||||
|
||||
// test C++ nullable
|
||||
@@ -105,6 +245,7 @@ void OptionalScalarsTest() {
|
||||
int FlatBufferCpp17Tests() {
|
||||
CreateTableByTypeTest();
|
||||
OptionalScalarsTest();
|
||||
StringifyAnyFlatbuffersTypeTest();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user