diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index 3a6adf81d..e7676bf8f 100644 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -138,5 +138,8 @@ Additional options: - `--force-defaults` : Emit default values in binary output from JSON. +- `--force-empty` : When serializing from object API representation, force + strings and vectors to empty rather than null. + NOTE: short-form options for generators are deprecated, use the long form whenever possible. diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 55ef89d14..9d50a94e1 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -422,6 +422,10 @@ struct IDLOptions { // for code generation. unsigned long lang_to_generate; + // If set (default behavior), empty string and vector fields will be set to + // nullptr to make the flatbuffer more compact. + bool set_empty_to_null; + IDLOptions() : strict_json(false), skip_js_exports(false), @@ -457,7 +461,8 @@ struct IDLOptions { force_defaults(false), lang(IDLOptions::kJava), mini_reflect(IDLOptions::kNone), - lang_to_generate(0) {} + lang_to_generate(0), + set_empty_to_null(true) {} }; // This encapsulates where the parser is in the current source file. diff --git a/src/flatc.cpp b/src/flatc.cpp index ef91792dc..bcd5c3bff 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -123,6 +123,8 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " --reflect-names Add minimal type/name reflection.\n" " --root-type T Select or override the default root_type\n" " --force-defaults Emit default values in binary output from JSON\n" + " --force-empty When serializing from object API representation, " + " force strings and vectors to empty rather than null.\n" "FILEs may be schemas (must end in .fbs), or JSON files (conforming to preceding\n" "schema). FILEs after the -- must be binary flatbuffer format files.\n" "Output files are named using the base file name of the input,\n" @@ -280,6 +282,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.root_type = argv[argi]; } else if (arg == "--force-defaults") { opts.force_defaults = true; + } else if (arg == "--force-empty") { + opts.set_empty_to_null = false; } else { for (size_t i = 0; i < params_.num_generators; ++i) { if (arg == params_.generators[i].generator_opt_long || diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 1980c168c..060f4bc6a 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2148,6 +2148,8 @@ class CppGenerator : public BaseGenerator { } std::string GenCreateParam(const FieldDef &field) { + const IDLOptions &opts = parser_.opts; + std::string value = "_o->"; if (field.value.type.base_type == BASE_TYPE_UTYPE) { value += StripUnionType(Name(field)); @@ -2172,8 +2174,13 @@ class CppGenerator : public BaseGenerator { code += "_fbb.CreateString(" + value + ")"; // For optional fields, check to see if there actually is any data - // in _o->field before attempting to access it. - if (!field.required) { code = value + ".empty() ? 0 : " + code; } + // in _o->field before attempting to access it. If there isn't, + // depending on set_empty_to_null either set it to 0 or an empty string. + if (!field.required) { + auto empty_value = + opts.set_empty_to_null ? "0" : "_fbb.CreateSharedString(\"\")"; + code = value + ".empty() ? " + empty_value + " : " + code; + } break; } // Vector fields come in several flavours, of the forms: @@ -2259,9 +2266,12 @@ class CppGenerator : public BaseGenerator { } } - // For optional fields, check to see if there actually is any data - // in _o->field before attempting to access it. - if (!field.required) { code = value + ".size() ? " + code + " : 0"; } + // If set_empty_to_null option is enabled, for optional fields, check to + // see if there actually is any data in _o->field before attempting to + // access it. + if (opts.set_empty_to_null && !field.required) { + code = value + ".size() ? " + code + " : 0"; + } break; } case BASE_TYPE_UNION: {