Added support for structs and strings in unions.

(C++ only for now).
Also fixed vector of union support in the object API.

Bug: 36902939
Change-Id: I935f4cc2c303a4728e26c7916a8ec0adcd6f84cb
Tested: on Linux.
parent 1fc12e0e
...@@ -411,4 +411,22 @@ manually wrap it in synchronisation primites. There's no automatic way to ...@@ -411,4 +411,22 @@ manually wrap it in synchronisation primites. There's no automatic way to
accomplish this, by design, as we feel multithreaded construction accomplish this, by design, as we feel multithreaded construction
of a single buffer will be rare, and synchronisation overhead would be costly. of a single buffer will be rare, and synchronisation overhead would be costly.
## Advanced union features
The C++ implementation currently supports vectors of unions (i.e. you can
declare a field as `[T]` where `T` is a union type instead of a table type). It
also supports structs and strings in unions, besides tables.
For an example of these features, see `tests/union_vector`, and
`UnionVectorTest` in `test.cpp`.
Since these features haven't been ported to other languages yet, if you
choose to use them, you won't be able to use these buffers in other languages
(`flatc` will refuse to compile a schema that uses these features).
These features reduce the amount of "table wrapping" that was previously
needed to use unions.
To use scalars, simply wrap them in a struct.
<br> <br>
...@@ -360,6 +360,8 @@ private: ...@@ -360,6 +360,8 @@ private:
const uint8_t *data_; const uint8_t *data_;
}; };
struct String;
// This is used as a helper type for accessing vectors. // This is used as a helper type for accessing vectors.
// Vector::data() assumes the vector elements start after the length field. // Vector::data() assumes the vector elements start after the length field.
template<typename T> class Vector { template<typename T> class Vector {
...@@ -391,6 +393,18 @@ public: ...@@ -391,6 +393,18 @@ public:
return static_cast<E>(Get(i)); return static_cast<E>(Get(i));
} }
// If this a vector of unions, this does the cast for you. There's no check
// to make sure this is the right type!
template<typename U> const U *GetAs(uoffset_t i) const {
return reinterpret_cast<const U *>(Get(i));
}
// If this a vector of unions, this does the cast for you. There's no check
// to make sure this is actually a string!
const String *GetAsString(uoffset_t i) const {
return reinterpret_cast<const String *>(Get(i));
}
const void *GetStructFromOffset(size_t o) const { const void *GetStructFromOffset(size_t o) const {
return reinterpret_cast<const void *>(Data() + o); return reinterpret_cast<const void *>(Data() + o);
} }
...@@ -1327,6 +1341,13 @@ FLATBUFFERS_FINAL_CLASS ...@@ -1327,6 +1341,13 @@ FLATBUFFERS_FINAL_CLASS
reinterpret_cast<uint8_t **>(buf)); reinterpret_cast<uint8_t **>(buf));
} }
/// @brief Write a struct by itself, typically to be part of a union.
template<typename T> Offset<const T *> CreateStruct(const T &structobj) {
Align(AlignOf<T>());
buf_.push_small(structobj);
return Offset<const T *>(GetSize());
}
/// @brief The length of a FlatBuffer file header. /// @brief The length of a FlatBuffer file header.
static const size_t kFileIdentifierLength = 4; static const size_t kFileIdentifierLength = 4;
......
...@@ -283,18 +283,18 @@ inline size_t InlineAlignment(const Type &type) { ...@@ -283,18 +283,18 @@ inline size_t InlineAlignment(const Type &type) {
struct EnumVal { struct EnumVal {
EnumVal(const std::string &_name, int64_t _val) EnumVal(const std::string &_name, int64_t _val)
: name(_name), value(_val), struct_def(nullptr) {} : name(_name), value(_val) {}
Offset<reflection::EnumVal> Serialize(FlatBufferBuilder *builder) const; Offset<reflection::EnumVal> Serialize(FlatBufferBuilder *builder) const;
std::string name; std::string name;
std::vector<std::string> doc_comment; std::vector<std::string> doc_comment;
int64_t value; int64_t value;
StructDef *struct_def; // only set if this is a union Type union_type;
}; };
struct EnumDef : public Definition { struct EnumDef : public Definition {
EnumDef() : is_union(false) {} EnumDef() : is_union(false), uses_type_aliases(false) {}
EnumVal *ReverseLookup(int enum_idx, bool skip_union_default = true) { EnumVal *ReverseLookup(int enum_idx, bool skip_union_default = true) {
for (auto it = vals.vec.begin() + static_cast<int>(is_union && for (auto it = vals.vec.begin() + static_cast<int>(is_union &&
...@@ -312,6 +312,7 @@ struct EnumDef : public Definition { ...@@ -312,6 +312,7 @@ struct EnumDef : public Definition {
SymbolTable<EnumVal> vals; SymbolTable<EnumVal> vals;
bool is_union; bool is_union;
bool uses_type_aliases;
Type underlying_type; Type underlying_type;
}; };
...@@ -548,6 +549,7 @@ private: ...@@ -548,6 +549,7 @@ private:
const std::string &name, const Type &type, const std::string &name, const Type &type,
FieldDef **dest); FieldDef **dest);
FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def); FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def);
FLATBUFFERS_CHECKED_ERROR ParseString(Value &val);
FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field, FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field,
size_t parent_fieldn, size_t parent_fieldn,
const StructDef *parent_struct_def); const StructDef *parent_struct_def);
......
...@@ -204,7 +204,8 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { ...@@ -204,7 +204,8 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum { enum {
VT_NAME = 4, VT_NAME = 4,
VT_VALUE = 6, VT_VALUE = 6,
VT_OBJECT = 8 VT_OBJECT = 8,
VT_UNION_TYPE = 10
}; };
const flatbuffers::String *name() const { const flatbuffers::String *name() const {
return GetPointer<const flatbuffers::String *>(VT_NAME); return GetPointer<const flatbuffers::String *>(VT_NAME);
...@@ -228,6 +229,9 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { ...@@ -228,6 +229,9 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
const Object *object() const { const Object *object() const {
return GetPointer<const Object *>(VT_OBJECT); return GetPointer<const Object *>(VT_OBJECT);
} }
const Type *union_type() const {
return GetPointer<const Type *>(VT_UNION_TYPE);
}
bool Verify(flatbuffers::Verifier &verifier) const { bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) && return VerifyTableStart(verifier) &&
VerifyFieldRequired<flatbuffers::uoffset_t>(verifier, VT_NAME) && VerifyFieldRequired<flatbuffers::uoffset_t>(verifier, VT_NAME) &&
...@@ -235,6 +239,8 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { ...@@ -235,6 +239,8 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
VerifyField<int64_t>(verifier, VT_VALUE) && VerifyField<int64_t>(verifier, VT_VALUE) &&
VerifyField<flatbuffers::uoffset_t>(verifier, VT_OBJECT) && VerifyField<flatbuffers::uoffset_t>(verifier, VT_OBJECT) &&
verifier.VerifyTable(object()) && verifier.VerifyTable(object()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, VT_UNION_TYPE) &&
verifier.VerifyTable(union_type()) &&
verifier.EndTable(); verifier.EndTable();
} }
}; };
...@@ -251,13 +257,16 @@ struct EnumValBuilder { ...@@ -251,13 +257,16 @@ struct EnumValBuilder {
void add_object(flatbuffers::Offset<Object> object) { void add_object(flatbuffers::Offset<Object> object) {
fbb_.AddOffset(EnumVal::VT_OBJECT, object); fbb_.AddOffset(EnumVal::VT_OBJECT, object);
} }
void add_union_type(flatbuffers::Offset<Type> union_type) {
fbb_.AddOffset(EnumVal::VT_UNION_TYPE, union_type);
}
EnumValBuilder(flatbuffers::FlatBufferBuilder &_fbb) EnumValBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) { : fbb_(_fbb) {
start_ = fbb_.StartTable(); start_ = fbb_.StartTable();
} }
EnumValBuilder &operator=(const EnumValBuilder &); EnumValBuilder &operator=(const EnumValBuilder &);
flatbuffers::Offset<EnumVal> Finish() { flatbuffers::Offset<EnumVal> Finish() {
const auto end = fbb_.EndTable(start_, 3); const auto end = fbb_.EndTable(start_, 4);
auto o = flatbuffers::Offset<EnumVal>(end); auto o = flatbuffers::Offset<EnumVal>(end);
fbb_.Required(o, EnumVal::VT_NAME); fbb_.Required(o, EnumVal::VT_NAME);
return o; return o;
...@@ -268,9 +277,11 @@ inline flatbuffers::Offset<EnumVal> CreateEnumVal( ...@@ -268,9 +277,11 @@ inline flatbuffers::Offset<EnumVal> CreateEnumVal(
flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> name = 0, flatbuffers::Offset<flatbuffers::String> name = 0,
int64_t value = 0, int64_t value = 0,
flatbuffers::Offset<Object> object = 0) { flatbuffers::Offset<Object> object = 0,
flatbuffers::Offset<Type> union_type = 0) {
EnumValBuilder builder_(_fbb); EnumValBuilder builder_(_fbb);
builder_.add_value(value); builder_.add_value(value);
builder_.add_union_type(union_type);
builder_.add_object(object); builder_.add_object(object);
builder_.add_name(name); builder_.add_name(name);
return builder_.Finish(); return builder_.Finish();
...@@ -280,12 +291,14 @@ inline flatbuffers::Offset<EnumVal> CreateEnumValDirect( ...@@ -280,12 +291,14 @@ inline flatbuffers::Offset<EnumVal> CreateEnumValDirect(
flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::FlatBufferBuilder &_fbb,
const char *name = nullptr, const char *name = nullptr,
int64_t value = 0, int64_t value = 0,
flatbuffers::Offset<Object> object = 0) { flatbuffers::Offset<Object> object = 0,
flatbuffers::Offset<Type> union_type = 0) {
return reflection::CreateEnumVal( return reflection::CreateEnumVal(
_fbb, _fbb,
name ? _fbb.CreateString(name) : 0, name ? _fbb.CreateString(name) : 0,
value, value,
object); object,
union_type);
} }
struct Enum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct Enum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
......
...@@ -42,7 +42,8 @@ table KeyValue { ...@@ -42,7 +42,8 @@ table KeyValue {
table EnumVal { table EnumVal {
name:string (required); name:string (required);
value:long (key); value:long (key);
object:Object; // Only if part of a union. object:Object; // Will be deprecated in favor of union_type in the future.
union_type:Type;
} }
table Enum { table Enum {
......
...@@ -71,35 +71,35 @@ template<> struct EquipmentTraits<Weapon> { ...@@ -71,35 +71,35 @@ template<> struct EquipmentTraits<Weapon> {
struct EquipmentUnion { struct EquipmentUnion {
Equipment type; Equipment type;
flatbuffers::NativeTable *table; void *value;
EquipmentUnion() : type(Equipment_NONE), table(nullptr) {} EquipmentUnion() : type(Equipment_NONE), value(nullptr) {}
EquipmentUnion(EquipmentUnion&& u) FLATBUFFERS_NOEXCEPT : EquipmentUnion(EquipmentUnion&& u) FLATBUFFERS_NOEXCEPT :
type(Equipment_NONE), table(nullptr) type(Equipment_NONE), value(nullptr)
{ std::swap(type, u.type); std::swap(table, u.table); } { std::swap(type, u.type); std::swap(value, u.value); }
EquipmentUnion(const EquipmentUnion &); EquipmentUnion(const EquipmentUnion &);
EquipmentUnion &operator=(const EquipmentUnion &); EquipmentUnion &operator=(const EquipmentUnion &);
EquipmentUnion &operator=(EquipmentUnion &&u) FLATBUFFERS_NOEXCEPT EquipmentUnion &operator=(EquipmentUnion &&u) FLATBUFFERS_NOEXCEPT
{ std::swap(type, u.type); std::swap(table, u.table); return *this; } { std::swap(type, u.type); std::swap(value, u.value); return *this; }
~EquipmentUnion() { Reset(); } ~EquipmentUnion() { Reset(); }
void Reset(); void Reset();
template <typename T> template <typename T>
void Set(T&& value) { void Set(T&& val) {
Reset(); Reset();
type = EquipmentTraits<typename T::TableType>::enum_value; type = EquipmentTraits<typename T::TableType>::enum_value;
if (type != Equipment_NONE) { if (type != Equipment_NONE) {
table = new T(std::forward<T>(value)); value = new T(std::forward<T>(val));
} }
} }
static flatbuffers::NativeTable *UnPack(const void *obj, Equipment type, const flatbuffers::resolver_function_t *resolver); static void *UnPack(const void *obj, Equipment type, const flatbuffers::resolver_function_t *resolver);
flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher = nullptr) const; flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher = nullptr) const;
WeaponT *AsWeapon() { WeaponT *AsWeapon() {
return type == Equipment_Weapon ? return type == Equipment_Weapon ?
reinterpret_cast<WeaponT *>(table) : nullptr; reinterpret_cast<WeaponT *>(value) : nullptr;
} }
}; };
...@@ -228,7 +228,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { ...@@ -228,7 +228,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
} }
template<typename T> const T *equipped_as() const; template<typename T> const T *equipped_as() const;
const Weapon *equipped_as_Weapon() const { const Weapon *equipped_as_Weapon() const {
return (equipped_type() == Equipment_Weapon)? static_cast<const Weapon *>(equipped()) : nullptr; return equipped_type() == Equipment_Weapon ? static_cast<const Weapon *>(equipped()) : nullptr;
} }
void *mutable_equipped() { void *mutable_equipped() {
return GetPointer<void *>(VT_EQUIPPED); return GetPointer<void *>(VT_EQUIPPED);
...@@ -451,7 +451,7 @@ inline void Monster::UnPackTo(MonsterT *_o, const flatbuffers::resolver_function ...@@ -451,7 +451,7 @@ inline void Monster::UnPackTo(MonsterT *_o, const flatbuffers::resolver_function
{ auto _e = color(); _o->color = _e; }; { auto _e = color(); _o->color = _e; };
{ auto _e = weapons(); if (_e) { _o->weapons.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->weapons[_i] = std::unique_ptr<WeaponT>(_e->Get(_i)->UnPack(_resolver)); } } }; { auto _e = weapons(); if (_e) { _o->weapons.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->weapons[_i] = std::unique_ptr<WeaponT>(_e->Get(_i)->UnPack(_resolver)); } } };
{ auto _e = equipped_type(); _o->equipped.type = _e; }; { auto _e = equipped_type(); _o->equipped.type = _e; };
{ auto _e = equipped(); if (_e) _o->equipped.table = EquipmentUnion::UnPack(_e, equipped_type(),_resolver); }; { auto _e = equipped(); if (_e) _o->equipped.value = EquipmentUnion::UnPack(_e, equipped_type(), _resolver); };
} }
inline flatbuffers::Offset<Monster> Monster::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT* _o, const flatbuffers::rehasher_function_t *_rehasher) { inline flatbuffers::Offset<Monster> Monster::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
...@@ -535,7 +535,7 @@ inline bool VerifyEquipmentVector(flatbuffers::Verifier &verifier, const flatbuf ...@@ -535,7 +535,7 @@ inline bool VerifyEquipmentVector(flatbuffers::Verifier &verifier, const flatbuf
return true; return true;
} }
inline flatbuffers::NativeTable *EquipmentUnion::UnPack(const void *obj, Equipment type, const flatbuffers::resolver_function_t *resolver) { inline void *EquipmentUnion::UnPack(const void *obj, Equipment type, const flatbuffers::resolver_function_t *resolver) {
switch (type) { switch (type) {
case Equipment_Weapon: { case Equipment_Weapon: {
auto ptr = reinterpret_cast<const Weapon *>(obj); auto ptr = reinterpret_cast<const Weapon *>(obj);
...@@ -548,7 +548,7 @@ inline flatbuffers::NativeTable *EquipmentUnion::UnPack(const void *obj, Equipme ...@@ -548,7 +548,7 @@ inline flatbuffers::NativeTable *EquipmentUnion::UnPack(const void *obj, Equipme
inline flatbuffers::Offset<void> EquipmentUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher) const { inline flatbuffers::Offset<void> EquipmentUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher) const {
switch (type) { switch (type) {
case Equipment_Weapon: { case Equipment_Weapon: {
auto ptr = reinterpret_cast<const WeaponT *>(table); auto ptr = reinterpret_cast<const WeaponT *>(value);
return CreateWeapon(_fbb, ptr, _rehasher).Union(); return CreateWeapon(_fbb, ptr, _rehasher).Union();
} }
default: return 0; default: return 0;
...@@ -558,13 +558,13 @@ inline flatbuffers::Offset<void> EquipmentUnion::Pack(flatbuffers::FlatBufferBui ...@@ -558,13 +558,13 @@ inline flatbuffers::Offset<void> EquipmentUnion::Pack(flatbuffers::FlatBufferBui
inline void EquipmentUnion::Reset() { inline void EquipmentUnion::Reset() {
switch (type) { switch (type) {
case Equipment_Weapon: { case Equipment_Weapon: {
auto ptr = reinterpret_cast<WeaponT *>(table); auto ptr = reinterpret_cast<WeaponT *>(value);
delete ptr; delete ptr;
break; break;
} }
default: break; default: break;
} }
table = nullptr; value = nullptr;
type = Equipment_NONE; type = Equipment_NONE;
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -49,7 +49,7 @@ void OutputIdentifier(const std::string &name, const IDLOptions &opts, ...@@ -49,7 +49,7 @@ void OutputIdentifier(const std::string &name, const IDLOptions &opts,
// for a single FlatBuffer value into JSON format. // for a single FlatBuffer value into JSON format.
// The general case for scalars: // The general case for scalars:
template<typename T> bool Print(T val, Type type, int /*indent*/, template<typename T> bool Print(T val, Type type, int /*indent*/,
StructDef * /*union_sd*/, Type */*union_type*/,
const IDLOptions &opts, const IDLOptions &opts,
std::string *_text) { std::string *_text) {
std::string &text = *_text; std::string &text = *_text;
...@@ -169,22 +169,16 @@ static bool EscapeString(const String &s, std::string *_text, const IDLOptions& ...@@ -169,22 +169,16 @@ static bool EscapeString(const String &s, std::string *_text, const IDLOptions&
// Specialization of Print above for pointer types. // Specialization of Print above for pointer types.
template<> bool Print<const void *>(const void *val, template<> bool Print<const void *>(const void *val,
Type type, int indent, Type type, int indent,
StructDef *union_sd, Type *union_type,
const IDLOptions &opts, const IDLOptions &opts,
std::string *_text) { std::string *_text) {
switch (type.base_type) { switch (type.base_type) {
case BASE_TYPE_UNION: case BASE_TYPE_UNION:
// If this assert hits, you have an corrupt buffer, a union type field // If this assert hits, you have an corrupt buffer, a union type field
// was not present or was out of range. // was not present or was out of range.
assert(union_sd); assert(union_type);
if (!GenStruct(*union_sd, return Print<const void *>(val, *union_type, indent, nullptr, opts,
reinterpret_cast<const Table *>(val), _text);
indent,
opts,
_text)) {
return false;
}
break;
case BASE_TYPE_STRUCT: case BASE_TYPE_STRUCT:
if (!GenStruct(*type.struct_def, if (!GenStruct(*type.struct_def,
reinterpret_cast<const Table *>(val), reinterpret_cast<const Table *>(val),
...@@ -236,7 +230,7 @@ template<typename T> static bool GenField(const FieldDef &fd, ...@@ -236,7 +230,7 @@ template<typename T> static bool GenField(const FieldDef &fd,
// Generate text for non-scalar field. // Generate text for non-scalar field.
static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed, static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
int indent, StructDef *union_sd, int indent, Type *union_type,
const IDLOptions &opts, std::string *_text) { const IDLOptions &opts, std::string *_text) {
const void *val = nullptr; const void *val = nullptr;
if (fixed) { if (fixed) {
...@@ -249,7 +243,7 @@ static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed, ...@@ -249,7 +243,7 @@ static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
? table->GetStruct<const void *>(fd.value.offset) ? table->GetStruct<const void *>(fd.value.offset)
: table->GetPointer<const void *>(fd.value.offset); : table->GetPointer<const void *>(fd.value.offset);
} }
return Print(val, fd.value.type, indent, union_sd, opts, _text); return Print(val, fd.value.type, indent, union_type, opts, _text);
} }
// Generate text for a struct or table, values separated by commas, indented, // Generate text for a struct or table, values separated by commas, indented,
...@@ -260,7 +254,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table, ...@@ -260,7 +254,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table,
std::string &text = *_text; std::string &text = *_text;
text += "{"; text += "{";
int fieldout = 0; int fieldout = 0;
StructDef *union_sd = nullptr; Type *union_type = nullptr;
for (auto it = struct_def.fields.vec.begin(); for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); it != struct_def.fields.vec.end();
++it) { ++it) {
...@@ -296,7 +290,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table, ...@@ -296,7 +290,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table,
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD) FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD #undef FLATBUFFERS_TD
if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts), if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
union_sd, opts, _text)) { union_type, opts, _text)) {
return false; return false;
} }
break; break;
...@@ -305,7 +299,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table, ...@@ -305,7 +299,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table,
auto enum_val = fd.value.type.enum_def->ReverseLookup( auto enum_val = fd.value.type.enum_def->ReverseLookup(
table->GetField<uint8_t>(fd.value.offset, 0)); table->GetField<uint8_t>(fd.value.offset, 0));
assert(enum_val); assert(enum_val);
union_sd = enum_val->struct_def; union_type = &enum_val->union_type;
} }
} }
else else
......
...@@ -735,6 +735,13 @@ CheckedError Parser::ParseField(StructDef &struct_def) { ...@@ -735,6 +735,13 @@ CheckedError Parser::ParseField(StructDef &struct_def) {
return NoError(); return NoError();
} }
CheckedError Parser::ParseString(Value &val) {
auto s = attribute_;
EXPECT(kTokenStringConstant);
val.constant = NumToString(builder_.CreateString(s).o);
return NoError();
}
CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
size_t parent_fieldn, size_t parent_fieldn,
const StructDef *parent_struct_def) { const StructDef *parent_struct_def) {
...@@ -784,16 +791,27 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, ...@@ -784,16 +791,27 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
ECHECK(atot(constant.c_str(), *this, &enum_idx)); ECHECK(atot(constant.c_str(), *this, &enum_idx));
auto enum_val = val.type.enum_def->ReverseLookup(enum_idx); auto enum_val = val.type.enum_def->ReverseLookup(enum_idx);
if (!enum_val) return Error("illegal type id for: " + field->name); if (!enum_val) return Error("illegal type id for: " + field->name);
ECHECK(ParseTable(*enum_val->struct_def, &val.constant, nullptr)); if (enum_val->union_type.base_type == BASE_TYPE_STRUCT) {
ECHECK(ParseTable(*enum_val->union_type.struct_def, &val.constant,
nullptr));
if (enum_val->union_type.struct_def->fixed) {
// All BASE_TYPE_UNION values are offsets, so turn this into one.
SerializeStruct(*enum_val->union_type.struct_def, val);
builder_.ClearOffsets();
val.constant = NumToString(builder_.GetSize());
}
} else if (enum_val->union_type.base_type == BASE_TYPE_STRING) {
ECHECK(ParseString(val));
} else {
assert(false);
}
break; break;
} }
case BASE_TYPE_STRUCT: case BASE_TYPE_STRUCT:
ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr)); ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr));
break; break;
case BASE_TYPE_STRING: { case BASE_TYPE_STRING: {
auto s = attribute_; ECHECK(ParseString(val));
EXPECT(kTokenStringConstant);
val.constant = NumToString(builder_.CreateString(s).o);
break; break;
} }
case BASE_TYPE_VECTOR: { case BASE_TYPE_VECTOR: {
...@@ -1290,7 +1308,16 @@ CheckedError Parser::ParseEnum(bool is_union, EnumDef **dest) { ...@@ -1290,7 +1308,16 @@ CheckedError Parser::ParseEnum(bool is_union, EnumDef **dest) {
return Error("enum value already exists: " + value_name); return Error("enum value already exists: " + value_name);
ev.doc_comment = value_comment; ev.doc_comment = value_comment;
if (is_union) { if (is_union) {
ev.struct_def = LookupCreateStruct(full_name); if (Is(':')) {
NEXT();
ECHECK(ParseType(ev.union_type));
if (ev.union_type.base_type != BASE_TYPE_STRUCT &&
ev.union_type.base_type != BASE_TYPE_STRING)
return Error("union value type may only be table/struct/string");
enum_def.uses_type_aliases = true;
} else {
ev.union_type = Type(BASE_TYPE_STRUCT, LookupCreateStruct(full_name));
}
} }
if (Is('=')) { if (Is('=')) {
NEXT(); NEXT();
...@@ -1994,6 +2021,8 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, ...@@ -1994,6 +2021,8 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths,
return Error("type referenced but not defined: " + (*it)->name); return Error("type referenced but not defined: " + (*it)->name);
} }
} }
// This check has to happen here and not earlier, because only now do we
// know for sure what the type of these are.
for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) { for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
auto &enum_def = **it; auto &enum_def = **it;
if (enum_def.is_union) { if (enum_def.is_union) {
...@@ -2001,8 +2030,11 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths, ...@@ -2001,8 +2030,11 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths,
val_it != enum_def.vals.vec.end(); val_it != enum_def.vals.vec.end();
++val_it) { ++val_it) {
auto &val = **val_it; auto &val = **val_it;
if (val.struct_def && val.struct_def->fixed) if (opts.lang_to_generate != IDLOptions::kCpp &&
return Error("only tables can be union elements: " + val.name); val.union_type.struct_def && val.union_type.struct_def->fixed)
return Error(
"only tables can be union elements in the generated language: "
+ val.name);
} }
} }
} }
...@@ -2145,9 +2177,11 @@ Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder *builder) const ...@@ -2145,9 +2177,11 @@ Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder *builder) const
return reflection::CreateEnumVal(*builder, return reflection::CreateEnumVal(*builder,
builder->CreateString(name), builder->CreateString(name),
value, value,
struct_def union_type.struct_def
? struct_def->serialized_location ? union_type.struct_def->
: 0); serialized_location
: 0,
union_type.Serialize(builder));
} }
Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const { Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
../flatc --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json ../flatc --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json
../flatc --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
../flatc --cpp -o union_vector ./union_vector/union_vector.fbs ../flatc --cpp --gen-mutable --gen-object-api -o union_vector ./union_vector/union_vector.fbs
../flatc -b --schema --bfbs-comments monster_test.fbs ../flatc -b --schema --bfbs-comments monster_test.fbs
cd ../samples cd ../samples
../flatc --cpp --gen-mutable --gen-object-api monster.fbs ../flatc --cpp --gen-mutable --gen-object-api monster.fbs
......
No preview for this file type
...@@ -102,43 +102,43 @@ template<> struct AnyTraits<MyGame::Example2::Monster> { ...@@ -102,43 +102,43 @@ template<> struct AnyTraits<MyGame::Example2::Monster> {
struct AnyUnion { struct AnyUnion {
Any type; Any type;
flatbuffers::NativeTable *table; void *value;
AnyUnion() : type(Any_NONE), table(nullptr) {} AnyUnion() : type(Any_NONE), value(nullptr) {}
AnyUnion(AnyUnion&& u) FLATBUFFERS_NOEXCEPT : AnyUnion(AnyUnion&& u) FLATBUFFERS_NOEXCEPT :
type(Any_NONE), table(nullptr) type(Any_NONE), value(nullptr)
{ std::swap(type, u.type); std::swap(table, u.table); } { std::swap(type, u.type); std::swap(value, u.value); }
AnyUnion(const AnyUnion &); AnyUnion(const AnyUnion &);
AnyUnion &operator=(const AnyUnion &); AnyUnion &operator=(const AnyUnion &);
AnyUnion &operator=(AnyUnion &&u) FLATBUFFERS_NOEXCEPT AnyUnion &operator=(AnyUnion &&u) FLATBUFFERS_NOEXCEPT
{ std::swap(type, u.type); std::swap(table, u.table); return *this; } { std::swap(type, u.type); std::swap(value, u.value); return *this; }
~AnyUnion() { Reset(); } ~AnyUnion() { Reset(); }
void Reset(); void Reset();
template <typename T> template <typename T>
void Set(T&& value) { void Set(T&& val) {
Reset(); Reset();
type = AnyTraits<typename T::TableType>::enum_value; type = AnyTraits<typename T::TableType>::enum_value;
if (type != Any_NONE) { if (type != Any_NONE) {
table = new T(std::forward<T>(value)); value = new T(std::forward<T>(val));
} }
} }
static flatbuffers::NativeTable *UnPack(const void *obj, Any type, const flatbuffers::resolver_function_t *resolver); static void *UnPack(const void *obj, Any type, const flatbuffers::resolver_function_t *resolver);
flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher = nullptr) const; flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher = nullptr) const;
MonsterT *AsMonster() { MonsterT *AsMonster() {
return type == Any_Monster ? return type == Any_Monster ?
reinterpret_cast<MonsterT *>(table) : nullptr; reinterpret_cast<MonsterT *>(value) : nullptr;
} }
TestSimpleTableWithEnumT *AsTestSimpleTableWithEnum() { TestSimpleTableWithEnumT *AsTestSimpleTableWithEnum() {
return type == Any_TestSimpleTableWithEnum ? return type == Any_TestSimpleTableWithEnum ?
reinterpret_cast<TestSimpleTableWithEnumT *>(table) : nullptr; reinterpret_cast<TestSimpleTableWithEnumT *>(value) : nullptr;
} }
MyGame::Example2::MonsterT *AsMyGame_Example2_Monster() { MyGame::Example2::MonsterT *AsMyGame_Example2_Monster() {
return type == Any_MyGame_Example2_Monster ? return type == Any_MyGame_Example2_Monster ?
reinterpret_cast<MyGame::Example2::MonsterT *>(table) : nullptr; reinterpret_cast<MyGame::Example2::MonsterT *>(value) : nullptr;
} }
}; };
...@@ -629,13 +629,13 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { ...@@ -629,13 +629,13 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
} }
template<typename T> const T *test_as() const; template<typename T> const T *test_as() const;
const Monster *test_as_Monster() const { const Monster *test_as_Monster() const {
return (test_type() == Any_Monster)? static_cast<const Monster *>(test()) : nullptr; return test_type() == Any_Monster ? static_cast<const Monster *>(test()) : nullptr;
} }
const TestSimpleTableWithEnum *test_as_TestSimpleTableWithEnum() const { const TestSimpleTableWithEnum *test_as_TestSimpleTableWithEnum() const {
return (test_type() == Any_TestSimpleTableWithEnum)? static_cast<const TestSimpleTableWithEnum *>(test()) : nullptr; return test_type() == Any_TestSimpleTableWithEnum ? static_cast<const TestSimpleTableWithEnum *>(test()) : nullptr;
} }
const MyGame::Example2::Monster *test_as_MyGame_Example2_Monster() const { const MyGame::Example2::Monster *test_as_MyGame_Example2_Monster() const {
return (test_type() == Any_MyGame_Example2_Monster)? static_cast<const MyGame::Example2::Monster *>(test()) : nullptr; return test_type() == Any_MyGame_Example2_Monster ? static_cast<const MyGame::Example2::Monster *>(test()) : nullptr;
} }
void *mutable_test() { void *mutable_test() {
return GetPointer<void *>(VT_TEST); return GetPointer<void *>(VT_TEST);
...@@ -1172,7 +1172,7 @@ inline void Monster::UnPackTo(MonsterT *_o, const flatbuffers::resolver_function ...@@ -1172,7 +1172,7 @@ inline void Monster::UnPackTo(MonsterT *_o, const flatbuffers::resolver_function
{ auto _e = inventory(); if (_e) { _o->inventory.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->inventory[_i] = _e->Get(_i); } } }; { auto _e = inventory(); if (_e) { _o->inventory.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->inventory[_i] = _e->Get(_i); } } };
{ auto _e = color(); _o->color = _e; }; { auto _e = color(); _o->color = _e; };
{ auto _e = test_type(); _o->test.type = _e; }; { auto _e = test_type(); _o->test.type = _e; };
{ auto _e = test(); if (_e) _o->test.table = AnyUnion::UnPack(_e, test_type(),_resolver); }; { auto _e = test(); if (_e) _o->test.value = AnyUnion::UnPack(_e, test_type(), _resolver); };
{ auto _e = test4(); if (_e) { _o->test4.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->test4[_i] = *_e->Get(_i); } } }; { auto _e = test4(); if (_e) { _o->test4.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->test4[_i] = *_e->Get(_i); } } };
{ auto _e = testarrayofstring(); if (_e) { _o->testarrayofstring.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring[_i] = _e->Get(_i)->str(); } } }; { auto _e = testarrayofstring(); if (_e) { _o->testarrayofstring.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring[_i] = _e->Get(_i)->str(); } } };
{ auto _e = testarrayoftables(); if (_e) { _o->testarrayoftables.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayoftables[_i] = std::unique_ptr<MonsterT>(_e->Get(_i)->UnPack(_resolver)); } } }; { auto _e = testarrayoftables(); if (_e) { _o->testarrayoftables.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayoftables[_i] = std::unique_ptr<MonsterT>(_e->Get(_i)->UnPack(_resolver)); } } };
...@@ -1297,7 +1297,7 @@ inline bool VerifyAnyVector(flatbuffers::Verifier &verifier, const flatbuffers:: ...@@ -1297,7 +1297,7 @@ inline bool VerifyAnyVector(flatbuffers::Verifier &verifier, const flatbuffers::
return true; return true;
} }
inline flatbuffers::NativeTable *AnyUnion::UnPack(const void *obj, Any type, const flatbuffers::resolver_function_t *resolver) { inline void *AnyUnion::UnPack(const void *obj, Any type, const flatbuffers::resolver_function_t *resolver) {
switch (type) { switch (type) {
case Any_Monster: { case Any_Monster: {
auto ptr = reinterpret_cast<const Monster *>(obj); auto ptr = reinterpret_cast<const Monster *>(obj);
...@@ -1318,15 +1318,15 @@ inline flatbuffers::NativeTable *AnyUnion::UnPack(const void *obj, Any type, con ...@@ -1318,15 +1318,15 @@ inline flatbuffers::NativeTable *AnyUnion::UnPack(const void *obj, Any type, con
inline flatbuffers::Offset<void> AnyUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher) const { inline flatbuffers::Offset<void> AnyUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher) const {
switch (type) { switch (type) {
case Any_Monster: { case Any_Monster: {
auto ptr = reinterpret_cast<const MonsterT *>(table); auto ptr = reinterpret_cast<const MonsterT *>(value);
return CreateMonster(_fbb, ptr, _rehasher).Union(); return CreateMonster(_fbb, ptr, _rehasher).Union();
} }
case Any_TestSimpleTableWithEnum: { case Any_TestSimpleTableWithEnum: {
auto ptr = reinterpret_cast<const TestSimpleTableWithEnumT *>(table); auto ptr = reinterpret_cast<const TestSimpleTableWithEnumT *>(value);
return CreateTestSimpleTableWithEnum(_fbb, ptr, _rehasher).Union(); return CreateTestSimpleTableWithEnum(_fbb, ptr, _rehasher).Union();
} }
case Any_MyGame_Example2_Monster: { case Any_MyGame_Example2_Monster: {
auto ptr = reinterpret_cast<const MyGame::Example2::MonsterT *>(table); auto ptr = reinterpret_cast<const MyGame::Example2::MonsterT *>(value);
return CreateMonster(_fbb, ptr, _rehasher).Union(); return CreateMonster(_fbb, ptr, _rehasher).Union();
} }
default: return 0; default: return 0;
...@@ -1336,23 +1336,23 @@ inline flatbuffers::Offset<void> AnyUnion::Pack(flatbuffers::FlatBufferBuilder & ...@@ -1336,23 +1336,23 @@ inline flatbuffers::Offset<void> AnyUnion::Pack(flatbuffers::FlatBufferBuilder &
inline void AnyUnion::Reset() { inline void AnyUnion::Reset() {
switch (type) { switch (type) {
case Any_Monster: { case Any_Monster: {
auto ptr = reinterpret_cast<MonsterT *>(table); auto ptr = reinterpret_cast<MonsterT *>(value);
delete ptr; delete ptr;
break; break;
} }
case Any_TestSimpleTableWithEnum: { case Any_TestSimpleTableWithEnum: {
auto ptr = reinterpret_cast<TestSimpleTableWithEnumT *>(table); auto ptr = reinterpret_cast<TestSimpleTableWithEnumT *>(value);
delete ptr; delete ptr;
break; break;
} }
case Any_MyGame_Example2_Monster: { case Any_MyGame_Example2_Monster: {
auto ptr = reinterpret_cast<MyGame::Example2::MonsterT *>(table); auto ptr = reinterpret_cast<MyGame::Example2::MonsterT *>(value);
delete ptr; delete ptr;
break; break;
} }
default: break; default: break;
} }
table = nullptr; value = nullptr;
type = Any_NONE; type = Any_NONE;
} }
......
...@@ -1334,46 +1334,78 @@ void UnionVectorTest() { ...@@ -1334,46 +1334,78 @@ void UnionVectorTest() {
// union types. // union types.
std::vector<uint8_t> types; std::vector<uint8_t> types;
types.push_back(static_cast<uint8_t>(Character_Belle)); types.push_back(static_cast<uint8_t>(Character_Belle));
types.push_back(static_cast<uint8_t>(Character_Rapunzel));
types.push_back(static_cast<uint8_t>(Character_MuLan)); types.push_back(static_cast<uint8_t>(Character_MuLan));
types.push_back(static_cast<uint8_t>(Character_BookFan));
types.push_back(static_cast<uint8_t>(Character_Other));
types.push_back(static_cast<uint8_t>(Character_Unused));
// union values. // union values.
std::vector<flatbuffers::Offset<void>> characters; std::vector<flatbuffers::Offset<void>> characters;
characters.push_back(CreateBelle(fbb, /*books_read=*/7).Union()); characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/7)).Union());
characters.push_back(CreateRapunzel(fbb, /*hair_length=*/6).Union()); characters.push_back(CreateAttacker(fbb, /*sword_attack_damage=*/5).Union());
characters.push_back(CreateMuLan(fbb, /*sword_attack_damage=*/5).Union()); characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/2)).Union());
characters.push_back(fbb.CreateString("Other").Union());
characters.push_back(fbb.CreateString("Unused").Union());
// create Movie. // create Movie.
const auto movie_offset = const auto movie_offset =
CreateMovie(fbb, fbb.CreateVector(types), fbb.CreateVector(characters)); CreateMovie(fbb,
Character_Rapunzel,
fbb.CreateStruct(Rapunzel(/*hair_length=*/6)).Union(),
fbb.CreateVector(types),
fbb.CreateVector(characters));
FinishMovieBuffer(fbb, movie_offset); FinishMovieBuffer(fbb, movie_offset);
uint8_t *buf = fbb.GetBufferPointer(); auto buf = fbb.GetBufferPointer();
flatbuffers::Verifier verifier(buf, fbb.GetSize()); flatbuffers::Verifier verifier(buf, fbb.GetSize());
TEST_EQ(VerifyMovieBuffer(verifier), true); TEST_EQ(VerifyMovieBuffer(verifier), true);
const Movie *movie = GetMovie(buf); auto flat_movie = GetMovie(buf);
TEST_EQ(movie->characters_type()->size(), 3);
TEST_EQ( auto TestMovie = [](const Movie *movie) {
movie->characters_type()->GetEnum<Character>(0) == Character_Belle, TEST_EQ(movie->main_character_type() == Character_Rapunzel, true);
true);
TEST_EQ( auto cts = movie->characters_type();
movie->characters_type()->GetEnum<Character>(1) == Character_Rapunzel, TEST_EQ(movie->characters_type()->size(), 5);
true); TEST_EQ(cts->GetEnum<Character>(0) == Character_Belle, true);
TEST_EQ( TEST_EQ(cts->GetEnum<Character>(1) == Character_MuLan, true);
movie->characters_type()->GetEnum<Character>(2) == Character_MuLan, TEST_EQ(cts->GetEnum<Character>(2) == Character_BookFan, true);
true); TEST_EQ(cts->GetEnum<Character>(3) == Character_Other, true);
TEST_EQ(cts->GetEnum<Character>(4) == Character_Unused, true);
TEST_EQ(movie->characters()->size(), 3);
const Belle *belle = auto rapunzel = movie->main_character_as_Rapunzel();
reinterpret_cast<const Belle*>(movie->characters()->Get(0)); TEST_EQ(rapunzel->hair_length(), 6);
TEST_EQ(belle->books_read(), 7);
const Rapunzel *rapunzel = auto cs = movie->characters();
reinterpret_cast<const Rapunzel*>(movie->characters()->Get(1)); TEST_EQ(cs->size(), 5);
TEST_EQ(rapunzel->hair_length(), 6); auto belle = cs->GetAs<BookReader>(0);
const MuLan *mu_lan = TEST_EQ(belle->books_read(), 7);
reinterpret_cast<const MuLan*>(movie->characters()->Get(2)); auto mu_lan = cs->GetAs<Attacker>(1);
TEST_EQ(mu_lan->sword_attack_damage(), 5); TEST_EQ(mu_lan->sword_attack_damage(), 5);
auto book_fan = cs->GetAs<BookReader>(2);
TEST_EQ(book_fan->books_read(), 2);
auto other = cs->GetAsString(3);
TEST_EQ_STR(other->c_str(), "Other");
auto unused = cs->GetAsString(4);
TEST_EQ_STR(unused->c_str(), "Unused");
};
TestMovie(flat_movie);
auto movie_object = flat_movie->UnPack();
TEST_EQ(movie_object->main_character.AsRapunzel()->hair_length(), 6);
TEST_EQ(movie_object->characters[0].AsBelle()->books_read(), 7);
TEST_EQ(movie_object->characters[1].AsMuLan()->sword_attack_damage, 5);
TEST_EQ(movie_object->characters[2].AsBookFan()->books_read(), 2);
TEST_EQ_STR(movie_object->characters[3].AsOther()->c_str(), "Other");
TEST_EQ_STR(movie_object->characters[4].AsUnused()->c_str(), "Unused");
fbb.Clear();
fbb.Finish(Movie::Pack(fbb, movie_object));
auto repacked_movie = GetMovie(fbb.GetBufferPointer());
TestMovie(repacked_movie);
} }
void ConformTest() { void ConformTest() {
......
table MuLan { // Demonstrates the ability to have vectors of unions, and also to
// store structs and strings in unions.
table Attacker {
sword_attack_damage: int; sword_attack_damage: int;
} }
table Rapunzel { struct Rapunzel {
hair_length: int; hair_length: int;
} }
table Belle { struct BookReader {
books_read: int; books_read: int;
} }
union Character { union Character {
MuLan, MuLan: Attacker, // Can have name be different from type.
Rapunzel, Rapunzel, // Or just both the same, as before.
Belle, Belle: BookReader,
BookFan: BookReader,
Other: string,
Unused: string
} }
table Movie { table Movie {
main_character: Character;
characters: [Character]; characters: [Character];
} }
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment