forked from BigfootDev/flatbuffers
vector-type
This commit is contained in:
@@ -238,6 +238,9 @@ set(FlatBuffers_Tests_SRCS
|
|||||||
tests/util_test.cpp
|
tests/util_test.cpp
|
||||||
tests/vector_table_naked_ptr_test.h
|
tests/vector_table_naked_ptr_test.h
|
||||||
tests/vector_table_naked_ptr_test.cpp
|
tests/vector_table_naked_ptr_test.cpp
|
||||||
|
tests/test_vector_type.h
|
||||||
|
tests/cpp_vector_type_test.h
|
||||||
|
tests/cpp_vector_type_test.cpp
|
||||||
tests/native_type_test_impl.h
|
tests/native_type_test_impl.h
|
||||||
tests/native_type_test_impl.cpp
|
tests/native_type_test_impl.cpp
|
||||||
tests/alignment_test.h
|
tests/alignment_test.h
|
||||||
@@ -548,6 +551,7 @@ if(FLATBUFFERS_BUILD_TESTS)
|
|||||||
# The flattest target needs some generated files
|
# The flattest target needs some generated files
|
||||||
SET(FLATC_OPT_COMP --cpp --gen-compare --gen-mutable --gen-object-api --reflect-names)
|
SET(FLATC_OPT_COMP --cpp --gen-compare --gen-mutable --gen-object-api --reflect-names)
|
||||||
SET(FLATC_OPT_SCOPED_ENUMS ${FLATC_OPT_COMP};--scoped-enums)
|
SET(FLATC_OPT_SCOPED_ENUMS ${FLATC_OPT_COMP};--scoped-enums)
|
||||||
|
SET(FLATC_OPT_CPP_VECTOR_TYPE ${FLATC_OPT_COMP};--cpp-include;test_vector_type.h;--cpp-vector-type;::flatbuffers::tests::CustomVector)
|
||||||
|
|
||||||
compile_schema_for_test(tests/alignment_test.fbs "${FLATC_OPT_COMP}")
|
compile_schema_for_test(tests/alignment_test.fbs "${FLATC_OPT_COMP}")
|
||||||
compile_schema_for_test_fbsh(tests/default_vectors_strings_test.fbs "${FLATC_OPT_COMP}")
|
compile_schema_for_test_fbsh(tests/default_vectors_strings_test.fbs "${FLATC_OPT_COMP}")
|
||||||
@@ -559,6 +563,7 @@ if(FLATBUFFERS_BUILD_TESTS)
|
|||||||
compile_schema_for_test(tests/64bit/evolution/v1.fbs "${FLATC_OPT_COMP}")
|
compile_schema_for_test(tests/64bit/evolution/v1.fbs "${FLATC_OPT_COMP}")
|
||||||
compile_schema_for_test(tests/64bit/evolution/v2.fbs "${FLATC_OPT_COMP}")
|
compile_schema_for_test(tests/64bit/evolution/v2.fbs "${FLATC_OPT_COMP}")
|
||||||
compile_schema_for_test(tests/union_underlying_type_test.fbs "${FLATC_OPT_SCOPED_ENUMS}")
|
compile_schema_for_test(tests/union_underlying_type_test.fbs "${FLATC_OPT_SCOPED_ENUMS}")
|
||||||
|
compile_schema_for_test(tests/cpp_vector_type.fbs "${FLATC_OPT_CPP_VECTOR_TYPE}")
|
||||||
|
|
||||||
if(FLATBUFFERS_CODE_SANITIZE)
|
if(FLATBUFFERS_CODE_SANITIZE)
|
||||||
add_fsanitize_to_target(flattests ${FLATBUFFERS_CODE_SANITIZE})
|
add_fsanitize_to_target(flattests ${FLATBUFFERS_CODE_SANITIZE})
|
||||||
|
|||||||
@@ -168,6 +168,12 @@ list of `FILES...`.
|
|||||||
std::string from Flatbuffers, but (char* + length). This allows efficient
|
std::string from Flatbuffers, but (char* + length). This allows efficient
|
||||||
construction of custom string types, including zero-copy construction.
|
construction of custom string types, including zero-copy construction.
|
||||||
|
|
||||||
|
- `--cpp-vector-type T` : Set object API vector type (default std::vector).
|
||||||
|
T must be a template taking a single element type argument and support
|
||||||
|
resize(), reserve(), size(), data(), operator[], emplace_back() and
|
||||||
|
begin()/end(), matching the subset of std::vector's interface generated
|
||||||
|
code relies on.
|
||||||
|
|
||||||
- `--no-cpp-direct-copy` : Don't generate direct copy methods for C++
|
- `--no-cpp-direct-copy` : Don't generate direct copy methods for C++
|
||||||
object-based API.
|
object-based API.
|
||||||
|
|
||||||
|
|||||||
@@ -337,6 +337,27 @@ constructor in the following format: `custom_str_class(const char *, size_t)`.
|
|||||||
Please note that the character array is not guaranteed to be NULL terminated,
|
Please note that the character array is not guaranteed to be NULL terminated,
|
||||||
you should always use the provided size to determine end of string.
|
you should always use the provided size to determine end of string.
|
||||||
|
|
||||||
|
## Using different vector type
|
||||||
|
|
||||||
|
By default the object tree's vector fields are built out of `std::vector`,
|
||||||
|
but you can influence this either globally (using the `--cpp-vector-type`
|
||||||
|
argument to `flatc`) or per field using the `cpp_vector_type` attribute, to
|
||||||
|
use any other vector-like template type (e.g. `eastl::vector`).
|
||||||
|
|
||||||
|
The type must be a template taking a single element type argument
|
||||||
|
(`my_vector<T>`), and must support the following member functions:
|
||||||
|
`resize()`, `reserve()`, `size()`, `data()`, `operator[]`, `emplace_back()`,
|
||||||
|
and `begin()`/`end()`. This matches the subset of `std::vector`'s interface
|
||||||
|
that generated code relies on.
|
||||||
|
|
||||||
|
Note that unlike `std::vector<bool>`, the custom vector type must not use a
|
||||||
|
bit-packed specialization for `bool` elements, since generated code accesses
|
||||||
|
`data()` as a contiguous `bool` array.
|
||||||
|
|
||||||
|
As with custom string types, the header defining the custom vector type is
|
||||||
|
not automatically included; use `--cpp-include` to add the necessary
|
||||||
|
`#include`.
|
||||||
|
|
||||||
## Reflection (& Resizing)
|
## Reflection (& Resizing)
|
||||||
|
|
||||||
There is experimental support for reflection in FlatBuffers, allowing you to
|
There is experimental support for reflection in FlatBuffers, allowing you to
|
||||||
|
|||||||
@@ -677,6 +677,7 @@ struct IDLOptions {
|
|||||||
std::string cpp_object_api_pointer_type;
|
std::string cpp_object_api_pointer_type;
|
||||||
std::string cpp_object_api_string_type;
|
std::string cpp_object_api_string_type;
|
||||||
bool cpp_object_api_string_flexible_constructor;
|
bool cpp_object_api_string_flexible_constructor;
|
||||||
|
std::string cpp_object_api_vector_type;
|
||||||
CaseStyle cpp_object_api_field_case_style;
|
CaseStyle cpp_object_api_field_case_style;
|
||||||
bool cpp_direct_copy;
|
bool cpp_direct_copy;
|
||||||
bool gen_nullable;
|
bool gen_nullable;
|
||||||
@@ -1012,6 +1013,7 @@ class Parser : public ParserState {
|
|||||||
known_attributes_["cpp_ptr_type_get"] = true;
|
known_attributes_["cpp_ptr_type_get"] = true;
|
||||||
known_attributes_["cpp_str_type"] = true;
|
known_attributes_["cpp_str_type"] = true;
|
||||||
known_attributes_["cpp_str_flex_ctor"] = true;
|
known_attributes_["cpp_str_flex_ctor"] = true;
|
||||||
|
known_attributes_["cpp_vector_type"] = true;
|
||||||
known_attributes_["native_inline"] = true;
|
known_attributes_["native_inline"] = true;
|
||||||
known_attributes_["native_custom_alloc"] = true;
|
known_attributes_["native_custom_alloc"] = true;
|
||||||
known_attributes_["native_type"] = true;
|
known_attributes_["native_type"] = true;
|
||||||
|
|||||||
@@ -251,6 +251,21 @@ flatc(
|
|||||||
schema="vector_table_naked_ptr.fbs",
|
schema="vector_table_naked_ptr.fbs",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
flatc(
|
||||||
|
[
|
||||||
|
"--cpp",
|
||||||
|
"--gen-compare",
|
||||||
|
"--gen-mutable",
|
||||||
|
"--gen-object-api",
|
||||||
|
"--reflect-names",
|
||||||
|
"--cpp-include",
|
||||||
|
"test_vector_type.h",
|
||||||
|
"--cpp-vector-type",
|
||||||
|
"::flatbuffers::tests::CustomVector",
|
||||||
|
],
|
||||||
|
schema="cpp_vector_type.fbs",
|
||||||
|
)
|
||||||
|
|
||||||
flatc(
|
flatc(
|
||||||
BASE_OPTS + CPP_OPTS + CS_OPTS + JAVA_OPTS + KOTLIN_OPTS + PHP_OPTS,
|
BASE_OPTS + CPP_OPTS + CS_OPTS + JAVA_OPTS + KOTLIN_OPTS + PHP_OPTS,
|
||||||
prefix="union_vector",
|
prefix="union_vector",
|
||||||
|
|||||||
@@ -145,6 +145,12 @@ const static FlatCOption flatc_options[] = {
|
|||||||
{"", "cpp-str-flex-ctor", "",
|
{"", "cpp-str-flex-ctor", "",
|
||||||
"Don't construct custom string types by passing std::string from "
|
"Don't construct custom string types by passing std::string from "
|
||||||
"Flatbuffers, but (char* + length)."},
|
"Flatbuffers, but (char* + length)."},
|
||||||
|
{"", "cpp-vector-type", "T",
|
||||||
|
"Set object API vector type (default std::vector). T must be a "
|
||||||
|
"template taking a single element type argument and support "
|
||||||
|
"resize(), reserve(), size(), data(), operator[], emplace_back(), and "
|
||||||
|
"begin()/end(). The custom type also needs its own header to be "
|
||||||
|
"included via --cpp-include."},
|
||||||
{"", "cpp-field-case-style", "STYLE",
|
{"", "cpp-field-case-style", "STYLE",
|
||||||
"Generate C++ fields using selected case style. Supported STYLE values: * "
|
"Generate C++ fields using selected case style. Supported STYLE values: * "
|
||||||
"'unchanged' - leave unchanged (default) * 'upper' - schema snake_case "
|
"'unchanged' - leave unchanged (default) * 'upper' - schema snake_case "
|
||||||
@@ -545,6 +551,9 @@ FlatCOptions FlatCompiler::ParseFromCommandLineArguments(int argc,
|
|||||||
opts.cpp_object_api_string_type = argv[argi];
|
opts.cpp_object_api_string_type = argv[argi];
|
||||||
} else if (arg == "--cpp-str-flex-ctor") {
|
} else if (arg == "--cpp-str-flex-ctor") {
|
||||||
opts.cpp_object_api_string_flexible_constructor = true;
|
opts.cpp_object_api_string_flexible_constructor = true;
|
||||||
|
} else if (arg == "--cpp-vector-type") {
|
||||||
|
if (++argi >= argc) Error("missing type following: " + arg, true);
|
||||||
|
opts.cpp_object_api_vector_type = argv[argi];
|
||||||
} else if (arg == "--no-cpp-direct-copy") {
|
} else if (arg == "--no-cpp-direct-copy") {
|
||||||
opts.cpp_direct_copy = false;
|
opts.cpp_direct_copy = false;
|
||||||
} else if (arg == "--cpp-field-case-style") {
|
} else if (arg == "--cpp-field-case-style") {
|
||||||
|
|||||||
+93
-11
@@ -937,6 +937,23 @@ class CppGenerator : public BaseGenerator {
|
|||||||
"std::string"; // Only for custom string types.
|
"std::string"; // Only for custom string types.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string NativeVectorType(const FieldDef* field) {
|
||||||
|
auto attr = field ? field->attributes.Lookup("cpp_vector_type") : nullptr;
|
||||||
|
auto& ret = attr ? attr->constant : opts_.cpp_object_api_vector_type;
|
||||||
|
if (ret.empty()) {
|
||||||
|
return "std::vector";
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if this field's native vector uses a custom (non-std::vector)
|
||||||
|
// container, meaning generated Pack() code can't rely on the std::vector
|
||||||
|
// specific CreateVector*() overloads and must fall back to data()/size()
|
||||||
|
// or per-element construction instead.
|
||||||
|
bool UsesCustomVectorType(const FieldDef* field) {
|
||||||
|
return NativeVectorType(field) != "std::vector";
|
||||||
|
}
|
||||||
|
|
||||||
std::string GenTypeNativePtr(const std::string& type, const FieldDef* field,
|
std::string GenTypeNativePtr(const std::string& type, const FieldDef* field,
|
||||||
bool is_constructor) {
|
bool is_constructor) {
|
||||||
auto& ptr_type = PtrType(field);
|
auto& ptr_type = PtrType(field);
|
||||||
@@ -974,14 +991,15 @@ class CppGenerator : public BaseGenerator {
|
|||||||
case BASE_TYPE_VECTOR64:
|
case BASE_TYPE_VECTOR64:
|
||||||
case BASE_TYPE_VECTOR: {
|
case BASE_TYPE_VECTOR: {
|
||||||
const auto type_name = GenTypeNative(type.VectorType(), true, field);
|
const auto type_name = GenTypeNative(type.VectorType(), true, field);
|
||||||
if (type.struct_def &&
|
const auto vector_type = NativeVectorType(&field);
|
||||||
|
if (vector_type == "std::vector" && type.struct_def &&
|
||||||
type.struct_def->attributes.Lookup("native_custom_alloc")) {
|
type.struct_def->attributes.Lookup("native_custom_alloc")) {
|
||||||
auto native_custom_alloc =
|
auto native_custom_alloc =
|
||||||
type.struct_def->attributes.Lookup("native_custom_alloc");
|
type.struct_def->attributes.Lookup("native_custom_alloc");
|
||||||
return "std::vector<" + type_name + "," +
|
return "std::vector<" + type_name + "," +
|
||||||
native_custom_alloc->constant + "<" + type_name + ">>";
|
native_custom_alloc->constant + "<" + type_name + ">>";
|
||||||
} else {
|
} else {
|
||||||
return "std::vector<" + type_name + ">";
|
return vector_type + "<" + type_name + ">";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case BASE_TYPE_STRUCT: {
|
case BASE_TYPE_STRUCT: {
|
||||||
@@ -1979,7 +1997,7 @@ class CppGenerator : public BaseGenerator {
|
|||||||
const std::string& full_type =
|
const std::string& full_type =
|
||||||
(cpp_type
|
(cpp_type
|
||||||
? (IsVector(field.value.type)
|
? (IsVector(field.value.type)
|
||||||
? "std::vector<" +
|
? NativeVectorType(&field) + "<" +
|
||||||
GenTypeNativePtr(cpp_type->constant, &field,
|
GenTypeNativePtr(cpp_type->constant, &field,
|
||||||
false) +
|
false) +
|
||||||
"> "
|
"> "
|
||||||
@@ -3772,13 +3790,22 @@ class CppGenerator : public BaseGenerator {
|
|||||||
case BASE_TYPE_VECTOR64:
|
case BASE_TYPE_VECTOR64:
|
||||||
case BASE_TYPE_VECTOR: {
|
case BASE_TYPE_VECTOR: {
|
||||||
auto vector_type = field.value.type.VectorType();
|
auto vector_type = field.value.type.VectorType();
|
||||||
|
// If the field's native container isn't std::vector, the
|
||||||
|
// std::vector-specific CreateVector*() overloads in
|
||||||
|
// flatbuffer_builder.h can't be used directly. Fall back to
|
||||||
|
// data()/size()-based overloads (which only require the container to
|
||||||
|
// support those, like std::vector does) or, where no such overload
|
||||||
|
// exists, to the same per-element lambda serialization already used
|
||||||
|
// above for custom string/table/union element types.
|
||||||
|
const bool custom_vector = UsesCustomVectorType(&field);
|
||||||
switch (vector_type.base_type) {
|
switch (vector_type.base_type) {
|
||||||
case BASE_TYPE_STRING: {
|
case BASE_TYPE_STRING: {
|
||||||
if (NativeString(&field) == "std::string") {
|
if (!custom_vector && NativeString(&field) == "std::string") {
|
||||||
code += "_fbb.CreateVectorOfStrings(" + value + ")";
|
code += "_fbb.CreateVectorOfStrings(" + value + ")";
|
||||||
} else {
|
} else {
|
||||||
// Use by-function serialization to emulate
|
// Use by-function serialization to emulate
|
||||||
// CreateVectorOfStrings(); this works also with non-std strings.
|
// CreateVectorOfStrings(); this works also with non-std strings
|
||||||
|
// and non-std::vector vector types.
|
||||||
code +=
|
code +=
|
||||||
"_fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::"
|
"_fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::"
|
||||||
"String>>"
|
"String>>"
|
||||||
@@ -3800,13 +3827,31 @@ class CppGenerator : public BaseGenerator {
|
|||||||
code += "_fbb.CreateVectorOfNativeStructs<";
|
code += "_fbb.CreateVectorOfNativeStructs<";
|
||||||
code += WrapInNameSpace(*vector_type.struct_def) + ", " +
|
code += WrapInNameSpace(*vector_type.struct_def) + ", " +
|
||||||
native_type->constant + ">";
|
native_type->constant + ">";
|
||||||
code += "(" + value;
|
if (custom_vector) {
|
||||||
|
code += "(" + value + ".data(), " + value + ".size()";
|
||||||
|
} else {
|
||||||
|
code += "(" + value;
|
||||||
|
}
|
||||||
const auto pack_name =
|
const auto pack_name =
|
||||||
struct_attrs.Lookup("native_type_pack_name");
|
struct_attrs.Lookup("native_type_pack_name");
|
||||||
if (pack_name) {
|
if (pack_name) {
|
||||||
code += ", ::flatbuffers::Pack" + pack_name->constant;
|
code += ", ::flatbuffers::Pack" + pack_name->constant;
|
||||||
}
|
}
|
||||||
code += ")";
|
code += ")";
|
||||||
|
} else if (custom_vector &&
|
||||||
|
(field.value.type.base_type == BASE_TYPE_VECTOR64 ||
|
||||||
|
field.offset64)) {
|
||||||
|
// CreateVectorOfStructs64() (and CreateVectorOfStructs64<V>()
|
||||||
|
// for offset64) only accept a std::vector; use the
|
||||||
|
// equivalent explicit-template raw pointer overload instead.
|
||||||
|
const auto struct_type = WrapInNameSpace(*vector_type.struct_def);
|
||||||
|
const auto vector_t = field.value.type.base_type ==
|
||||||
|
BASE_TYPE_VECTOR64
|
||||||
|
? "::flatbuffers::Vector64"
|
||||||
|
: "::flatbuffers::Vector";
|
||||||
|
code += "_fbb.CreateVectorOfStructs<" + struct_type +
|
||||||
|
", ::flatbuffers::Offset64, " + vector_t + ">(" +
|
||||||
|
value + ".data(), " + value + ".size())";
|
||||||
} else {
|
} else {
|
||||||
// If the field uses 64-bit addressing, create a 64-bit vector.
|
// If the field uses 64-bit addressing, create a 64-bit vector.
|
||||||
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
||||||
@@ -3818,7 +3863,11 @@ class CppGenerator : public BaseGenerator {
|
|||||||
code += "64<::flatbuffers::Vector>";
|
code += "64<::flatbuffers::Vector>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
code += "(" + value + ")";
|
if (custom_vector) {
|
||||||
|
code += "(" + value + ".data(), " + value + ".size())";
|
||||||
|
} else {
|
||||||
|
code += "(" + value + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
code += "_fbb.CreateVector<::flatbuffers::Offset<";
|
code += "_fbb.CreateVector<::flatbuffers::Offset<";
|
||||||
@@ -3837,7 +3886,17 @@ class CppGenerator : public BaseGenerator {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BASE_TYPE_BOOL: {
|
case BASE_TYPE_BOOL: {
|
||||||
code += "_fbb.CreateVector(" + value + ")";
|
if (custom_vector) {
|
||||||
|
// Vectors of bool are always stored on the wire as uint8_t
|
||||||
|
// (there is no Vector<bool>); CreateVectorScalarCast() does
|
||||||
|
// the per-element bool->uint8_t cast from a raw data()/size()
|
||||||
|
// pointer pair, avoiding any dependency on std::vector<bool>'s
|
||||||
|
// bit-packed specialization.
|
||||||
|
code += "_fbb.CreateVectorScalarCast<uint8_t>(" + value +
|
||||||
|
".data(), " + value + ".size())";
|
||||||
|
} else {
|
||||||
|
code += "_fbb.CreateVector(" + value + ")";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BASE_TYPE_UNION: {
|
case BASE_TYPE_UNION: {
|
||||||
@@ -3873,9 +3932,11 @@ class CppGenerator : public BaseGenerator {
|
|||||||
// the underlying storage type (eg. uint8_t).
|
// the underlying storage type (eg. uint8_t).
|
||||||
const auto basetype = GenTypeBasic(
|
const auto basetype = GenTypeBasic(
|
||||||
field.value.type.enum_def->underlying_type, false);
|
field.value.type.enum_def->underlying_type, false);
|
||||||
code += "_fbb.CreateVectorScalarCast<" + basetype +
|
const std::string data_ptr =
|
||||||
">(::flatbuffers::data(" + value + "), " + value +
|
custom_vector ? value + ".data()"
|
||||||
".size())";
|
: "::flatbuffers::data(" + value + ")";
|
||||||
|
code += "_fbb.CreateVectorScalarCast<" + basetype + ">(" +
|
||||||
|
data_ptr + ", " + value + ".size())";
|
||||||
} else if (field.attributes.Lookup("cpp_type")) {
|
} else if (field.attributes.Lookup("cpp_type")) {
|
||||||
auto type = GenTypeBasic(vector_type, false);
|
auto type = GenTypeBasic(vector_type, false);
|
||||||
code += "_fbb.CreateVector<" + type + ">(" + value + ".size(), ";
|
code += "_fbb.CreateVector<" + type + ">(" + value + ".size(), ";
|
||||||
@@ -3884,6 +3945,27 @@ class CppGenerator : public BaseGenerator {
|
|||||||
code += "static_cast<" + type + ">((*__va->__rehasher)";
|
code += "static_cast<" + type + ">((*__va->__rehasher)";
|
||||||
code += "(__va->_" + value + "[i]" + GenPtrGet(field) + ")) : 0";
|
code += "(__va->_" + value + "[i]" + GenPtrGet(field) + ")) : 0";
|
||||||
code += "; }, &_va )";
|
code += "; }, &_va )";
|
||||||
|
} else if (custom_vector) {
|
||||||
|
// Explicitly specify the element type so it can be deduced
|
||||||
|
// from a raw data()/size() pointer pair instead of relying on
|
||||||
|
// the std::vector<T, Alloc>-specific CreateVector overloads.
|
||||||
|
// Use the user-facing type (matches what GenTypeNative()
|
||||||
|
// stores in the native vector, e.g. the enum type itself for
|
||||||
|
// scoped-enum fields) so it matches data()'s pointee type.
|
||||||
|
auto type = GenTypeBasic(vector_type, true);
|
||||||
|
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
||||||
|
code += "_fbb.CreateVector<" + type +
|
||||||
|
", ::flatbuffers::Offset64, ::flatbuffers::Vector64>(" +
|
||||||
|
value + ".data(), " + value + ".size())";
|
||||||
|
} else if (field.offset64) {
|
||||||
|
// This is normal 32-bit vector, with 64-bit addressing.
|
||||||
|
code += "_fbb.CreateVector<" + type +
|
||||||
|
", ::flatbuffers::Offset64, ::flatbuffers::Vector>(" +
|
||||||
|
value + ".data(), " + value + ".size())";
|
||||||
|
} else {
|
||||||
|
code += "_fbb.CreateVector(" + value + ".data(), " + value +
|
||||||
|
".size())";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If the field uses 64-bit addressing, create a 64-bit vector.
|
// If the field uses 64-bit addressing, create a 64-bit vector.
|
||||||
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ cc_test(
|
|||||||
"vector_table_naked_ptr/vector_table_naked_ptr_generated.h",
|
"vector_table_naked_ptr/vector_table_naked_ptr_generated.h",
|
||||||
"vector_table_naked_ptr_test.h",
|
"vector_table_naked_ptr_test.h",
|
||||||
"vector_table_naked_ptr_test.cpp",
|
"vector_table_naked_ptr_test.cpp",
|
||||||
|
"test_vector_type.h",
|
||||||
|
"cpp_vector_type_test.h",
|
||||||
|
"cpp_vector_type_test.cpp",
|
||||||
],
|
],
|
||||||
copts = [
|
copts = [
|
||||||
"-DFLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE",
|
"-DFLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE",
|
||||||
@@ -136,6 +139,7 @@ cc_test(
|
|||||||
deps = [
|
deps = [
|
||||||
":alignment_test_cc_fbs",
|
":alignment_test_cc_fbs",
|
||||||
":arrays_test_cc_fbs",
|
":arrays_test_cc_fbs",
|
||||||
|
":cpp_vector_type_cc_fbs",
|
||||||
":default_vectors_strings_test_cc_fbs",
|
":default_vectors_strings_test_cc_fbs",
|
||||||
":monster_extra_cc_fbs",
|
":monster_extra_cc_fbs",
|
||||||
":monster_test_cc_fbs",
|
":monster_test_cc_fbs",
|
||||||
@@ -271,6 +275,19 @@ flatbuffer_cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
flatbuffer_cc_library(
|
||||||
|
name = "cpp_vector_type_cc_fbs",
|
||||||
|
srcs = ["cpp_vector_type.fbs"],
|
||||||
|
flatc_args = [
|
||||||
|
"--gen-compare",
|
||||||
|
"--gen-mutable",
|
||||||
|
"--gen-object-api",
|
||||||
|
"--reflect-names",
|
||||||
|
"--cpp-include test_vector_type.h",
|
||||||
|
"--cpp-vector-type ::flatbuffers::tests::CustomVector",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
flatbuffer_cc_library(
|
flatbuffer_cc_library(
|
||||||
name = "alignment_test_cc_fbs",
|
name = "alignment_test_cc_fbs",
|
||||||
srcs = ["alignment_test.fbs"],
|
srcs = ["alignment_test.fbs"],
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Schema used to test the --cpp-vector-type flatc option, which lets the
|
||||||
|
// generated Object API use a custom vector-like container (e.g.
|
||||||
|
// eastl::vector) instead of std::vector. See tests/test_vector_type.h for
|
||||||
|
// the stand-in container used by the test, and
|
||||||
|
// tests/cpp_vector_type_test.cpp for the test itself.
|
||||||
|
|
||||||
|
namespace CppVectorTypeNS;
|
||||||
|
|
||||||
|
enum CppVectorTypeColor : byte { Red = 0, Green, Blue }
|
||||||
|
|
||||||
|
struct CppVectorTypeVec2 {
|
||||||
|
x: float;
|
||||||
|
y: float;
|
||||||
|
}
|
||||||
|
|
||||||
|
table CppVectorTypeMonster {
|
||||||
|
id: int32;
|
||||||
|
}
|
||||||
|
|
||||||
|
table CppVectorTypeTest {
|
||||||
|
ints: [int32];
|
||||||
|
flags: [bool];
|
||||||
|
colors: [CppVectorTypeColor];
|
||||||
|
strings: [string];
|
||||||
|
positions: [CppVectorTypeVec2];
|
||||||
|
monsters: [CppVectorTypeMonster];
|
||||||
|
}
|
||||||
|
|
||||||
|
root_type CppVectorTypeTest;
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#include "cpp_vector_type_test.h"
|
||||||
|
|
||||||
|
#include "cpp_vector_type_generated.h"
|
||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
namespace flatbuffers {
|
||||||
|
namespace tests {
|
||||||
|
|
||||||
|
// Exercises the --cpp-vector-type flatc option (see
|
||||||
|
// tests/cpp_vector_type.fbs and tests/test_vector_type.h), which replaces
|
||||||
|
// std::vector with a custom vector-like container in the generated Object
|
||||||
|
// API. This covers every CreateVector*-family code path in Pack(): plain
|
||||||
|
// scalars, bool, enum-cast, vector-of-string, vector-of-struct and
|
||||||
|
// vector-of-table, then round-trips everything back through UnPackTo().
|
||||||
|
void CppVectorTypeTest() {
|
||||||
|
using CppVectorTypeNS::CppVectorTypeColor_Blue;
|
||||||
|
using CppVectorTypeNS::CppVectorTypeColor_Green;
|
||||||
|
using CppVectorTypeNS::CppVectorTypeColor_Red;
|
||||||
|
using CppVectorTypeNS::CppVectorTypeMonsterT;
|
||||||
|
using CppVectorTypeNS::CppVectorTypeTest;
|
||||||
|
using CppVectorTypeNS::CppVectorTypeTestT;
|
||||||
|
using CppVectorTypeNS::CppVectorTypeVec2;
|
||||||
|
|
||||||
|
// ---------------------------------------
|
||||||
|
// 1) Build a native object using the custom vector container for every
|
||||||
|
// vector field (scalars, bools, enums, strings, structs and tables).
|
||||||
|
// ---------------------------------------
|
||||||
|
CppVectorTypeTestT src;
|
||||||
|
src.ints.push_back(1);
|
||||||
|
src.ints.push_back(2);
|
||||||
|
src.ints.push_back(3);
|
||||||
|
|
||||||
|
src.flags.push_back(true);
|
||||||
|
src.flags.push_back(false);
|
||||||
|
src.flags.push_back(true);
|
||||||
|
|
||||||
|
src.colors.push_back(CppVectorTypeColor_Red);
|
||||||
|
src.colors.push_back(CppVectorTypeColor_Green);
|
||||||
|
src.colors.push_back(CppVectorTypeColor_Blue);
|
||||||
|
|
||||||
|
src.strings.push_back("hello");
|
||||||
|
src.strings.push_back("world");
|
||||||
|
|
||||||
|
src.positions.push_back(CppVectorTypeVec2(1.0f, 2.0f));
|
||||||
|
src.positions.push_back(CppVectorTypeVec2(3.0f, 4.0f));
|
||||||
|
|
||||||
|
src.monsters.emplace_back(new CppVectorTypeMonsterT());
|
||||||
|
src.monsters[0]->id = 111;
|
||||||
|
src.monsters.emplace_back(new CppVectorTypeMonsterT());
|
||||||
|
src.monsters[1]->id = 222;
|
||||||
|
|
||||||
|
// ---------------------------------------
|
||||||
|
// 2) Exercise the copy constructor / copy assignment operator generated
|
||||||
|
// for object-API types (--gen-compare requires these too), which rely
|
||||||
|
// on the custom vector's own copy ctor plus reserve()/emplace_back()
|
||||||
|
// for the vector-of-pointer (table) field.
|
||||||
|
// ---------------------------------------
|
||||||
|
CppVectorTypeTestT copy_of_src(src);
|
||||||
|
TEST_EQ(copy_of_src == src, true);
|
||||||
|
TEST_ASSERT(copy_of_src.monsters[0].get() != src.monsters[0].get());
|
||||||
|
TEST_EQ(copy_of_src.monsters[0]->id, 111);
|
||||||
|
|
||||||
|
// ---------------------------------------
|
||||||
|
// 3) Pack into a FlatBuffer and verify the wire-format contents.
|
||||||
|
// ---------------------------------------
|
||||||
|
flatbuffers::FlatBufferBuilder fbb;
|
||||||
|
fbb.Finish(CppVectorTypeTest::Pack(fbb, &src));
|
||||||
|
|
||||||
|
const auto* fb =
|
||||||
|
flatbuffers::GetRoot<CppVectorTypeTest>(fbb.GetBufferPointer());
|
||||||
|
|
||||||
|
TEST_EQ(fb->ints()->size(), 3u);
|
||||||
|
TEST_EQ(fb->ints()->Get(0), 1);
|
||||||
|
TEST_EQ(fb->ints()->Get(1), 2);
|
||||||
|
TEST_EQ(fb->ints()->Get(2), 3);
|
||||||
|
|
||||||
|
TEST_EQ(fb->flags()->size(), 3u);
|
||||||
|
TEST_EQ(fb->flags()->Get(0) != 0, true);
|
||||||
|
TEST_EQ(fb->flags()->Get(1) != 0, false);
|
||||||
|
TEST_EQ(fb->flags()->Get(2) != 0, true);
|
||||||
|
|
||||||
|
TEST_EQ(fb->colors()->size(), 3u);
|
||||||
|
TEST_EQ(fb->colors()->Get(0), CppVectorTypeColor_Red);
|
||||||
|
TEST_EQ(fb->colors()->Get(1), CppVectorTypeColor_Green);
|
||||||
|
TEST_EQ(fb->colors()->Get(2), CppVectorTypeColor_Blue);
|
||||||
|
|
||||||
|
TEST_EQ(fb->strings()->size(), 2u);
|
||||||
|
TEST_EQ_STR(fb->strings()->Get(0)->c_str(), "hello");
|
||||||
|
TEST_EQ_STR(fb->strings()->Get(1)->c_str(), "world");
|
||||||
|
|
||||||
|
TEST_EQ(fb->positions()->size(), 2u);
|
||||||
|
TEST_EQ(fb->positions()->Get(0)->x(), 1.0f);
|
||||||
|
TEST_EQ(fb->positions()->Get(0)->y(), 2.0f);
|
||||||
|
TEST_EQ(fb->positions()->Get(1)->x(), 3.0f);
|
||||||
|
TEST_EQ(fb->positions()->Get(1)->y(), 4.0f);
|
||||||
|
|
||||||
|
TEST_EQ(fb->monsters()->size(), 2u);
|
||||||
|
TEST_EQ(fb->monsters()->Get(0)->id(), 111);
|
||||||
|
TEST_EQ(fb->monsters()->Get(1)->id(), 222);
|
||||||
|
|
||||||
|
// ---------------------------------------
|
||||||
|
// 4) Unpack back into a fresh native object and verify a full round-trip
|
||||||
|
// through the custom vector container.
|
||||||
|
// ---------------------------------------
|
||||||
|
CppVectorTypeTestT dst;
|
||||||
|
fb->UnPackTo(&dst);
|
||||||
|
|
||||||
|
TEST_EQ(dst == src, true);
|
||||||
|
|
||||||
|
TEST_EQ(dst.ints.size(), 3u);
|
||||||
|
TEST_EQ(dst.ints[0], 1);
|
||||||
|
TEST_EQ(dst.ints[1], 2);
|
||||||
|
TEST_EQ(dst.ints[2], 3);
|
||||||
|
|
||||||
|
TEST_EQ(dst.flags.size(), 3u);
|
||||||
|
TEST_EQ(dst.flags[0], true);
|
||||||
|
TEST_EQ(dst.flags[1], false);
|
||||||
|
TEST_EQ(dst.flags[2], true);
|
||||||
|
|
||||||
|
TEST_EQ(dst.colors.size(), 3u);
|
||||||
|
TEST_EQ(dst.colors[0], CppVectorTypeColor_Red);
|
||||||
|
TEST_EQ(dst.colors[1], CppVectorTypeColor_Green);
|
||||||
|
TEST_EQ(dst.colors[2], CppVectorTypeColor_Blue);
|
||||||
|
|
||||||
|
TEST_EQ(dst.strings.size(), 2u);
|
||||||
|
TEST_EQ_STR(dst.strings[0].c_str(), "hello");
|
||||||
|
TEST_EQ_STR(dst.strings[1].c_str(), "world");
|
||||||
|
|
||||||
|
TEST_EQ(dst.positions.size(), 2u);
|
||||||
|
TEST_EQ(dst.positions[0].x(), 1.0f);
|
||||||
|
TEST_EQ(dst.positions[0].y(), 2.0f);
|
||||||
|
TEST_EQ(dst.positions[1].x(), 3.0f);
|
||||||
|
TEST_EQ(dst.positions[1].y(), 4.0f);
|
||||||
|
|
||||||
|
TEST_EQ(dst.monsters.size(), 2u);
|
||||||
|
TEST_ASSERT(dst.monsters[0] != nullptr);
|
||||||
|
TEST_ASSERT(dst.monsters[1] != nullptr);
|
||||||
|
TEST_EQ(dst.monsters[0]->id, 111);
|
||||||
|
TEST_EQ(dst.monsters[1]->id, 222);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tests
|
||||||
|
} // namespace flatbuffers
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef TESTS_CPP_VECTOR_TYPE_TEST_H
|
||||||
|
#define TESTS_CPP_VECTOR_TYPE_TEST_H
|
||||||
|
|
||||||
|
namespace flatbuffers {
|
||||||
|
namespace tests {
|
||||||
|
|
||||||
|
void CppVectorTypeTest();
|
||||||
|
|
||||||
|
} // namespace tests
|
||||||
|
} // namespace flatbuffers
|
||||||
|
|
||||||
|
#endif // TESTS_CPP_VECTOR_TYPE_TEST_H
|
||||||
@@ -66,6 +66,7 @@
|
|||||||
#include "native_type_test_generated.h"
|
#include "native_type_test_generated.h"
|
||||||
#include "test_assert.h"
|
#include "test_assert.h"
|
||||||
#include "util_test.h"
|
#include "util_test.h"
|
||||||
|
#include "cpp_vector_type_test.h"
|
||||||
#include "vector_table_naked_ptr_test.h"
|
#include "vector_table_naked_ptr_test.h"
|
||||||
|
|
||||||
void FlatBufferBuilderTest();
|
void FlatBufferBuilderTest();
|
||||||
@@ -1744,6 +1745,8 @@ int FlatBufferTests(const std::string& tests_data_path) {
|
|||||||
|
|
||||||
AlignmentTest();
|
AlignmentTest();
|
||||||
|
|
||||||
|
CppVectorTypeTest();
|
||||||
|
|
||||||
#ifndef FLATBUFFERS_NO_FILE_TESTS
|
#ifndef FLATBUFFERS_NO_FILE_TESTS
|
||||||
ParseAndGenerateTextTest(tests_data_path, false);
|
ParseAndGenerateTextTest(tests_data_path, false);
|
||||||
ParseAndGenerateTextTest(tests_data_path, true);
|
ParseAndGenerateTextTest(tests_data_path, true);
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#ifndef TESTS_TEST_VECTOR_TYPE_H_
|
||||||
|
#define TESTS_TEST_VECTOR_TYPE_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <new>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace flatbuffers {
|
||||||
|
namespace tests {
|
||||||
|
|
||||||
|
// A minimal stand-in for a custom vector-like container (e.g.
|
||||||
|
// eastl::vector), used to exercise the --cpp-vector-type flatc option.
|
||||||
|
//
|
||||||
|
// This is deliberately its own contiguous container rather than a wrapper
|
||||||
|
// around std::vector, so that:
|
||||||
|
// - generated code can't silently keep depending on any of the
|
||||||
|
// std::vector-specific overloads in flatbuffer_builder.h (e.g.
|
||||||
|
// CreateVector(const std::vector<T,Alloc>&)), and
|
||||||
|
// - CustomVector<bool> stores plain contiguous bools accessible via
|
||||||
|
// data(), unlike std::vector<bool>'s bit-packed specialization, matching
|
||||||
|
// how real alternative containers such as eastl::vector behave.
|
||||||
|
template<typename T>
|
||||||
|
class CustomVector {
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using iterator = T*;
|
||||||
|
using const_iterator = const T*;
|
||||||
|
|
||||||
|
CustomVector() = default;
|
||||||
|
CustomVector(const CustomVector& o) { assign_copy(o); }
|
||||||
|
CustomVector(CustomVector&& o) noexcept { steal(o); }
|
||||||
|
|
||||||
|
CustomVector& operator=(const CustomVector& o) {
|
||||||
|
if (this != &o) {
|
||||||
|
destroy_all();
|
||||||
|
deallocate();
|
||||||
|
assign_copy(o);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomVector& operator=(CustomVector&& o) noexcept {
|
||||||
|
if (this != &o) {
|
||||||
|
destroy_all();
|
||||||
|
deallocate();
|
||||||
|
steal(o);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~CustomVector() {
|
||||||
|
destroy_all();
|
||||||
|
deallocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(size_t n) {
|
||||||
|
if (n > capacity_) grow_to(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(size_t n) {
|
||||||
|
if (n > capacity_) grow_to(n);
|
||||||
|
for (size_t i = n; i < size_; ++i) data_[i].~T();
|
||||||
|
for (size_t i = size_; i < n; ++i) new (&data_[i]) T();
|
||||||
|
size_ = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const { return size_; }
|
||||||
|
bool empty() const { return size_ == 0; }
|
||||||
|
|
||||||
|
T* data() { return data_; }
|
||||||
|
const T* data() const { return data_; }
|
||||||
|
|
||||||
|
T& operator[](size_t i) { return data_[i]; }
|
||||||
|
const T& operator[](size_t i) const { return data_[i]; }
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void emplace_back(Args&&... args) {
|
||||||
|
if (size_ == capacity_) grow_to(capacity_ == 0 ? 1 : capacity_ * 2);
|
||||||
|
new (&data_[size_]) T(std::forward<Args>(args)...);
|
||||||
|
++size_;
|
||||||
|
}
|
||||||
|
void push_back(const T& v) { emplace_back(v); }
|
||||||
|
|
||||||
|
iterator begin() { return data_; }
|
||||||
|
iterator end() { return data_ + size_; }
|
||||||
|
const_iterator begin() const { return data_; }
|
||||||
|
const_iterator end() const { return data_ + size_; }
|
||||||
|
const_iterator cbegin() const { return data_; }
|
||||||
|
const_iterator cend() const { return data_ + size_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void grow_to(size_t n) {
|
||||||
|
T* new_data = static_cast<T*>(::operator new(n * sizeof(T)));
|
||||||
|
for (size_t i = 0; i < size_; ++i) {
|
||||||
|
new (&new_data[i]) T(std::move(data_[i]));
|
||||||
|
data_[i].~T();
|
||||||
|
}
|
||||||
|
::operator delete(data_);
|
||||||
|
data_ = new_data;
|
||||||
|
capacity_ = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign_copy(const CustomVector& o) {
|
||||||
|
data_ = o.size_ ? static_cast<T*>(::operator new(o.size_ * sizeof(T)))
|
||||||
|
: nullptr;
|
||||||
|
capacity_ = o.size_;
|
||||||
|
for (size_t i = 0; i < o.size_; ++i) new (&data_[i]) T(o.data_[i]);
|
||||||
|
size_ = o.size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void steal(CustomVector& o) {
|
||||||
|
data_ = o.data_;
|
||||||
|
size_ = o.size_;
|
||||||
|
capacity_ = o.capacity_;
|
||||||
|
o.data_ = nullptr;
|
||||||
|
o.size_ = o.capacity_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_all() {
|
||||||
|
for (size_t i = 0; i < size_; ++i) data_[i].~T();
|
||||||
|
size_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate() {
|
||||||
|
::operator delete(data_);
|
||||||
|
data_ = nullptr;
|
||||||
|
capacity_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* data_ = nullptr;
|
||||||
|
size_t size_ = 0;
|
||||||
|
size_t capacity_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool operator==(const CustomVector<T>& lhs, const CustomVector<T>& rhs) {
|
||||||
|
if (lhs.size() != rhs.size()) return false;
|
||||||
|
for (size_t i = 0; i < lhs.size(); ++i) {
|
||||||
|
if (!(lhs[i] == rhs[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool operator!=(const CustomVector<T>& lhs, const CustomVector<T>& rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tests
|
||||||
|
} // namespace flatbuffers
|
||||||
|
|
||||||
|
#endif // TESTS_TEST_VECTOR_TYPE_H_
|
||||||
Reference in New Issue
Block a user