P GetStruct(voffset_t field) const {
- auto field_offset = GetOptionalFieldOffset(field);
- return field_offset ? reinterpret_cast(data_ + field_offset) : nullptr;
+ template P GetPointer(voffset_t field) const {
+ return const_cast(this)->GetPointer(field);
}
- template void SetField(voffset_t field, T val) {
+ template P GetStruct(voffset_t field) const {
auto field_offset = GetOptionalFieldOffset(field);
- // If this asserts, you're trying to set a field that's not there
- // (or should we return a bool instead?).
- // check if it exists first using CheckField()
- assert(field_offset);
+ auto p = const_cast(data_ + field_offset);
+ return field_offset ? reinterpret_cast(p) : nullptr;
+ }
+
+ template bool SetField(voffset_t field, T val) {
+ auto field_offset = GetOptionalFieldOffset(field);
+ if (!field_offset) return false;
WriteScalar(data_ + field_offset, val);
+ return true;
}
bool CheckField(voffset_t field) const {
diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h
index 7eb9fc44a..14d3364a9 100644
--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
@@ -395,6 +395,7 @@ struct GeneratorOptions {
bool output_enum_identifiers;
bool prefixed_enums;
bool include_dependence_headers;
+ bool mutable_buffer;
// Possible options for the more general generator below.
enum Language { kJava, kCSharp, kGo, kMAX };
@@ -404,6 +405,7 @@ struct GeneratorOptions {
GeneratorOptions() : strict_json(false), indent_step(2),
output_enum_identifiers(true), prefixed_enums(true),
include_dependence_headers(false),
+ mutable_buffer(false),
lang(GeneratorOptions::kJava) {}
};
diff --git a/src/flatc.cpp b/src/flatc.cpp
index 3f91eb6a3..e22085e40 100755
--- a/src/flatc.cpp
+++ b/src/flatc.cpp
@@ -89,6 +89,7 @@ static void Error(const char *err, const char *obj, bool usage,
" --no-prefix Don\'t prefix enum values with the enum type in C++.\n"
" --gen-includes Generate include statements for included schemas the\n"
" generated file depends on (C++).\n"
+ " --gen-mutable Generate accessors that can mutate buffers in-place.\n"
" --proto Input is a .proto, translate to .fbs.\n"
"FILEs may depend on declarations in earlier files.\n"
"FILEs after the -- must be binary flatbuffer format files.\n"
@@ -128,6 +129,8 @@ int main(int argc, const char *argv[]) {
opts.strict_json = true;
} else if(opt == "--no-prefix") {
opts.prefixed_enums = false;
+ } else if(opt == "--gen-mutable") {
+ opts.mutable_buffer = true;
} else if(opt == "--gen-includes") {
opts.include_dependence_headers = true;
} else if(opt == "--") { // Separator between text and binary inputs.
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index 841c6cad1..aab572905 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -221,22 +221,41 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
++it) {
auto &field = **it;
if (!field.deprecated) { // Deprecated fields won't be accessible.
+ auto is_scalar = IsScalar(field.value.type.base_type);
GenComment(field.doc_comment, code_ptr, nullptr, " ");
code += " " + GenTypeGet(parser, field.value.type, " ", "const ", " *",
true);
code += field.name + "() const { return ";
// Call a different accessor for pointers, that indirects.
- std::string call = IsScalar(field.value.type.base_type)
+ auto accessor = is_scalar
? "GetField<"
: (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<");
- call += GenTypeGet(parser, field.value.type, "", "const ", " *", false);
- call += ">(" + NumToString(field.value.offset);
+ auto offsetstr = NumToString(field.value.offset);
+ auto call =
+ accessor +
+ GenTypeGet(parser, field.value.type, "", "const ", " *", false) +
+ ">(" + offsetstr;
// Default value as second arg for non-pointer types.
if (IsScalar(field.value.type.base_type))
call += ", " + field.value.constant;
call += ")";
code += GenUnderlyingCast(parser, field, true, call);
code += "; }\n";
+ if (opts.mutable_buffer) {
+ if (is_scalar) {
+ code += " bool mutate_" + field.name + "(";
+ code += GenTypeBasic(parser, field.value.type, true);
+ code += " " + field.name + ") { return SetField(" + offsetstr + ", ";
+ code += GenUnderlyingCast(parser, field, false, field.name);
+ code += "); }\n";
+ } else {
+ auto type = GenTypeGet(parser, field.value.type, " ", "", " *", true);
+ code += " " + type + "mutable_" + field.name + "() { return ";
+ code += GenUnderlyingCast(parser, field, true,
+ accessor + type + ">(" + offsetstr + ")");
+ code += "; }\n";
+ }
+ }
auto nested = field.attributes.Lookup("nested_flatbuffer");
if (nested) {
auto nested_root = parser.structs_.Lookup(nested->constant);
@@ -416,7 +435,8 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
code += " return builder_.Finish();\n}\n\n";
}
-static void GenPadding(const FieldDef &field, const std::function &f) {
+static void GenPadding(const FieldDef &field,
+ const std::function &f) {
if (field.padding) {
for (int i = 0; i < 4; i++)
if (static_cast(field.padding) & (1 << i))
@@ -427,7 +447,7 @@ static void GenPadding(const FieldDef &field, const std::function(buf); }\n\n";
+ if (opts.mutable_buffer) {
+ code += "inline " + name + " *GetMutable";
+ code += name;
+ code += "(void *buf) { return flatbuffers::GetMutableRoot<";
+ code += name + ">(buf); }\n\n";
+ }
// The root verifier:
code += "inline bool Verify";
diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h
index 8313cea46..f729fa02a 100755
--- a/tests/monster_test_generated.h
+++ b/tests/monster_test_generated.h
@@ -57,7 +57,9 @@ MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS {
: a_(flatbuffers::EndianScalar(a)), b_(flatbuffers::EndianScalar(b)), __padding0(0) { (void)__padding0; }
int16_t a() const { return flatbuffers::EndianScalar(a_); }
+ void mutate_a(int16_t a) { flatbuffers::WriteScalar(&a_, a); }
int8_t b() const { return flatbuffers::EndianScalar(b_); }
+ void mutate_b(int8_t b) { flatbuffers::WriteScalar(&b_, b); }
};
STRUCT_END(Test, 4);
@@ -78,18 +80,27 @@ MANUALLY_ALIGNED_STRUCT(16) Vec3 FLATBUFFERS_FINAL_CLASS {
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), z_(flatbuffers::EndianScalar(z)), __padding0(0), test1_(flatbuffers::EndianScalar(test1)), test2_(flatbuffers::EndianScalar(static_cast(test2))), __padding1(0), test3_(test3), __padding2(0) { (void)__padding0; (void)__padding1; (void)__padding2; }
float x() const { return flatbuffers::EndianScalar(x_); }
+ void mutate_x(float x) { flatbuffers::WriteScalar(&x_, x); }
float y() const { return flatbuffers::EndianScalar(y_); }
+ void mutate_y(float y) { flatbuffers::WriteScalar(&y_, y); }
float z() const { return flatbuffers::EndianScalar(z_); }
+ void mutate_z(float z) { flatbuffers::WriteScalar(&z_, z); }
double test1() const { return flatbuffers::EndianScalar(test1_); }
+ void mutate_test1(double test1) { flatbuffers::WriteScalar(&test1_, test1); }
Color test2() const { return static_cast(flatbuffers::EndianScalar(test2_)); }
+ void mutate_test2(Color test2) { flatbuffers::WriteScalar(&test2_, static_cast(test2)); }
const Test &test3() const { return test3_; }
+ Test &mutable_test3() { return test3_; }
};
STRUCT_END(Vec3, 32);
struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
const flatbuffers::String *id() const { return GetPointer(4); }
+ flatbuffers::String *mutable_id() { return GetPointer(4); }
int64_t val() const { return GetField(6, 0); }
+ bool mutate_val(int64_t val) { return SetField(6, val); }
uint16_t count() const { return GetField(8, 0); }
+ bool mutate_count(uint16_t count) { return SetField(8, count); }
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField(verifier, 4 /* id */) &&
@@ -127,33 +138,56 @@ inline flatbuffers::Offset CreateStat(flatbuffers::FlatBufferBuilder &_fbb
struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
const Vec3 *pos() const { return GetStruct(4); }
+ Vec3 *mutable_pos() { return GetStruct(4); }
int16_t mana() const { return GetField(6, 150); }
+ bool mutate_mana(int16_t mana) { return SetField(6, mana); }
int16_t hp() const { return GetField(8, 100); }
+ bool mutate_hp(int16_t hp) { return SetField(8, hp); }
const flatbuffers::String *name() const { return GetPointer(10); }
+ flatbuffers::String *mutable_name() { return GetPointer(10); }
bool KeyCompareLessThan(const Monster *o) const { return *name() < *o->name(); }
int KeyCompareWithValue(const char *val) const { return strcmp(name()->c_str(), val); }
const flatbuffers::Vector *inventory() const { return GetPointer *>(14); }
+ flatbuffers::Vector *mutable_inventory() { return GetPointer *>(14); }
Color color() const { return static_cast(GetField(16, 8)); }
+ bool mutate_color(Color color) { return SetField(16, static_cast(color)); }
Any test_type() const { return static_cast(GetField(18, 0)); }
+ bool mutate_test_type(Any test_type) { return SetField(18, static_cast(test_type)); }
const void *test() const { return GetPointer(20); }
+ void *mutable_test() { return GetPointer(20); }
const flatbuffers::Vector *test4() const { return GetPointer *>(22); }
+ flatbuffers::Vector *mutable_test4() { return GetPointer *>(22); }
const flatbuffers::Vector> *testarrayofstring() const { return GetPointer> *>(24); }
+ flatbuffers::Vector> *mutable_testarrayofstring() { return GetPointer> *>(24); }
/// an example documentation comment: this will end up in the generated code
/// multiline too
const flatbuffers::Vector> *testarrayoftables() const { return GetPointer> *>(26); }
+ flatbuffers::Vector> *mutable_testarrayoftables() { return GetPointer> *>(26); }
const Monster *enemy() const { return GetPointer(28); }
+ Monster *mutable_enemy() { return GetPointer(28); }
const flatbuffers::Vector *testnestedflatbuffer() const { return GetPointer *>(30); }
+ flatbuffers::Vector *mutable_testnestedflatbuffer() { return GetPointer *>(30); }
const Monster *testnestedflatbuffer_nested_root() const { return flatbuffers::GetRoot(testnestedflatbuffer()->Data()); }
const Stat *testempty() const { return GetPointer(32); }
+ Stat *mutable_testempty() { return GetPointer(32); }
uint8_t testbool() const { return GetField(34, 0); }
+ bool mutate_testbool(uint8_t testbool) { return SetField(34, testbool); }
int32_t testhashs32_fnv1() const { return GetField(36, 0); }
+ bool mutate_testhashs32_fnv1(int32_t testhashs32_fnv1) { return SetField(36, testhashs32_fnv1); }
uint32_t testhashu32_fnv1() const { return GetField(38, 0); }
+ bool mutate_testhashu32_fnv1(uint32_t testhashu32_fnv1) { return SetField(38, testhashu32_fnv1); }
int64_t testhashs64_fnv1() const { return GetField(40, 0); }
+ bool mutate_testhashs64_fnv1(int64_t testhashs64_fnv1) { return SetField(40, testhashs64_fnv1); }
uint64_t testhashu64_fnv1() const { return GetField(42, 0); }
+ bool mutate_testhashu64_fnv1(uint64_t testhashu64_fnv1) { return SetField(42, testhashu64_fnv1); }
int32_t testhashs32_fnv1a() const { return GetField(44, 0); }
+ bool mutate_testhashs32_fnv1a(int32_t testhashs32_fnv1a) { return SetField(44, testhashs32_fnv1a); }
uint32_t testhashu32_fnv1a() const { return GetField(46, 0); }
+ bool mutate_testhashu32_fnv1a(uint32_t testhashu32_fnv1a) { return SetField(46, testhashu32_fnv1a); }
int64_t testhashs64_fnv1a() const { return GetField(48, 0); }
+ bool mutate_testhashs64_fnv1a(int64_t testhashs64_fnv1a) { return SetField(48, testhashs64_fnv1a); }
uint64_t testhashu64_fnv1a() const { return GetField(50, 0); }
+ bool mutate_testhashu64_fnv1a(uint64_t testhashu64_fnv1a) { return SetField(50, testhashu64_fnv1a); }
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField(verifier, 4 /* pos */) &&
@@ -290,6 +324,8 @@ inline bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, An
inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot(buf); }
+inline Monster *GetMutableMonster(void *buf) { return flatbuffers::GetMutableRoot(buf); }
+
inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(); }
inline const char *MonsterIdentifier() { return "MONS"; }
diff --git a/tests/test.cpp b/tests/test.cpp
index 4f9a12c56..766b3258d 100644
--- a/tests/test.cpp
+++ b/tests/test.cpp
@@ -127,7 +127,7 @@ flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) {
}
// example of accessing a buffer loaded in memory:
-void AccessFlatBufferTest(const uint8_t *flatbuf, const std::size_t length) {
+void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length) {
// First, verify the buffers integrity (optional)
flatbuffers::Verifier verifier(flatbuf, length);
@@ -159,6 +159,8 @@ void AccessFlatBufferTest(const uint8_t *flatbuf, const std::size_t length) {
for (auto it = inventory->begin(); it != inventory->end(); ++it)
TEST_EQ(*it, inv_data[it - inventory->begin()]);
+ TEST_EQ(monster->color(), Color_Blue);
+
// Example of accessing a union:
TEST_EQ(monster->test_type(), Any_Monster); // First make sure which it is.
auto monster2 = reinterpret_cast(monster->test());
@@ -200,7 +202,39 @@ void AccessFlatBufferTest(const uint8_t *flatbuf, const std::size_t length) {
for (auto it = tests->begin(); it != tests->end(); ++it) {
TEST_EQ(it->a() == 10 || it->a() == 30, true); // Just testing iterators.
}
+}
+// Change a FlatBuffer in-place, after it has been constructed.
+void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
+ // Get non-const pointer to root.
+ auto monster = GetMutableMonster(flatbuf);
+
+ // Each of these tests mutates, then tests, then set back to the original,
+ // so we can test that the buffer in the end still passes our original test.
+ auto hp_ok = monster->mutate_hp(10);
+ TEST_EQ(hp_ok, true); // Field was present.
+ TEST_EQ(monster->hp(), 10);
+ monster->mutate_hp(80);
+
+ auto mana_ok = monster->mutate_mana(10);
+ TEST_EQ(mana_ok, false); // Field was NOT present, because default value.
+
+ // Mutate structs.
+ auto pos = monster->mutable_pos();
+ auto test3 = pos->mutable_test3(); // Struct inside a struct.
+ test3.mutate_a(50); // Struct fields never fail.
+ TEST_EQ(test3.a(), 50);
+ test3.mutate_a(10);
+
+ // Mutate vectors.
+ auto inventory = monster->mutable_inventory();
+ inventory->Mutate(9, 100);
+ TEST_EQ(inventory->Get(9), 100);
+ inventory->Mutate(9, 9);
+
+ // Run the verifier and the regular test to make sure we didn't trample on
+ // anything.
+ AccessFlatBufferTest(flatbuf, length);
}
// example of parsing text straight into a buffer, and generating
@@ -626,6 +660,8 @@ int main(int /*argc*/, const char * /*argv*/[]) {
rawbuf.length());
AccessFlatBufferTest(flatbuf.get(), rawbuf.length());
+ MutateFlatBuffersTest(flatbuf.get(), rawbuf.length());
+
#ifndef __ANDROID__ // requires file access
ParseAndGenerateTextTest();
ParseProtoTest();