Skip to content

Commit

Permalink
Added support for structs and strings in unions.
Browse files Browse the repository at this point in the history
(C++ only for now).
Also fixed vector of union support in the object API.

Bug: 36902939
Change-Id: I935f4cc2c303a4728e26c7916a8ec0adcd6f84cb
Tested: on Linux.
  • Loading branch information
aardappel committed Apr 13, 2017
1 parent 1fc12e0 commit b0752e1
Show file tree
Hide file tree
Showing 16 changed files with 851 additions and 317 deletions.
18 changes: 18 additions & 0 deletions docs/source/CppUsage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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>
21 changes: 21 additions & 0 deletions include/flatbuffers/flatbuffers.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ struct VectorIterator
const uint8_t *data_;
};

struct String;

// This is used as a helper type for accessing vectors.
// Vector::data() assumes the vector elements start after the length field.
template<typename T> class Vector {
Expand Down Expand Up @@ -391,6 +393,18 @@ template<typename T> class Vector {
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 {
return reinterpret_cast<const void *>(Data() + o);
}
Expand Down Expand Up @@ -1327,6 +1341,13 @@ FLATBUFFERS_FINAL_CLASS
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.
static const size_t kFileIdentifierLength = 4;

Expand Down
8 changes: 5 additions & 3 deletions include/flatbuffers/idl.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,18 +283,18 @@ inline size_t InlineAlignment(const Type &type) {

struct EnumVal {
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;

std::string name;
std::vector<std::string> doc_comment;
int64_t value;
StructDef *struct_def; // only set if this is a union
Type union_type;
};

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) {
for (auto it = vals.vec.begin() + static_cast<int>(is_union &&
Expand All @@ -312,6 +312,7 @@ struct EnumDef : public Definition {

SymbolTable<EnumVal> vals;
bool is_union;
bool uses_type_aliases;
Type underlying_type;
};

Expand Down Expand Up @@ -548,6 +549,7 @@ class Parser : public ParserState {
const std::string &name, const Type &type,
FieldDef **dest);
FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def);
FLATBUFFERS_CHECKED_ERROR ParseString(Value &val);
FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field,
size_t parent_fieldn,
const StructDef *parent_struct_def);
Expand Down
23 changes: 18 additions & 5 deletions include/flatbuffers/reflection_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum {
VT_NAME = 4,
VT_VALUE = 6,
VT_OBJECT = 8
VT_OBJECT = 8,
VT_UNION_TYPE = 10
};
const flatbuffers::String *name() const {
return GetPointer<const flatbuffers::String *>(VT_NAME);
Expand All @@ -228,13 +229,18 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
const Object *object() const {
return GetPointer<const Object *>(VT_OBJECT);
}
const Type *union_type() const {
return GetPointer<const Type *>(VT_UNION_TYPE);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyFieldRequired<flatbuffers::uoffset_t>(verifier, VT_NAME) &&
verifier.Verify(name()) &&
VerifyField<int64_t>(verifier, VT_VALUE) &&
VerifyField<flatbuffers::uoffset_t>(verifier, VT_OBJECT) &&
verifier.VerifyTable(object()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, VT_UNION_TYPE) &&
verifier.VerifyTable(union_type()) &&
verifier.EndTable();
}
};
Expand All @@ -251,13 +257,16 @@ struct EnumValBuilder {
void add_object(flatbuffers::Offset<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)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
EnumValBuilder &operator=(const EnumValBuilder &);
flatbuffers::Offset<EnumVal> Finish() {
const auto end = fbb_.EndTable(start_, 3);
const auto end = fbb_.EndTable(start_, 4);
auto o = flatbuffers::Offset<EnumVal>(end);
fbb_.Required(o, EnumVal::VT_NAME);
return o;
Expand All @@ -268,9 +277,11 @@ inline flatbuffers::Offset<EnumVal> CreateEnumVal(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> name = 0,
int64_t value = 0,
flatbuffers::Offset<Object> object = 0) {
flatbuffers::Offset<Object> object = 0,
flatbuffers::Offset<Type> union_type = 0) {
EnumValBuilder builder_(_fbb);
builder_.add_value(value);
builder_.add_union_type(union_type);
builder_.add_object(object);
builder_.add_name(name);
return builder_.Finish();
Expand All @@ -280,12 +291,14 @@ inline flatbuffers::Offset<EnumVal> CreateEnumValDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const char *name = nullptr,
int64_t value = 0,
flatbuffers::Offset<Object> object = 0) {
flatbuffers::Offset<Object> object = 0,
flatbuffers::Offset<Type> union_type = 0) {
return reflection::CreateEnumVal(
_fbb,
name ? _fbb.CreateString(name) : 0,
value,
object);
object,
union_type);
}

struct Enum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
Expand Down
3 changes: 2 additions & 1 deletion reflection/reflection.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ table KeyValue {
table EnumVal {
name:string (required);
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 {
Expand Down
30 changes: 15 additions & 15 deletions samples/monster_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,35 +71,35 @@ template<> struct EquipmentTraits<Weapon> {

struct EquipmentUnion {
Equipment type;
flatbuffers::NativeTable *table;
void *value;

EquipmentUnion() : type(Equipment_NONE), table(nullptr) {}
EquipmentUnion() : type(Equipment_NONE), value(nullptr) {}
EquipmentUnion(EquipmentUnion&& u) FLATBUFFERS_NOEXCEPT :
type(Equipment_NONE), table(nullptr)
{ std::swap(type, u.type); std::swap(table, u.table); }
type(Equipment_NONE), value(nullptr)
{ std::swap(type, u.type); std::swap(value, u.value); }
EquipmentUnion(const EquipmentUnion &);
EquipmentUnion &operator=(const EquipmentUnion &);
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(); }

void Reset();

template <typename T>
void Set(T&& value) {
void Set(T&& val) {
Reset();
type = EquipmentTraits<typename T::TableType>::enum_value;
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;

WeaponT *AsWeapon() {
return type == Equipment_Weapon ?
reinterpret_cast<WeaponT *>(table) : nullptr;
reinterpret_cast<WeaponT *>(value) : nullptr;
}
};

Expand Down Expand Up @@ -228,7 +228,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
}
template<typename T> const T *equipped_as() 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() {
return GetPointer<void *>(VT_EQUIPPED);
Expand Down Expand Up @@ -451,7 +451,7 @@ inline void Monster::UnPackTo(MonsterT *_o, const flatbuffers::resolver_function
{ 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 = 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) {
Expand Down Expand Up @@ -535,7 +535,7 @@ inline bool VerifyEquipmentVector(flatbuffers::Verifier &verifier, const flatbuf
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) {
case Equipment_Weapon: {
auto ptr = reinterpret_cast<const Weapon *>(obj);
Expand All @@ -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 {
switch (type) {
case Equipment_Weapon: {
auto ptr = reinterpret_cast<const WeaponT *>(table);
auto ptr = reinterpret_cast<const WeaponT *>(value);
return CreateWeapon(_fbb, ptr, _rehasher).Union();
}
default: return 0;
Expand All @@ -558,13 +558,13 @@ inline flatbuffers::Offset<void> EquipmentUnion::Pack(flatbuffers::FlatBufferBui
inline void EquipmentUnion::Reset() {
switch (type) {
case Equipment_Weapon: {
auto ptr = reinterpret_cast<WeaponT *>(table);
auto ptr = reinterpret_cast<WeaponT *>(value);
delete ptr;
break;
}
default: break;
}
table = nullptr;
value = nullptr;
type = Equipment_NONE;
}

Expand Down
Loading

0 comments on commit b0752e1

Please sign in to comment.