diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index d7b44c874..90f8d8b41 100644 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -75,7 +75,7 @@ Additional options: - `--allow-non-utf8` : Pass non-UTF-8 input through parser and emit nonstandard \x escapes in JSON. (Default is to raise parse error on non-UTF-8 input.) -- `--natural-utf8` : Output strings with UTF-8 as human-readable strings. +- `--natural-utf8` : Output strings with UTF-8 as human-readable strings. By default, UTF-8 characters are printed as \uXXXX escapes." - `--defaults-json` : Output fields whose value is equal to the default value @@ -120,7 +120,13 @@ Additional options: - `--cpp-ptr-type T` : Set object API pointer type (default std::unique_ptr) - `--cpp-str-type T` : Set object API string type (default std::string) -- T::c_str() and T::length() must be supported. + T::c_str(), T::length() and T::empty() must be supported. + The custom type also needs to be constructible from std::string (see the + --cpp-str-flex-ctor option to change this behavior). + +- `--cpp-str-flex-ctor` : Don't construct custom string types by passing + std::string from Flatbuffers, but (char* + length). This allows efficient + construction of custom string types, including zero-copy construction. - `--object-prefix` : Customise class prefix for C++ object-based API. @@ -168,7 +174,7 @@ Additional options: an evolution of. Gives errors if not. Useful to check if schema modifications don't break schema evolution rules. -- `--conform-includes PATH` : Include path for the schema given with +- `--conform-includes PATH` : Include path for the schema given with `--conform PATH`. - `--include-prefix PATH` : Prefix this path to any generated include diff --git a/docs/source/CppUsage.md b/docs/source/CppUsage.md index 2808c49c6..647ab6362 100644 --- a/docs/source/CppUsage.md +++ b/docs/source/CppUsage.md @@ -255,14 +255,24 @@ you, so you'll have to manage their lifecycles manually. To reference the pointer type specified by the `--cpp-ptr-type` argument to `flatc` from a flatbuffer field set the `cpp_ptr_type` attribute to `default_ptr_type`. - # Using different string type. By default the object tree is built out of `std::string`, but you can influence this either globally (using the `--cpp-str-type` argument to `flatc`) or per field using the `cpp_str_type` attribute. -The type must support T::c_str() and T::length() as member functions. +The type must support T::c_str(), T::length() and T::empty() as member functions. + +Further, the type must be constructible from std::string, as by default a +std::string instance is constructed and then used to initialize the custom +string type. This behavior impedes efficient and zero-copy construction of +custom string types; the `--cpp-str-flex-ctor` argument to `flatc` or the +per field attribute `cpp_str_flex_ctor` can be used to change this behavior, +so that the custom string type is constructed by passing the pointer and +length of the FlatBuffers String. The custom string class will require a +constructor in the following format: custom_str_class(const char *, uint32_t). +Please note that the character array is not guaranteed to be NUL terminated, +you should always use the provided size to determine end of string. ## Reflection (& Resizing) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index cfd8d039a..d5ad3b023 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -410,6 +410,7 @@ struct IDLOptions { bool gen_compare; std::string cpp_object_api_pointer_type; std::string cpp_object_api_string_type; + bool cpp_object_api_string_flexible_constructor; bool gen_nullable; bool gen_generated; std::string object_prefix; @@ -486,6 +487,7 @@ struct IDLOptions { generate_object_based_api(false), gen_compare(false), cpp_object_api_pointer_type("std::unique_ptr"), + cpp_object_api_string_flexible_constructor(false), gen_nullable(false), gen_generated(false), object_suffix("T"), @@ -627,6 +629,7 @@ class Parser : public ParserState { known_attributes_["cpp_ptr_type"] = true; known_attributes_["cpp_ptr_type_get"] = true; known_attributes_["cpp_str_type"] = true; + known_attributes_["cpp_str_flex_ctor"] = true; known_attributes_["native_inline"] = true; known_attributes_["native_custom_alloc"] = true; known_attributes_["native_type"] = true; diff --git a/src/flatc.cpp b/src/flatc.cpp index c5ec70ecf..3833f3dec 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -100,14 +100,18 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " --gen-compare Generate operator== for object-based API types.\n" " --gen-nullable Add Clang _Nullable for C++ pointer. or @Nullable for Java\n" " --gen-generated Add @Generated annotation for Java\n" - " --gen-all Generate not just code for the current schema files,\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-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() and T::length() must be supported.\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" @@ -247,6 +251,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { } else if (arg == "--cpp-str-type") { if (++argi >= argc) Error("missing type following" + arg, true); opts.cpp_object_api_string_type = argv[argi]; + } else if (arg == "--cpp-str-flex-ctor") { + opts.cpp_object_api_string_flexible_constructor = true; } else if (arg == "--gen-nullable") { opts.gen_nullable = true; } else if (arg == "--gen-generated") { diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index b21f79ca1..12f63f64d 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -578,6 +578,12 @@ class CppGenerator : public BaseGenerator { return ret; } + bool FlexibleStringConstructor(const FieldDef *field) { + auto attr = field ? (field->attributes.Lookup("cpp_str_flex_ctor") != nullptr) : false; + auto ret = attr ? attr : parser_.opts.cpp_object_api_string_flexible_constructor; + return (ret && NativeString(field) != "std::string"); // Only for custom string types. + } + std::string GenTypeNativePtr(const std::string &type, const FieldDef *field, bool is_constructor) { auto &ptr_type = PtrType(field); @@ -2138,7 +2144,11 @@ class CppGenerator : public BaseGenerator { bool invector, const FieldDef &afield) { switch (type.base_type) { case BASE_TYPE_STRING: { - return val + "->str()"; + if (FlexibleStringConstructor(&afield)) { + return NativeString(&afield) + "(" + val + "->c_str(), " + val + "->size())"; + } else { + return val + "->str()"; + } } case BASE_TYPE_STRUCT: { const auto name = WrapInNameSpace(*type.struct_def); @@ -2169,7 +2179,7 @@ class CppGenerator : public BaseGenerator { break; } } - }; + } std::string GenUnpackFieldStatement(const FieldDef &field, const FieldDef *union_field) {