Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
aa39ddf
[C++17] Add compile-time reflection for fields.
no-more-secrets Dec 10, 2020
a1f366c
Fix int-conversion warning on MSVC.
no-more-secrets Dec 10, 2020
c662260
Try to fix std::to_string ambiguity on MSVC.
no-more-secrets Dec 10, 2020
a895c01
Merge branch 'master' into cpp17-field-traits
no-more-secrets Dec 11, 2020
2692175
Merge branch 'master' into cpp17-field-traits
no-more-secrets Dec 11, 2020
4b458c2
Merge branch 'cpp17-field-traits' of ssh://github.com/dpacbach/flatbu…
no-more-secrets Dec 11, 2020
94790e2
Fix clang-format diffs.
no-more-secrets Dec 12, 2020
3f06697
Fix more clang-format diffs.
no-more-secrets Dec 12, 2020
7575a2c
Fix last clang-format diff.
no-more-secrets Dec 13, 2020
181220d
Enable C++17 build/test for VC 19 platform in CI.
no-more-secrets Dec 13, 2020
5a42f2d
Forgot to add value to cmake command line variable.
no-more-secrets Dec 13, 2020
9f8ff70
Various fixes/changes in response to @vglavnyy's feedback.
no-more-secrets Dec 13, 2020
b5e7679
Merge branch 'master' into cpp17-field-traits
no-more-secrets Jan 8, 2021
44302e6
Replace "fields pack" with index-based getters.
no-more-secrets Jan 8, 2021
36ee3b0
Fix MSVC error.
no-more-secrets Jan 8, 2021
09e46b2
Fix clang-format diffs.
no-more-secrets Jan 8, 2021
68ad486
getter_for method returns result instead of address-of-getter.
no-more-secrets Jan 9, 2021
1859546
Merge branch 'master' into cpp17-field-traits
no-more-secrets Feb 20, 2021
505a315
Merge branch 'cpp17-field-traits' of ssh://github.com/dpacbach/flatbu…
no-more-secrets Feb 20, 2021
dcdb4e7
Next round of reviewer suggestions.
no-more-secrets Feb 20, 2021
d4ed8a4
Use type instead of hardcoded struct name.
no-more-secrets Feb 20, 2021
191029a
Fix clang-format diff.
no-more-secrets Feb 20, 2021
db4fa27
Add test for FieldType since it is not used in the stringify test.
no-more-secrets Feb 21, 2021
17e75b5
Add fields_number field to Traits struct.
no-more-secrets Feb 28, 2021
883390d
Merge branch 'master' of https://github.com/google/flatbuffers into c…
no-more-secrets Mar 5, 2021
4bc3ca9
Add --cpp-static-reflection flag and put those features behind it.
no-more-secrets Mar 5, 2021
8d7b9dc
Fix clang-format diffs.
no-more-secrets Mar 5, 2021
5102108
Remove <tuple> include.
no-more-secrets Mar 5, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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=ON .
- name: build
run: msbuild.exe FlatBuffers.sln /p:Configuration=Release /p:Platform=x64
- name: test
Expand Down
2 changes: 2 additions & 0 deletions include/flatbuffers/idl.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ struct IDLOptions {
bool cs_gen_json_serializer;
std::vector<std::string> cpp_includes;
std::string cpp_std;
bool cpp_static_reflection;
std::string proto_namespace_suffix;
std::string filename_suffix;
std::string filename_extension;
Expand Down Expand Up @@ -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),
Expand Down
5 changes: 5 additions & 0 deletions src/flatc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 ||
Expand Down
157 changes: 149 additions & 8 deletions src/idl_gen_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2041,6 +2041,135 @@ 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}}\\";
}

void GenFieldTypeHelper(const StructDef &struct_def) {
if (struct_def.fields.vec.empty()) { return; }
code_ += " template<size_t Index>";
code_ += " using FieldType = \\";
code_ += "decltype(std::declval<type>().get_field<Index>());";
}

void GenIndexBasedFieldGetter(const StructDef &struct_def) {
if (struct_def.fields.vec.empty()) { return; }
code_ += " template<size_t Index>";
code_ += " auto get_field() const {";

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;
if (field.deprecated) {
// Deprecated fields won't be accessible.
continue;
}
code_.SetValue("FIELD_NAME", Name(field));
code_.SetValue("FIELD_INDEX",
std::to_string(static_cast<long long>(index++)));
if (need_else) {
code_ += " else \\";
} else {
code_ += " \\";
}
need_else = true;
code_ += "if constexpr (Index == {{FIELD_INDEX}}) \\";
code_ += "return {{FIELD_NAME}}();";
}
code_ += " else static_assert(Index != Index, \"Invalid Field Index\");";
code_ += " }";
}

// Sample for Vec3:
//
// static constexpr std::array<const char *, 3> field_names = {
// "x",
// "y",
// "z"
// };
//
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; });
code_ += " static constexpr std::array<\\";
code_.SetValue(
"FIELD_COUNT",
std::to_string(static_cast<long long>(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 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<long long>(non_deprecated_field_count)));
code_ += " static constexpr size_t fields_number = {{FIELD_COUNT}};";
}

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);
GenFieldsNumber(struct_def);
code_ += "};";
code_ += "";
}

void GenTableFieldSetter(const FieldDef &field) {
const auto &type = field.value.type;
const bool is_scalar = IsScalar(type.base_type);
Expand Down Expand Up @@ -2098,7 +2227,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() {";
Expand Down Expand Up @@ -2181,6 +2310,8 @@ class CppGenerator : public BaseGenerator {
if (field.key) { GenKeyFieldMethods(field); }
}

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.
code_ += " bool Verify(flatbuffers::Verifier &verifier) const {";
Expand Down Expand Up @@ -2388,13 +2519,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) {
code_ += "struct {{STRUCT_NAME}}::Traits {";
code_ += " using type = {{STRUCT_NAME}};";
code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};";
code_ += "};";
code_ += "";
}
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) {
Expand Down Expand Up @@ -3168,6 +3293,8 @@ class CppGenerator : public BaseGenerator {
code_ += "";
code_ += " public:";

if (opts_.cpp_static_reflection) { code_ += " struct Traits;"; }

// Make TypeTable accessible via the generated struct.
if (opts_.mini_reflect != IDLOptions::kNone) {
code_ +=
Expand Down Expand Up @@ -3253,12 +3380,19 @@ class CppGenerator : public BaseGenerator {
}
code_.SetValue("NATIVE_NAME", Name(struct_def));
GenOperatorNewDelete(struct_def);

if (opts_.cpp_static_reflection) { GenIndexBasedFieldGetter(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 table type. This allows querying var-
// ious compile-time traits of the table.
if (opts_.cpp_static_reflection) { GenTraitsStruct(struct_def); }
}

// Set up the correct namespace. Only open a namespace if the existing one is
Expand Down Expand Up @@ -3331,6 +3465,13 @@ 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();
}
Expand Down
Loading