diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index ea78aeeea..048167c55 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -1646,9 +1646,9 @@ class Table { return field_offset ? reinterpret_cast

(p) : nullptr; } - template bool SetField(voffset_t field, T val) { + template 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; } diff --git a/include/flatbuffers/reflection.h b/include/flatbuffers/reflection.h index 6d8608a68..aab9f2088 100644 --- a/include/flatbuffers/reflection.h +++ b/include/flatbuffers/reflection.h @@ -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(flatbuf); } +// Get a field's default, if you know it's an integer, and its exact type. +template T GetFieldDefaultI(const reflection::Field &field) { + assert(sizeof(T) == GetTypeSize(field.type()->base_type())); + return static_cast(field.default_integer()); +} + +// Get a field's default, if you know it's floating point and its exact type. +template T GetFieldDefaultF(const reflection::Field &field) { + assert(sizeof(T) == GetTypeSize(field.type()->base_type())); + return static_cast(field.default_real()); +} + // Get a field, if you know it's an integer, and its exact type. template T GetFieldI(const Table &table, const reflection::Field &field) { @@ -242,8 +263,19 @@ template T *GetAnyFieldAddressOf(const Struct &st, // Set any scalar field, if you know its exact type. template 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(field); + } else { + assert(IsFloat(type)); + def = GetFieldDefaultF(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(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(field); SetAnyValueF(field.type()->base_type(), field_ptr, val); return true; } diff --git a/samples/monster_generated.h b/samples/monster_generated.h index 48ca9818d..464fc1768 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -183,13 +183,13 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return GetField(VT_MANA, 150); } bool mutate_mana(int16_t _mana) { - return SetField(VT_MANA, _mana); + return SetField(VT_MANA, _mana, 150); } int16_t hp() const { return GetField(VT_HP, 100); } bool mutate_hp(int16_t _hp) { - return SetField(VT_HP, _hp); + return SetField(VT_HP, _hp, 100); } const flatbuffers::String *name() const { return GetPointer(VT_NAME); @@ -207,7 +207,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return static_cast(GetField(VT_COLOR, 2)); } bool mutate_color(Color _color) { - return SetField(VT_COLOR, static_cast(_color)); + return SetField(VT_COLOR, static_cast(_color), 2); } const flatbuffers::Vector> *weapons() const { return GetPointer> *>(VT_WEAPONS); @@ -219,7 +219,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return static_cast(GetField(VT_EQUIPPED_TYPE, 0)); } bool mutate_equipped_type(Equipment _equipped_type) { - return SetField(VT_EQUIPPED_TYPE, static_cast(_equipped_type)); + return SetField(VT_EQUIPPED_TYPE, static_cast(_equipped_type), 0); } const void *equipped() const { return GetPointer(VT_EQUIPPED); @@ -375,7 +375,7 @@ struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return GetField(VT_DAMAGE, 0); } bool mutate_damage(int16_t _damage) { - return SetField(VT_DAMAGE, _damage); + return SetField(VT_DAMAGE, _damage, 0); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 19172aa93..857287596 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -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); diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs index 449921cc8..21f3c0008 100644 Binary files a/tests/monster_test.bfbs and b/tests/monster_test.bfbs differ diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 61e909840..ea7f4c3bd 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -312,7 +312,7 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta return static_cast(GetField(VT_COLOR, 2)); } bool mutate_color(Color _color) { - return SetField(VT_COLOR, static_cast(_color)); + return SetField(VT_COLOR, static_cast(_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(VT_VAL, 0); } bool mutate_val(int64_t _val) { - return SetField(VT_VAL, _val); + return SetField(VT_VAL, _val, 0); } uint16_t count() const { return GetField(VT_COUNT, 0); } bool mutate_count(uint16_t _count) { - return SetField(VT_COUNT, _count); + return SetField(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(VT_MANA, 150); } bool mutate_mana(int16_t _mana) { - return SetField(VT_MANA, _mana); + return SetField(VT_MANA, _mana, 150); } int16_t hp() const { return GetField(VT_HP, 100); } bool mutate_hp(int16_t _hp) { - return SetField(VT_HP, _hp); + return SetField(VT_HP, _hp, 100); } const flatbuffers::String *name() const { return GetPointer(VT_NAME); @@ -572,13 +572,13 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return static_cast(GetField(VT_COLOR, 8)); } bool mutate_color(Color _color) { - return SetField(VT_COLOR, static_cast(_color)); + return SetField(VT_COLOR, static_cast(_color), 8); } Any test_type() const { return static_cast(GetField(VT_TEST_TYPE, 0)); } bool mutate_test_type(Any _test_type) { - return SetField(VT_TEST_TYPE, static_cast(_test_type)); + return SetField(VT_TEST_TYPE, static_cast(_test_type), 0); } const void *test() const { return GetPointer(VT_TEST); @@ -642,55 +642,55 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return GetField(VT_TESTBOOL, 0) != 0; } bool mutate_testbool(bool _testbool) { - return SetField(VT_TESTBOOL, static_cast(_testbool)); + return SetField(VT_TESTBOOL, static_cast(_testbool), 0); } int32_t testhashs32_fnv1() const { return GetField(VT_TESTHASHS32_FNV1, 0); } bool mutate_testhashs32_fnv1(int32_t _testhashs32_fnv1) { - return SetField(VT_TESTHASHS32_FNV1, _testhashs32_fnv1); + return SetField(VT_TESTHASHS32_FNV1, _testhashs32_fnv1, 0); } uint32_t testhashu32_fnv1() const { return GetField(VT_TESTHASHU32_FNV1, 0); } bool mutate_testhashu32_fnv1(uint32_t _testhashu32_fnv1) { - return SetField(VT_TESTHASHU32_FNV1, _testhashu32_fnv1); + return SetField(VT_TESTHASHU32_FNV1, _testhashu32_fnv1, 0); } int64_t testhashs64_fnv1() const { return GetField(VT_TESTHASHS64_FNV1, 0); } bool mutate_testhashs64_fnv1(int64_t _testhashs64_fnv1) { - return SetField(VT_TESTHASHS64_FNV1, _testhashs64_fnv1); + return SetField(VT_TESTHASHS64_FNV1, _testhashs64_fnv1, 0); } uint64_t testhashu64_fnv1() const { return GetField(VT_TESTHASHU64_FNV1, 0); } bool mutate_testhashu64_fnv1(uint64_t _testhashu64_fnv1) { - return SetField(VT_TESTHASHU64_FNV1, _testhashu64_fnv1); + return SetField(VT_TESTHASHU64_FNV1, _testhashu64_fnv1, 0); } int32_t testhashs32_fnv1a() const { return GetField(VT_TESTHASHS32_FNV1A, 0); } bool mutate_testhashs32_fnv1a(int32_t _testhashs32_fnv1a) { - return SetField(VT_TESTHASHS32_FNV1A, _testhashs32_fnv1a); + return SetField(VT_TESTHASHS32_FNV1A, _testhashs32_fnv1a, 0); } uint32_t testhashu32_fnv1a() const { return GetField(VT_TESTHASHU32_FNV1A, 0); } bool mutate_testhashu32_fnv1a(uint32_t _testhashu32_fnv1a) { - return SetField(VT_TESTHASHU32_FNV1A, _testhashu32_fnv1a); + return SetField(VT_TESTHASHU32_FNV1A, _testhashu32_fnv1a, 0); } int64_t testhashs64_fnv1a() const { return GetField(VT_TESTHASHS64_FNV1A, 0); } bool mutate_testhashs64_fnv1a(int64_t _testhashs64_fnv1a) { - return SetField(VT_TESTHASHS64_FNV1A, _testhashs64_fnv1a); + return SetField(VT_TESTHASHS64_FNV1A, _testhashs64_fnv1a, 0); } uint64_t testhashu64_fnv1a() const { return GetField(VT_TESTHASHU64_FNV1A, 0); } bool mutate_testhashu64_fnv1a(uint64_t _testhashu64_fnv1a) { - return SetField(VT_TESTHASHU64_FNV1A, _testhashu64_fnv1a); + return SetField(VT_TESTHASHU64_FNV1A, _testhashu64_fnv1a, 0); } const flatbuffers::Vector *testarrayofbools() const { return GetPointer *>(VT_TESTARRAYOFBOOLS); @@ -702,19 +702,19 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return GetField(VT_TESTF, 3.14159f); } bool mutate_testf(float _testf) { - return SetField(VT_TESTF, _testf); + return SetField(VT_TESTF, _testf, 3.14159f); } float testf2() const { return GetField(VT_TESTF2, 3.0f); } bool mutate_testf2(float _testf2) { - return SetField(VT_TESTF2, _testf2); + return SetField(VT_TESTF2, _testf2, 3.0f); } float testf3() const { return GetField(VT_TESTF3, 0.0f); } bool mutate_testf3(float _testf3) { - return SetField(VT_TESTF3, _testf3); + return SetField(VT_TESTF3, _testf3, 0.0f); } const flatbuffers::Vector> *testarrayofstring2() const { return GetPointer> *>(VT_TESTARRAYOFSTRING2); diff --git a/tests/namespace_test/namespace_test1_generated.h b/tests/namespace_test/namespace_test1_generated.h index 9c8c28a73..2a71fbcad 100644 --- a/tests/namespace_test/namespace_test1_generated.h +++ b/tests/namespace_test/namespace_test1_generated.h @@ -75,7 +75,7 @@ struct TableInNestedNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return GetField(VT_FOO, 0); } bool mutate_foo(int32_t _foo) { - return SetField(VT_FOO, _foo); + return SetField(VT_FOO, _foo, 0); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index 15971ba18..4e2a622c9 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -40,7 +40,7 @@ struct TableInFirstNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return static_cast(GetField(VT_FOO_ENUM, 0)); } bool mutate_foo_enum(NamespaceA::NamespaceB::EnumInNestedNS _foo_enum) { - return SetField(VT_FOO_ENUM, static_cast(_foo_enum)); + return SetField(VT_FOO_ENUM, static_cast(_foo_enum), 0); } const NamespaceA::NamespaceB::StructInNestedNS *foo_struct() const { return GetStruct(VT_FOO_STRUCT); diff --git a/tests/test.cpp b/tests/test.cpp index f74e9a8b8..4e031b6dd 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -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();