Skip to content

Commit

Permalink
feat(C++): support underlying type for union
Browse files Browse the repository at this point in the history
  • Loading branch information
sssooonnnggg committed May 11, 2023
1 parent 0ce6957 commit 197a4c8
Show file tree
Hide file tree
Showing 8 changed files with 973 additions and 18 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ if(FLATBUFFERS_BUILD_TESTS)
compile_schema_for_test(tests/64bit/test_64bit.fbs "${FLATC_OPT_COMP};--bfbs-gen-embed")
compile_schema_for_test(tests/64bit/evolution/v1.fbs "${FLATC_OPT_COMP}")
compile_schema_for_test(tests/64bit/evolution/v2.fbs "${FLATC_OPT_COMP}")
compile_schema_for_test(tests/union_underlying_type_test.fbs "${FLATC_OPT_COMP}")

if(FLATBUFFERS_CODE_SANITIZE)
add_fsanitize_to_target(flattests ${FLATBUFFERS_CODE_SANITIZE})
Expand Down
14 changes: 10 additions & 4 deletions src/idl_gen_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,12 @@ class CppGenerator : public BaseGenerator {
if (type.enum_def) return WrapInNameSpace(*type.enum_def);
if (type.base_type == BASE_TYPE_BOOL) return "bool";
}
return StringOf(type.base_type);
// Get real underlying type for union type
auto base_type = type.base_type;
if (type.base_type == BASE_TYPE_UTYPE && type.enum_def != nullptr) {
base_type = type.enum_def->underlying_type.base_type;
}
return StringOf(base_type);
}

