diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 86cadd320..1e53088e4 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -1118,6 +1118,38 @@ class CppGenerator : public BaseGenerator { code_ += " return {{FIELD_VALUE}};"; code_ += " }"; + if (field.value.type.base_type == BASE_TYPE_UNION) { + auto u = field.value.type.enum_def; + + code_ += " template " + "const T *{{FIELD_NAME}}_as() const;"; + + for (auto u_it = u->vals.vec.begin(); + u_it != u->vals.vec.end(); ++u_it) { + if (!(*u_it)->struct_def) { + continue; + } + auto arg_struct_def = (*u_it)->struct_def; + auto full_struct_name = WrapInNameSpace(*arg_struct_def); + + // @TODO: Mby make this decisions more universal? How? + code_.SetValue("U_GET_TYPE", field.name + UnionTypeFieldSuffix()); + code_.SetValue("U_ELEMENT_TYPE", WrapInNameSpace( + u->defined_namespace, GetEnumValUse(*u, **u_it))); + code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *"); + code_.SetValue("U_ELEMENT_NAME", full_struct_name); + code_.SetValue("U_FIELD_NAME", + field.name + "_as_" + (*u_it)->name); + + // `const Type *union_name_asType() const` accessor. + code_ += " {{U_FIELD_TYPE}}{{U_FIELD_NAME}}() const {"; + code_ += " return ({{U_GET_TYPE}}() == {{U_ELEMENT_TYPE}})? " + "static_cast<{{U_FIELD_TYPE}}>({{FIELD_NAME}}()) " + ": nullptr;"; + code_ += " }"; + } + } + if (parser_.opts.mutable_buffer) { if (is_scalar) { code_.SetValue("OFFSET_NAME", offset_str); @@ -1222,6 +1254,44 @@ class CppGenerator : public BaseGenerator { code_ += "};"; // End of table. code_ += ""; + // Explicit specializations for union accessors + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated || + field.value.type.base_type != BASE_TYPE_UNION) { + continue; + } + + auto u = field.value.type.enum_def; + code_.SetValue("FIELD_NAME", field.name); + + for (auto u_it = u->vals.vec.begin(); + u_it != u->vals.vec.end(); ++u_it) { + if (!(*u_it)->struct_def) { + continue; + } + + auto arg_struct_def = (*u_it)->struct_def; + auto full_struct_name = WrapInNameSpace(*arg_struct_def); + + code_.SetValue("U_ELEMENT_TYPE", WrapInNameSpace( + u->defined_namespace, GetEnumValUse(*u, **u_it))); + code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *"); + code_.SetValue("U_ELEMENT_NAME", full_struct_name); + code_.SetValue("U_FIELD_NAME", + field.name + "_as_" + (*u_it)->name); + + // `template<> const T *union_name_as() const` accessor. + code_ += "template<> " + "inline {{U_FIELD_TYPE}}{{STRUCT_NAME}}::{{FIELD_NAME}}_as" + "<{{U_ELEMENT_NAME}}>() const {"; + code_ += " return {{U_FIELD_NAME}}();"; + code_ += "}"; + code_ += ""; + } + } + GenBuilders(struct_def); if (parser_.opts.generate_object_based_api) { diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs index 21f3c0008..8df32d9d5 100644 Binary files a/tests/monster_test.bfbs and b/tests/monster_test.bfbs differ diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index ee9453650..ce952e1a7 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -582,6 +582,16 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const void *test() const { return GetPointer(VT_TEST); } + template const T *test_as() const; + const Monster *test_as_Monster() const { + return (test_type() == Any_Monster)? static_cast(test()) : nullptr; + } + const TestSimpleTableWithEnum *test_as_TestSimpleTableWithEnum() const { + return (test_type() == Any_TestSimpleTableWithEnum)? static_cast(test()) : nullptr; + } + const MyGame::Example2::Monster *test_as_MyGame_Example2_Monster() const { + return (test_type() == Any_MyGame_Example2_Monster)? static_cast(test()) : nullptr; + } void *mutable_test() { return GetPointer(VT_TEST); } @@ -762,6 +772,18 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); }; +template<> inline const Monster *Monster::test_as() const { + return test_as_Monster(); +} + +template<> inline const TestSimpleTableWithEnum *Monster::test_as() const { + return test_as_TestSimpleTableWithEnum(); +} + +template<> inline const MyGame::Example2::Monster *Monster::test_as() const { + return test_as_MyGame_Example2_Monster(); +} + struct MonsterBuilder { flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_;