Add --force-empty-vectors option (#5653)
authorcryptocode <stein@nano.org>
Fri, 6 Dec 2019 01:33:45 +0000 (02:33 +0100)
committerWouter van Oortmerssen <aardappel@gmail.com>
Fri, 6 Dec 2019 01:33:45 +0000 (17:33 -0800)
The rationale for this option is that JSON clients typically want empty arrays (i.e [] in the JSON) instead of missing properties, but not empty strings when the value isn't set.
--force-empty is kept as-is, i.e. it will force both empty strings and vectors.

Closes #5652

docs/source/Compiler.md
include/flatbuffers/idl.h
src/flatc.cpp
src/idl_gen_cpp.cpp

index 9eaabf8..df03786 100644 (file)
@@ -203,5 +203,8 @@ Additional options:
 -   `--force-empty` : When serializing from object API representation, force
      strings and vectors to empty rather than null.
 
+-   `--force-empty-vectors` : When serializing from object API representation, force
+     vectors to empty rather than null.
+
 NOTE: short-form options for generators are deprecated, use the long form
 whenever possible.
index 4e8f71d..2161621 100644 (file)
@@ -588,9 +588,13 @@ 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;
+  // If set (default behavior), empty string fields will be set to nullptr to make
+  // the flatbuffer more compact.
+  bool set_empty_strings_to_null;
+
+  // If set (default behavior), empty vector fields will be set to nullptr to make
+  // the flatbuffer more compact.
+  bool set_empty_vectors_to_null;
 
   IDLOptions()
       : use_flexbuffers(false),
@@ -635,7 +639,8 @@ struct IDLOptions {
         lang(IDLOptions::kJava),
         mini_reflect(IDLOptions::kNone),
         lang_to_generate(0),
-        set_empty_to_null(true) {}
+        set_empty_strings_to_null(true),
+        set_empty_vectors_to_null(true) {}
 };
 
 // This encapsulates where the parser is in the current source file.
index 8cc2d3c..9c59232 100644 (file)
@@ -63,7 +63,7 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const {
     const Generator &g = params_.generators[i];
 
     std::stringstream full_name;
-    full_name << std::setw(12) << std::left << g.generator_opt_long;
+    full_name << std::setw(16) << std::left << g.generator_opt_long;
     const char *name = g.generator_opt_short ? g.generator_opt_short : "  ";
     const char *help = g.generator_help;
 
@@ -71,86 +71,88 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const {
   }
   // clang-format off
   ss <<
-    "  -o PATH            Prefix PATH to all generated files.\n"
-    "  -I PATH            Search for includes in the specified path.\n"
-    "  -M                 Print make rules for generated files.\n"
-    "  --version          Print the version number of flatc and exit.\n"
-    "  --strict-json      Strict JSON: field names must be / will be quoted,\n"
-    "                     no trailing commas in tables/vectors.\n"
-    "  --allow-non-utf8   Pass non-UTF-8 input through parser and emit nonstandard\n"
-    "                     \\x escapes in JSON. (Default is to raise parse error on\n"
-    "                     non-UTF-8 input.)\n"
-    "  --natural-utf8     Output strings with UTF-8 as human-readable strings.\n"
-    "                     By default, UTF-8 characters are printed as \\uXXXX escapes.\n"
-    "  --defaults-json    Output fields whose value is the default when\n"
-    "                     writing JSON\n"
-    "  --unknown-json     Allow fields in JSON that are not defined in the\n"
-    "                     schema. These fields will be discared when generating\n"
-    "                     binaries.\n"
-    "  --no-prefix        Don\'t prefix enum values with the enum type in C++.\n"
-    "  --scoped-enums     Use C++11 style scoped and strongly typed enums.\n"
-    "                     also implies --no-prefix.\n"
-    "  --gen-includes     (deprecated), this is the default behavior.\n"
-    "                     If the original behavior is required (no include\n"
-    "                     statements) use --no-includes.\n"
-    "  --no-includes      Don\'t generate include statements for included\n"
-    "                     schemas the generated file depends on (C++ / Python).\n"
-    "  --gen-mutable      Generate accessors that can mutate buffers in-place.\n"
-    "  --gen-onefile      Generate single output file for C# and Go.\n"
-    "  --gen-name-strings Generate type name functions for C++.\n"
-    "  --gen-object-api   Generate an additional object-based API.\n"
-    "  --gen-compare      Generate operator== for object-based API types.\n"
-    "  --gen-nullable     Add Clang _Nullable for C++ pointer. or @Nullable for Java\n"
-    "  --java-checkerframework Add @Pure for Java.\n"
-    "  --gen-generated    Add @Generated annotation for Java\n"
-    "  --gen-all          Generate not just code for the current schema files,\n"
-    "                     but for all files it includes as well.\n"
-    "                     If the language uses a single file for output (by default\n"
-    "                     the case for C++ and JS), all code will end up in this one\n"
-    "                     file.\n"
-    "  --cpp-include      Adds an #include in generated file.\n"
-    "  --cpp-ptr-type T   Set object API pointer type (default std::unique_ptr).\n"
-    "  --cpp-str-type T   Set object API string type (default std::string).\n"
-    "                     T::c_str(), T::length() and T::empty() must be supported.\n"
-    "                     The custom type also needs to be constructible from std::string\n"
-    "                     (see the --cpp-str-flex-ctor option to change this behavior).\n"
-    "  --cpp-str-flex-ctor Don't construct custom string types by passing std::string\n"
-    "                     from Flatbuffers, but (char* + length).\n"
-    "  --object-prefix    Customise class prefix for C++ object-based API.\n"
-    "  --object-suffix    Customise class suffix for C++ object-based API.\n"
-    "                     Default value is \"T\".\n"
-    "  --no-js-exports    Removes Node.js style export lines in JS.\n"
-    "  --goog-js-export   Uses goog.exports* for closure compiler exporting in JS.\n"
-    "  --es6-js-export    Uses ECMAScript 6 export style lines in JS.\n"
-    "  --go-namespace     Generate the overrided namespace in Golang.\n"
-    "  --go-import        Generate the overrided import for flatbuffers in Golang\n"
-    "                     (default is \"github.com/google/flatbuffers/go\").\n"
-    "  --raw-binary       Allow binaries without file_indentifier to be read.\n"
-    "                     This may crash flatc given a mismatched schema.\n"
-    "  --size-prefixed    Input binaries are size prefixed buffers.\n"
-    "  --proto            Input is a .proto, translate to .fbs.\n"
-    "  --oneof-union      Translate .proto oneofs to flatbuffer unions.\n"
-    "  --grpc             Generate GRPC interfaces for the specified languages.\n"
-    "  --schema           Serialize schemas instead of JSON (use with -b).\n"
-    "  --bfbs-comments    Add doc comments to the binary schema files.\n"
-    "  --bfbs-builtins    Add builtin attributes to the binary schema files.\n"
-    "  --conform FILE     Specify a schema the following schemas should be\n"
-    "                     an evolution of. Gives errors if not.\n"
-    "  --conform-includes Include path for the schema given with --conform PATH\n"
-    "  --include-prefix   Prefix this path to any generated include statements.\n"
+    "  -o PATH                Prefix PATH to all generated files.\n"
+    "  -I PATH                Search for includes in the specified path.\n"
+    "  -M                     Print make rules for generated files.\n"
+    "  --version              Print the version number of flatc and exit.\n"
+    "  --strict-json          Strict JSON: field names must be / will be quoted,\n"
+    "                         no trailing commas in tables/vectors.\n"
+    "  --allow-non-utf8       Pass non-UTF-8 input through parser and emit nonstandard\n"
+    "                         \\x escapes in JSON. (Default is to raise parse error on\n"
+    "                         non-UTF-8 input.)\n"
+    "  --natural-utf8         Output strings with UTF-8 as human-readable strings.\n"
+    "                         By default, UTF-8 characters are printed as \\uXXXX escapes.\n"
+    "  --defaults-json        Output fields whose value is the default when\n"
+    "                         writing JSON\n"
+    "  --unknown-json         Allow fields in JSON that are not defined in the\n"
+    "                         schema. These fields will be discared when generating\n"
+    "                         binaries.\n"
+    "  --no-prefix            Don\'t prefix enum values with the enum type in C++.\n"
+    "  --scoped-enums         Use C++11 style scoped and strongly typed enums.\n"
+    "                         also implies --no-prefix.\n"
+    "  --gen-includes         (deprecated), this is the default behavior.\n"
+    "                         If the original behavior is required (no include\n"
+    "                         statements) use --no-includes.\n"
+    "  --no-includes          Don\'t generate include statements for included\n"
+    "                         schemas the generated file depends on (C++ / Python).\n"
+    "  --gen-mutable          Generate accessors that can mutate buffers in-place.\n"
+    "  --gen-onefile          Generate single output file for C# and Go.\n"
+    "  --gen-name-strings     Generate type name functions for C++.\n"
+    "  --gen-object-api       Generate an additional object-based API.\n"
+    "  --gen-compare          Generate operator== for object-based API types.\n"
+    "  --gen-nullable         Add Clang _Nullable for C++ pointer. or @Nullable for Java\n"
+    "  --java-checkerframe    work Add @Pure for Java.\n"
+    "  --gen-generated        Add @Generated annotation for Java\n"
+    "  --gen-all              Generate not just code for the current schema files,\n"
+    "                         but for all files it includes as well.\n"
+    "                         If the language uses a single file for output (by default\n"
+    "                         the case for C++ and JS), all code will end up in this one\n"
+    "                         file.\n"
+    "  --cpp-include          Adds an #include in generated file.\n"
+    "  --cpp-ptr-type T       Set object API pointer type (default std::unique_ptr).\n"
+    "  --cpp-str-type T       Set object API string type (default std::string).\n"
+    "                         T::c_str(), T::length() and T::empty() must be supported.\n"
+    "                         The custom type also needs to be constructible from std::string\n"
+    "                         (see the --cpp-str-flex-ctor option to change this behavior).\n"
+    "  --cpp-str-flex-ctor     Don't construct custom string types by passing std::string\n"
+    "                         from Flatbuffers, but (char* + length).\n"
+    "  --object-prefix        Customise class prefix for C++ object-based API.\n"
+    "  --object-suffix        Customise class suffix for C++ object-based API.\n"
+    "                         Default value is \"T\".\n"
+    "  --no-js-exports        Removes Node.js style export lines in JS.\n"
+    "  --goog-js-export       Uses goog.exports* for closure compiler exporting in JS.\n"
+    "  --es6-js-export        Uses ECMAScript 6 export style lines in JS.\n"
+    "  --go-namespace         Generate the overrided namespace in Golang.\n"
+    "  --go-import            Generate the overrided import for flatbuffers in Golang\n"
+    "                         (default is \"github.com/google/flatbuffers/go\").\n"
+    "  --raw-binary           Allow binaries without file_indentifier to be read.\n"
+    "                         This may crash flatc given a mismatched schema.\n"
+    "  --size-prefixed        Input binaries are size prefixed buffers.\n"
+    "  --proto                Input is a .proto, translate to .fbs.\n"
+    "  --oneof-union          Translate .proto oneofs to flatbuffer unions.\n"
+    "  --grpc                 Generate GRPC interfaces for the specified languages.\n"
+    "  --schema               Serialize schemas instead of JSON (use with -b).\n"
+    "  --bfbs-comments        Add doc comments to the binary schema files.\n"
+    "  --bfbs-builtins        Add builtin attributes to the binary schema files.\n"
+    "  --conform FILE         Specify a schema the following schemas should be\n"
+    "                         an evolution of. Gives errors if not.\n"
+    "  --conform-includes     Include path for the schema given with --conform PATH\n"
+    "  --include-prefix       Prefix this path to any generated include statements.\n"
     "    PATH\n"
-    "  --keep-prefix      Keep original prefix of schema include statement.\n"
-    "  --no-fb-import     Don't include flatbuffers import statement for TypeScript.\n"
-    "  --no-ts-reexport   Don't re-export imported dependencies for TypeScript.\n"
-    "  --short-names      Use short function names for JS and TypeScript.\n"
-    "  --reflect-types    Add minimal type reflection to code generation.\n"
-    "  --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,\n"
-    "                     force strings and vectors to empty rather than null.\n"
-    "  --flexbuffers      Used with \"binary\" and \"json\" options, it generates\n"
-    "                     data using schema-less FlexBuffers.\n"
+    "  --keep-prefix          Keep original prefix of schema include statement.\n"
+    "  --no-fb-import         Don't include flatbuffers import statement for TypeScript.\n"
+    "  --no-ts-reexport       Don't re-export imported dependencies for TypeScript.\n"
+    "  --short-names          Use short function names for JS and TypeScript.\n"
+    "  --reflect-types        Add minimal type reflection to code generation.\n"
+    "  --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,\n"
+    "                         force strings and vectors to empty rather than null.\n"
+    "  --force-empty-vectors  When serializing from object API representation,\n"
+    "                         force vectors to empty rather than null.\n"
+    "  --flexbuffers          Used with \"binary\" and \"json\" options, it generates\n"
+    "                         data using schema-less FlexBuffers.\n"
     "FILEs may be schemas (must end in .fbs), binary schemas (must end in .bfbs),\n"
     "or JSON files (conforming to preceding schema). FILEs after the -- must be\n"
     "binary flatbuffer format files.\n"
@@ -323,7 +325,10 @@ int FlatCompiler::Compile(int argc, const char **argv) {
       } else if (arg == "--force-defaults") {
         opts.force_defaults = true;
       } else if (arg == "--force-empty") {
-        opts.set_empty_to_null = false;
+        opts.set_empty_strings_to_null = false;
+        opts.set_empty_vectors_to_null = false;
+      } else if (arg == "--force-empty-vectors") {
+        opts.set_empty_vectors_to_null = false;
       } else if (arg == "--java-primitive-has-method") {
         opts.java_primitive_has_method = true;
       } else if (arg == "--flexbuffers") {
index 7a46792..fd53ece 100644 (file)
@@ -2452,10 +2452,10 @@ 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 there isn't,
-        // depending on set_empty_to_null either set it to 0 or an empty string.
+        // depending on set_empty_strings_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(\"\")";
+              opts.set_empty_strings_to_null ? "0" : "_fbb.CreateSharedString(\"\")";
           code = value + ".empty() ? " + empty_value + " : " + code;
         }
         break;
@@ -2557,10 +2557,10 @@ class CppGenerator : public BaseGenerator {
           }
         }
 
-        // If set_empty_to_null option is enabled, for optional fields, check to
+        // If set_empty_vectors_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) {
+        if (opts.set_empty_vectors_to_null && !field.required) {
           code = value + ".size() ? " + code + " : 0";
         }
         break;