// Return a C++ pointer type, specialized to the actual struct/table types,
Expand Down Expand Up @@ -1048,7 +1053,7 @@ class CppGenerator : public BaseGenerator {

std::string UnionVectorVerifySignature(const EnumDef &enum_def) {
const std::string name = Name(enum_def);
const std::string &type = opts_.scoped_enums ? name : "uint8_t";
const std::string &type = opts_.scoped_enums ? name : GenTypeBasic(enum_def.underlying_type, false);
return "bool Verify" + name + "Vector" +
"(::flatbuffers::Verifier &verifier, " +
"const ::flatbuffers::Vector<::flatbuffers::Offset<void>> "
Expand Down Expand Up @@ -3496,12 +3501,13 @@ class CppGenerator : public BaseGenerator {
}
case BASE_TYPE_UTYPE: {
value = StripUnionType(value);
auto underlying_type = GenTypeBasic(vector_type, false);
const std::string &type = opts_.scoped_enums
? Name(*field.value.type.enum_def)
: "uint8_t";
: underlying_type;
auto enum_value = "__va->_" + value + "[i].type";
if (!opts_.scoped_enums)
enum_value = "static_cast<uint8_t>(" + enum_value + ")";
enum_value = "static_cast<" + underlying_type + ">(" + enum_value + ")";

code += "_fbb.CreateVector<" + type + ">(" + value +
".size(), [](size_t i, _VectorArgs *__va) { return " +
Expand Down
39 changes: 26 additions & 13 deletions src/idl_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,8 +947,12 @@ CheckedError Parser::ParseField(StructDef &struct_def) {
if (type.base_type == BASE_TYPE_UNION) {
// For union fields, add a second auto-generated field to hold the type,
// with a special suffix.
ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(),
type.enum_def->underlying_type, &typefield));

// To ensure compatibility with many codes that rely on the BASE_TYPE_UTYPE value to identify union type fields.
Type union_type(type.enum_def->underlying_type);
union_type.base_type = BASE_TYPE_UTYPE;
ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(),union_type, &typefield));

} else if (IsVector(type) && type.element == BASE_TYPE_UNION) {
advanced_features_ |= reflection::AdvancedUnionFeatures;
// Only cpp, js and ts supports the union vector feature so far.
Expand Down Expand Up @@ -2478,23 +2482,32 @@ CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest,
&GetPooledString(RelativeToRootPath(opts.project_root, filename));
}
enum_def->doc_comment = enum_comment;
if (!is_union && !opts.proto_mode) {
if (!opts.proto_mode) {
// Give specialized error message, since this type spec used to
// be optional in the first FlatBuffers release.
bool explicit_underlying_type = false;
if (!Is(':')) {
return Error(
"must specify the underlying integer type for this"
" enum (e.g. \': short\', which was the default).");
// Enum is forced to have an explicit underlying type in declaration.
if (!is_union) {
return Error(
"must specify the underlying integer type for this"
" enum (e.g. \': short\', which was the default).");
}
} else {
NEXT();
explicit_underlying_type = true;
}
// Specify the integer type underlying this enum.
ECHECK(ParseType(enum_def->underlying_type));
if (!IsInteger(enum_def->underlying_type.base_type) ||
IsBool(enum_def->underlying_type.base_type))
return Error("underlying enum type must be integral");
// Make this type refer back to the enum it was derived from.
enum_def->underlying_type.enum_def = enum_def;

if (explicit_underlying_type) {
// Specify the integer type underlying this enum.
ECHECK(ParseType(enum_def->underlying_type));
if (!IsInteger(enum_def->underlying_type.base_type) ||
IsBool(enum_def->underlying_type.base_type))
return Error("underlying " + std::string(is_union ? "union" : "enum") + "type must be integral");
// Make this type refer back to the enum it was derived from.
enum_def->underlying_type.enum_def = enum_def;
}

}
ECHECK(ParseMetaData(&enum_def->attributes));
const auto underlying_type = enum_def->underlying_type.base_type;
Expand Down
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ cc_test(
"namespace_test/namespace_test1_generated.h",
"namespace_test/namespace_test2_generated.h",
"native_inline_table_test_generated.h",
"union_underlying_type_test_generated.h",
"native_type_test_impl.cpp",
"native_type_test_impl.h",
"optional_scalars_generated.h",
Expand Down
37 changes: 37 additions & 0 deletions tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "proto_test.h"
#include "reflection_test.h"
#include "union_vector/union_vector_generated.h"
#include "union_underlying_type_test_generated.h"
#if !defined(_MSC_VER) || _MSC_VER >= 1700
# include "arrays_test_generated.h"
#endif
Expand Down Expand Up @@ -1540,6 +1541,41 @@ void DoNotRequireEofTest(const std::string &tests_data_path) {
}
#endif

void UnionUnderlyingTypeTest() {
using namespace UnionUnderlyingType;
TEST_ASSERT(sizeof(ABC) == sizeof(uint32_t));
TEST_ASSERT(ABC::ABC_A == 555);
TEST_ASSERT(ABC::ABC_B == 666);
TEST_ASSERT(ABC::ABC_C == 777);

DT buffer;
AT a;
a.a = 42;
BT b;
b.b = "foo";
CT c;
c.c = true;
buffer.test_union = ABCUnion();
buffer.test_union.Set(a);
buffer.test_vector_of_union.resize(3);
buffer.test_vector_of_union[0].Set(a);
buffer.test_vector_of_union[1].Set(b);
buffer.test_vector_of_union[2].Set(c);

flatbuffers::FlatBufferBuilder fbb;
auto offset = D::Pack(fbb, &buffer);
fbb.Finish(offset);

auto *root =
flatbuffers::GetRoot<D>(fbb.GetBufferPointer());
DT unpacked;
root->UnPackTo(&unpacked);

TEST_ASSERT(unpacked.test_union == buffer.test_union);
TEST_ASSERT(unpacked.test_vector_of_union == buffer.test_vector_of_union);

}

static void Offset64Tests() {
Offset64Test();
Offset64SerializedFirst();
Expand Down Expand Up @@ -1663,6 +1699,7 @@ int FlatBufferTests(const std::string &tests_data_path) {
FixedSizedStructArrayKeyInStructTest();
EmbeddedSchemaAccess();
Offset64Tests();
UnionUnderlyingTypeTest();
return 0;
}
} // namespace
Expand Down
2 changes: 1 addition & 1 deletion tests/ts/bazel_repository_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
BAZEL_BIN="$(rlocation bazel_linux_x86_64/file/bazel)"
readonly BAZEL_BIN

if [[ ! -e "${BAZEL_BIN}" ]]; then
if [[ ! -x "${BAZEL_BIN}" ]]; then
echo "Failed to find the bazel binary." >&2
exit 1
fi
Expand Down
17 changes: 17 additions & 0 deletions tests/union_underlying_type_test.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

namespace UnionUnderlyingType;

table A {
a: int;
}
table B {
b: string;
}
table C {
c: bool;
}
union ABC: int { A = 555, B = 666, C = 777}
table D {
test_union: ABC;
test_vector_of_union: [ABC];
}
Loading

0 comments on commit 197a4c8

Please sign in to comment.