vector-type

This commit is contained in:
2026-07-29 14:38:13 +02:00
parent 03fffb25e2
commit 81e5f093f9
13 changed files with 507 additions and 11 deletions
+5
View File
@@ -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})
+6
View File
@@ -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.
+21
View File
@@ -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
+2
View File
@@ -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;
+15
View File
@@ -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",
+9
View File
@@ -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") {
+90 -8
View File
@@ -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 + ">";
if (custom_vector) {
code += "(" + value + ".data(), " + value + ".size()";
} else {
code += "(" + value; 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,8 +3863,12 @@ class CppGenerator : public BaseGenerator {
code += "64<::flatbuffers::Vector>"; code += "64<::flatbuffers::Vector>";
} }
} }
if (custom_vector) {
code += "(" + value + ".data(), " + value + ".size())";
} else {
code += "(" + value + ")"; code += "(" + value + ")";
} }
}
} else { } else {
code += "_fbb.CreateVector<::flatbuffers::Offset<"; code += "_fbb.CreateVector<::flatbuffers::Offset<";
code += WrapInNameSpace(*vector_type.struct_def) + ">> "; code += WrapInNameSpace(*vector_type.struct_def) + ">> ";
@@ -3837,7 +3886,17 @@ class CppGenerator : public BaseGenerator {
break; break;
} }
case BASE_TYPE_BOOL: { case BASE_TYPE_BOOL: {
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 + ")"; 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) {
+17
View File
@@ -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"],
+29
View File
@@ -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;
+143
View File
@@ -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
+12
View File
@@ -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
+3
View File
@@ -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);
+152
View File
@@ -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_