C++: Add default value handling to mutation/SetField code (#4230)
* Add default value handling to mutation/SetField code * Shorten reflection SetField impl * Modify impl to work with C++03 * Add more mutation tests * Fail SetField if non-scalar * Add IsScalar/IsInteger/IsFloat for reflection::BaseType * Use new IsScalar/IsInteger/IsFloat in reflection SetField * Assume scalar is either int or float
This commit is contained in:
parent
b8f5f84437
commit
1a27c7017a
|
@ -1646,9 +1646,9 @@ class Table {
|
|||
return field_offset ? reinterpret_cast<P>(p) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T> bool SetField(voffset_t field, T val) {
|
||||
template<typename T> bool SetField(voffset_t field, T val, T def) {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
if (!field_offset) return false;
|
||||
if (!field_offset) return val == def;
|
||||
WriteScalar(data_ + field_offset, val);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,15 @@ namespace flatbuffers {
|
|||
|
||||
// ------------------------- GETTERS -------------------------
|
||||
|
||||
inline bool IsScalar (reflection::BaseType t) { return t >= reflection::UType &&
|
||||
t <= reflection::Double; }
|
||||
inline bool IsInteger(reflection::BaseType t) { return t >= reflection::UType &&
|
||||
t <= reflection::ULong; }
|
||||
inline bool IsFloat (reflection::BaseType t) { return t == reflection::Float ||
|
||||
t == reflection::Double; }
|
||||
inline bool IsLong (reflection::BaseType t) { return t == reflection::Long ||
|
||||
t == reflection::ULong; }
|
||||
|
||||
// Size of a basic type, don't use with structs.
|
||||
inline size_t GetTypeSize(reflection::BaseType base_type) {
|
||||
// This needs to correspond to the BaseType enum.
|
||||
|
@ -58,6 +67,18 @@ inline const Table *GetAnyRoot(const uint8_t *flatbuf) {
|
|||
return GetRoot<Table>(flatbuf);
|
||||
}
|
||||
|
||||
// Get a field's default, if you know it's an integer, and its exact type.
|
||||
template<typename T> T GetFieldDefaultI(const reflection::Field &field) {
|
||||
assert(sizeof(T) == GetTypeSize(field.type()->base_type()));
|
||||
return static_cast<T>(field.default_integer());
|
||||
}
|
||||
|
||||
// Get a field's default, if you know it's floating point and its exact type.
|
||||
template<typename T> T GetFieldDefaultF(const reflection::Field &field) {
|
||||
assert(sizeof(T) == GetTypeSize(field.type()->base_type()));
|
||||
return static_cast<T>(field.default_real());
|
||||
}
|
||||
|
||||
// Get a field, if you know it's an integer, and its exact type.
|
||||
template<typename T> T GetFieldI(const Table &table,
|
||||
const reflection::Field &field) {
|
||||
|
@ -242,8 +263,19 @@ template<typename T> T *GetAnyFieldAddressOf(const Struct &st,
|
|||
// Set any scalar field, if you know its exact type.
|
||||
template<typename T> bool SetField(Table *table, const reflection::Field &field,
|
||||
T val) {
|
||||
assert(sizeof(T) == GetTypeSize(field.type()->base_type()));
|
||||
return table->SetField(field.offset(), val);
|
||||
reflection::BaseType type = field.type()->base_type();
|
||||
if (!IsScalar(type)) {
|
||||
return false;
|
||||
}
|
||||
assert(sizeof(T) == GetTypeSize(type));
|
||||
T def;
|
||||
if (IsInteger(type)) {
|
||||
def = GetFieldDefaultI<T>(field);
|
||||
} else {
|
||||
assert(IsFloat(type));
|
||||
def = GetFieldDefaultF<T>(field);
|
||||
}
|
||||
return table->SetField(field.offset(), val, def);
|
||||
}
|
||||
|
||||
// Raw helper functions used below: set any value in memory as a 64bit int, a
|
||||
|
@ -258,7 +290,7 @@ void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val);
|
|||
inline bool SetAnyFieldI(Table *table, const reflection::Field &field,
|
||||
int64_t val) {
|
||||
auto field_ptr = table->GetAddressOf(field.offset());
|
||||
if (!field_ptr) return false;
|
||||
if (!field_ptr) return val == GetFieldDefaultI<int64_t>(field);
|
||||
SetAnyValueI(field.type()->base_type(), field_ptr, val);
|
||||
return true;
|
||||
}
|
||||
|
@ -267,7 +299,7 @@ inline bool SetAnyFieldI(Table *table, const reflection::Field &field,
|
|||
inline bool SetAnyFieldF(Table *table, const reflection::Field &field,
|
||||
double val) {
|
||||
auto field_ptr = table->GetAddressOf(field.offset());
|
||||
if (!field_ptr) return false;
|
||||
if (!field_ptr) return val == GetFieldDefaultF<double>(field);
|
||||
SetAnyValueF(field.type()->base_type(), field_ptr, val);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -183,13 +183,13 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return GetField<int16_t>(VT_MANA, 150);
|
||||
}
|
||||
bool mutate_mana(int16_t _mana) {
|
||||
return SetField(VT_MANA, _mana);
|
||||
return SetField<int16_t>(VT_MANA, _mana, 150);
|
||||
}
|
||||
int16_t hp() const {
|
||||
return GetField<int16_t>(VT_HP, 100);
|
||||
}
|
||||
bool mutate_hp(int16_t _hp) {
|
||||
return SetField(VT_HP, _hp);
|
||||
return SetField<int16_t>(VT_HP, _hp, 100);
|
||||
}
|
||||
const flatbuffers::String *name() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_NAME);
|
||||
|
@ -207,7 +207,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return static_cast<Color>(GetField<int8_t>(VT_COLOR, 2));
|
||||
}
|
||||
bool mutate_color(Color _color) {
|
||||
return SetField(VT_COLOR, static_cast<int8_t>(_color));
|
||||
return SetField<int8_t>(VT_COLOR, static_cast<int8_t>(_color), 2);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<Weapon>> *weapons() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Weapon>> *>(VT_WEAPONS);
|
||||
|
@ -219,7 +219,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return static_cast<Equipment>(GetField<uint8_t>(VT_EQUIPPED_TYPE, 0));
|
||||
}
|
||||
bool mutate_equipped_type(Equipment _equipped_type) {
|
||||
return SetField(VT_EQUIPPED_TYPE, static_cast<uint8_t>(_equipped_type));
|
||||
return SetField<uint8_t>(VT_EQUIPPED_TYPE, static_cast<uint8_t>(_equipped_type), 0);
|
||||
}
|
||||
const void *equipped() const {
|
||||
return GetPointer<const void *>(VT_EQUIPPED);
|
||||
|
@ -375,7 +375,7 @@ struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return GetField<int16_t>(VT_DAMAGE, 0);
|
||||
}
|
||||
bool mutate_damage(int16_t _damage) {
|
||||
return SetField(VT_DAMAGE, _damage);
|
||||
return SetField<int16_t>(VT_DAMAGE, _damage, 0);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
|
|
|
@ -1162,14 +1162,17 @@ class CppGenerator : public BaseGenerator {
|
|||
|
||||
if (parser_.opts.mutable_buffer) {
|
||||
if (is_scalar) {
|
||||
const auto type = GenTypeWire(field.value.type, "", false);
|
||||
code_.SetValue("SET_FN", "SetField<" + type + ">");
|
||||
code_.SetValue("OFFSET_NAME", offset_str);
|
||||
code_.SetValue("FIELD_TYPE", GenTypeBasic(field.value.type, true));
|
||||
code_.SetValue("FIELD_VALUE",
|
||||
GenUnderlyingCast(field, false, "_" + field.name));
|
||||
code_.SetValue("DEFAULT_VALUE", GenDefaultConstant(field));
|
||||
|
||||
code_ += " bool mutate_{{FIELD_NAME}}({{FIELD_TYPE}} "
|
||||
"_{{FIELD_NAME}}) {";
|
||||
code_ += " return SetField({{OFFSET_NAME}}, {{FIELD_VALUE}});";
|
||||
code_ += " return {{SET_FN}}({{OFFSET_NAME}}, {{FIELD_VALUE}}, {{DEFAULT_VALUE}});";
|
||||
code_ += " }";
|
||||
} else {
|
||||
auto type = GenTypeGet(field.value.type, " ", "", " *", true);
|
||||
|
|
Binary file not shown.
|
@ -312,7 +312,7 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta
|
|||
return static_cast<Color>(GetField<int8_t>(VT_COLOR, 2));
|
||||
}
|
||||
bool mutate_color(Color _color) {
|
||||
return SetField(VT_COLOR, static_cast<int8_t>(_color));
|
||||
return SetField<int8_t>(VT_COLOR, static_cast<int8_t>(_color), 2);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
|
@ -380,13 +380,13 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return GetField<int64_t>(VT_VAL, 0);
|
||||
}
|
||||
bool mutate_val(int64_t _val) {
|
||||
return SetField(VT_VAL, _val);
|
||||
return SetField<int64_t>(VT_VAL, _val, 0);
|
||||
}
|
||||
uint16_t count() const {
|
||||
return GetField<uint16_t>(VT_COUNT, 0);
|
||||
}
|
||||
bool mutate_count(uint16_t _count) {
|
||||
return SetField(VT_COUNT, _count);
|
||||
return SetField<uint16_t>(VT_COUNT, _count, 0);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
|
@ -542,13 +542,13 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return GetField<int16_t>(VT_MANA, 150);
|
||||
}
|
||||
bool mutate_mana(int16_t _mana) {
|
||||
return SetField(VT_MANA, _mana);
|
||||
return SetField<int16_t>(VT_MANA, _mana, 150);
|
||||
}
|
||||
int16_t hp() const {
|
||||
return GetField<int16_t>(VT_HP, 100);
|
||||
}
|
||||
bool mutate_hp(int16_t _hp) {
|
||||
return SetField(VT_HP, _hp);
|
||||
return SetField<int16_t>(VT_HP, _hp, 100);
|
||||
}
|
||||
const flatbuffers::String *name() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_NAME);
|
||||
|
@ -572,13 +572,13 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return static_cast<Color>(GetField<int8_t>(VT_COLOR, 8));
|
||||
}
|
||||
bool mutate_color(Color _color) {
|
||||
return SetField(VT_COLOR, static_cast<int8_t>(_color));
|
||||
return SetField<int8_t>(VT_COLOR, static_cast<int8_t>(_color), 8);
|
||||
}
|
||||
Any test_type() const {
|
||||
return static_cast<Any>(GetField<uint8_t>(VT_TEST_TYPE, 0));
|
||||
}
|
||||
bool mutate_test_type(Any _test_type) {
|
||||
return SetField(VT_TEST_TYPE, static_cast<uint8_t>(_test_type));
|
||||
return SetField<uint8_t>(VT_TEST_TYPE, static_cast<uint8_t>(_test_type), 0);
|
||||
}
|
||||
const void *test() const {
|
||||
return GetPointer<const void *>(VT_TEST);
|
||||
|
@ -642,55 +642,55 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return GetField<uint8_t>(VT_TESTBOOL, 0) != 0;
|
||||
}
|
||||
bool mutate_testbool(bool _testbool) {
|
||||
return SetField(VT_TESTBOOL, static_cast<uint8_t>(_testbool));
|
||||
return SetField<uint8_t>(VT_TESTBOOL, static_cast<uint8_t>(_testbool), 0);
|
||||
}
|
||||
int32_t testhashs32_fnv1() const {
|
||||
return GetField<int32_t>(VT_TESTHASHS32_FNV1, 0);
|
||||
}
|
||||
bool mutate_testhashs32_fnv1(int32_t _testhashs32_fnv1) {
|
||||
return SetField(VT_TESTHASHS32_FNV1, _testhashs32_fnv1);
|
||||
return SetField<int32_t>(VT_TESTHASHS32_FNV1, _testhashs32_fnv1, 0);
|
||||
}
|
||||
uint32_t testhashu32_fnv1() const {
|
||||
return GetField<uint32_t>(VT_TESTHASHU32_FNV1, 0);
|
||||
}
|
||||
bool mutate_testhashu32_fnv1(uint32_t _testhashu32_fnv1) {
|
||||
return SetField(VT_TESTHASHU32_FNV1, _testhashu32_fnv1);
|
||||
return SetField<uint32_t>(VT_TESTHASHU32_FNV1, _testhashu32_fnv1, 0);
|
||||
}
|
||||
int64_t testhashs64_fnv1() const {
|
||||
return GetField<int64_t>(VT_TESTHASHS64_FNV1, 0);
|
||||
}
|
||||
bool mutate_testhashs64_fnv1(int64_t _testhashs64_fnv1) {
|
||||
return SetField(VT_TESTHASHS64_FNV1, _testhashs64_fnv1);
|
||||
return SetField<int64_t>(VT_TESTHASHS64_FNV1, _testhashs64_fnv1, 0);
|
||||
}
|
||||
uint64_t testhashu64_fnv1() const {
|
||||
return GetField<uint64_t>(VT_TESTHASHU64_FNV1, 0);
|
||||
}
|
||||
bool mutate_testhashu64_fnv1(uint64_t _testhashu64_fnv1) {
|
||||
return SetField(VT_TESTHASHU64_FNV1, _testhashu64_fnv1);
|
||||
return SetField<uint64_t>(VT_TESTHASHU64_FNV1, _testhashu64_fnv1, 0);
|
||||
}
|
||||
int32_t testhashs32_fnv1a() const {
|
||||
return GetField<int32_t>(VT_TESTHASHS32_FNV1A, 0);
|
||||
}
|
||||
bool mutate_testhashs32_fnv1a(int32_t _testhashs32_fnv1a) {
|
||||
return SetField(VT_TESTHASHS32_FNV1A, _testhashs32_fnv1a);
|
||||
return SetField<int32_t>(VT_TESTHASHS32_FNV1A, _testhashs32_fnv1a, 0);
|
||||
}
|
||||
uint32_t testhashu32_fnv1a() const {
|
||||
return GetField<uint32_t>(VT_TESTHASHU32_FNV1A, 0);
|
||||
}
|
||||
bool mutate_testhashu32_fnv1a(uint32_t _testhashu32_fnv1a) {
|
||||
return SetField(VT_TESTHASHU32_FNV1A, _testhashu32_fnv1a);
|
||||
return SetField<uint32_t>(VT_TESTHASHU32_FNV1A, _testhashu32_fnv1a, 0);
|
||||
}
|
||||
int64_t testhashs64_fnv1a() const {
|
||||
return GetField<int64_t>(VT_TESTHASHS64_FNV1A, 0);
|
||||
}
|
||||
bool mutate_testhashs64_fnv1a(int64_t _testhashs64_fnv1a) {
|
||||
return SetField(VT_TESTHASHS64_FNV1A, _testhashs64_fnv1a);
|
||||
return SetField<int64_t>(VT_TESTHASHS64_FNV1A, _testhashs64_fnv1a, 0);
|
||||
}
|
||||
uint64_t testhashu64_fnv1a() const {
|
||||
return GetField<uint64_t>(VT_TESTHASHU64_FNV1A, 0);
|
||||
}
|
||||
bool mutate_testhashu64_fnv1a(uint64_t _testhashu64_fnv1a) {
|
||||
return SetField(VT_TESTHASHU64_FNV1A, _testhashu64_fnv1a);
|
||||
return SetField<uint64_t>(VT_TESTHASHU64_FNV1A, _testhashu64_fnv1a, 0);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *testarrayofbools() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_TESTARRAYOFBOOLS);
|
||||
|
@ -702,19 +702,19 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return GetField<float>(VT_TESTF, 3.14159f);
|
||||
}
|
||||
bool mutate_testf(float _testf) {
|
||||
return SetField(VT_TESTF, _testf);
|
||||
return SetField<float>(VT_TESTF, _testf, 3.14159f);
|
||||
}
|
||||
float testf2() const {
|
||||
return GetField<float>(VT_TESTF2, 3.0f);
|
||||
}
|
||||
bool mutate_testf2(float _testf2) {
|
||||
return SetField(VT_TESTF2, _testf2);
|
||||
return SetField<float>(VT_TESTF2, _testf2, 3.0f);
|
||||
}
|
||||
float testf3() const {
|
||||
return GetField<float>(VT_TESTF3, 0.0f);
|
||||
}
|
||||
bool mutate_testf3(float _testf3) {
|
||||
return SetField(VT_TESTF3, _testf3);
|
||||
return SetField<float>(VT_TESTF3, _testf3, 0.0f);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *testarrayofstring2() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_TESTARRAYOFSTRING2);
|
||||
|
|
|
@ -75,7 +75,7 @@ struct TableInNestedNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return GetField<int32_t>(VT_FOO, 0);
|
||||
}
|
||||
bool mutate_foo(int32_t _foo) {
|
||||
return SetField(VT_FOO, _foo);
|
||||
return SetField<int32_t>(VT_FOO, _foo, 0);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
|
|
|
@ -40,7 +40,7 @@ struct TableInFirstNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return static_cast<NamespaceA::NamespaceB::EnumInNestedNS>(GetField<int8_t>(VT_FOO_ENUM, 0));
|
||||
}
|
||||
bool mutate_foo_enum(NamespaceA::NamespaceB::EnumInNestedNS _foo_enum) {
|
||||
return SetField(VT_FOO_ENUM, static_cast<int8_t>(_foo_enum));
|
||||
return SetField<int8_t>(VT_FOO_ENUM, static_cast<int8_t>(_foo_enum), 0);
|
||||
}
|
||||
const NamespaceA::NamespaceB::StructInNestedNS *foo_struct() const {
|
||||
return GetStruct<const NamespaceA::NamespaceB::StructInNestedNS *>(VT_FOO_STRUCT);
|
||||
|
|
|
@ -285,10 +285,23 @@ void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
|
|||
auto hp_ok = monster->mutate_hp(10);
|
||||
TEST_EQ(hp_ok, true); // Field was present.
|
||||
TEST_EQ(monster->hp(), 10);
|
||||
// Mutate to default value
|
||||
auto hp_ok_default = monster->mutate_hp(100);
|
||||
TEST_EQ(hp_ok_default, true); // Field was present.
|
||||
TEST_EQ(monster->hp(), 100);
|
||||
// Test that mutate to default above keeps field valid for further mutations
|
||||
auto hp_ok_2 = monster->mutate_hp(20);
|
||||
TEST_EQ(hp_ok_2, true);
|
||||
TEST_EQ(monster->hp(), 20);
|
||||
monster->mutate_hp(80);
|
||||
|
||||
// Monster originally at 150 mana (default value)
|
||||
auto mana_default_ok = monster->mutate_mana(150); // Mutate to default value.
|
||||
TEST_EQ(mana_default_ok, true); // Mutation should succeed, because default value.
|
||||
TEST_EQ(monster->mana(), 150);
|
||||
auto mana_ok = monster->mutate_mana(10);
|
||||
TEST_EQ(mana_ok, false); // Field was NOT present, because default value.
|
||||
TEST_EQ(monster->mana(), 150);
|
||||
|
||||
// Mutate structs.
|
||||
auto pos = monster->mutable_pos();
|
||||
|
|
Loading…
Reference in New Issue