feat(C++): Support underlying_type for union (#7954)

* feat(C++): support underlying type for union

* chore: add conform checks for underlying type changes
This commit is contained in:
sssooonnnggg
2023-05-15 12:22:38 +08:00
committed by GitHub
parent fe5e4c71c5
commit 1d3afb90c5
10 changed files with 1004 additions and 17 deletions

View File

@@ -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,
@@ -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>> "
@@ -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 " +

View File

@@ -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.
@@ -2482,23 +2486,39 @@ 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 {
// Union underlying type is only supported for cpp
if (is_union && !SupportsUnionUnderlyingType()) {
return Error(
"Underlying type for union is not yet supported in at least one of "
"the specified programming languages.");
}
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;
@@ -2697,6 +2717,10 @@ bool Parser::Supports64BitOffsets() const {
~(IDLOptions::kCpp | IDLOptions::kJson | IDLOptions::kBinary)) == 0;
}
bool Parser::SupportsUnionUnderlyingType() const {
return (opts.lang_to_generate & ~IDLOptions::kCpp) == 0;
}
Namespace *Parser::UniqueNamespace(Namespace *ns) {
for (auto it = namespaces_.begin(); it != namespaces_.end(); ++it) {
if (ns->components == (*it)->components) {
@@ -4428,6 +4452,10 @@ std::string Parser::ConformTo(const Parser &base) {
return "values differ for enum: " + enum_val.name;
}
}
// Check underlying type changes
if (enum_def_base->underlying_type.base_type != enum_def.underlying_type.base_type) {
return "underlying type differ for " + std::string(enum_def.is_union ? "union: " : "enum: ") + qualified_name;
}
}
return "";
}