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:
Lawrence Chan 2017-03-20 19:36:27 -05:00 committed by Wouter van Oortmerssen
parent b8f5f84437
commit 1a27c7017a
9 changed files with 81 additions and 33 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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) &&

View File

@ -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.

View File

@ -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);

View File

@ -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) &&

View File

@ -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);

View File

@ -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();