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