forked from BigfootDev/flatbuffers
Add cpp_vec_type attribute for object API vector customization
Mirrors cpp_str_type and cpp_ptr_type: a per-field cpp_vec_type attribute lets users substitute any std::vector-compatible container in generated T-structs, and --cpp-vec-type sets the global default. NativeVector() resolves the attribute (falling back to the global option, then std::vector). Changes applied to GenTypeNative, GenMember, GenParam (CreateDirect), and the CreateVectorOfStrings fast-path guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,7 @@ set(FlatBuffers_Tests_SRCS
|
||||
tests/vector_table_naked_ptr_test.cpp
|
||||
tests/native_type_test_impl.h
|
||||
tests/native_type_test_impl.cpp
|
||||
tests/cpp_vec_type_test_impl.h
|
||||
tests/alignment_test.h
|
||||
tests/alignment_test.cpp
|
||||
tests/64bit/offset64_test.h
|
||||
@@ -554,6 +555,7 @@ if(FLATBUFFERS_BUILD_TESTS)
|
||||
compile_schema_for_test(tests/arrays_test.fbs "${FLATC_OPT_SCOPED_ENUMS}")
|
||||
compile_schema_for_test(tests/native_inline_table_test.fbs "${FLATC_OPT_COMP}")
|
||||
compile_schema_for_test(tests/native_type_test.fbs "${FLATC_OPT_COMP}")
|
||||
compile_schema_for_test(tests/cpp_vec_type_test.fbs "${FLATC_OPT_COMP}")
|
||||
compile_schema_for_test(tests/key_field/key_field_sample.fbs "${FLATC_OPT_COMP}")
|
||||
compile_schema_for_test(tests/64bit/test_64bit.fbs "${FLATC_OPT_COMP};--bfbs-gen-embed")
|
||||
compile_schema_for_test(tests/64bit/evolution/v1.fbs "${FLATC_OPT_COMP}")
|
||||
|
||||
@@ -676,6 +676,7 @@ struct IDLOptions {
|
||||
bool gen_absl_hash;
|
||||
std::string cpp_object_api_pointer_type;
|
||||
std::string cpp_object_api_string_type;
|
||||
std::string cpp_object_api_vector_type;
|
||||
bool cpp_object_api_string_flexible_constructor;
|
||||
CaseStyle cpp_object_api_field_case_style;
|
||||
bool cpp_direct_copy;
|
||||
@@ -1012,6 +1013,7 @@ class Parser : public ParserState {
|
||||
known_attributes_["cpp_ptr_type_get"] = true;
|
||||
known_attributes_["cpp_str_type"] = true;
|
||||
known_attributes_["cpp_str_flex_ctor"] = true;
|
||||
known_attributes_["cpp_vec_type"] = true;
|
||||
known_attributes_["native_inline"] = true;
|
||||
known_attributes_["native_custom_alloc"] = true;
|
||||
known_attributes_["native_type"] = true;
|
||||
|
||||
@@ -383,6 +383,11 @@ flatc(
|
||||
schema="native_type_test.fbs",
|
||||
)
|
||||
|
||||
flatc(
|
||||
["--cpp", "--gen-compare", "--gen-mutable", "--gen-object-api", "--reflect-names"],
|
||||
schema="cpp_vec_type_test.fbs",
|
||||
)
|
||||
|
||||
flatc(
|
||||
[
|
||||
"--cpp",
|
||||
|
||||
@@ -142,6 +142,10 @@ const static FlatCOption flatc_options[] = {
|
||||
"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-vec-type", "T",
|
||||
"Set object API vector type (default std::vector). T must support the "
|
||||
"same interface as std::vector: size(), resize(), reserve(), "
|
||||
"emplace_back(), operator[], begin(), end(), and data()."},
|
||||
{"", "cpp-str-flex-ctor", "",
|
||||
"Don't construct custom string types by passing std::string from "
|
||||
"Flatbuffers, but (char* + length)."},
|
||||
@@ -543,6 +547,9 @@ FlatCOptions FlatCompiler::ParseFromCommandLineArguments(int argc,
|
||||
} 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-vec-type") {
|
||||
if (++argi >= argc) Error("missing type following: " + arg, true);
|
||||
opts.cpp_object_api_vector_type = argv[argi];
|
||||
} else if (arg == "--cpp-str-flex-ctor") {
|
||||
opts.cpp_object_api_string_flexible_constructor = true;
|
||||
} else if (arg == "--no-cpp-direct-copy") {
|
||||
|
||||
@@ -929,6 +929,15 @@ class CppGenerator : public BaseGenerator {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::string NativeVector(const FieldDef* field) {
|
||||
auto attr = field ? field->attributes.Lookup("cpp_vec_type") : nullptr;
|
||||
auto& ret = attr ? attr->constant : opts_.cpp_object_api_vector_type;
|
||||
if (ret.empty()) {
|
||||
return "std::vector";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FlexibleStringConstructor(const FieldDef* field) {
|
||||
auto attr = field != nullptr &&
|
||||
(field->attributes.Lookup("cpp_str_flex_ctor") != nullptr);
|
||||
@@ -974,15 +983,15 @@ class CppGenerator : public BaseGenerator {
|
||||
case BASE_TYPE_VECTOR64:
|
||||
case BASE_TYPE_VECTOR: {
|
||||
const auto type_name = GenTypeNative(type.VectorType(), true, field);
|
||||
if (type.struct_def &&
|
||||
const auto vec_type = NativeVector(&field);
|
||||
if (vec_type == "std::vector" && type.struct_def &&
|
||||
type.struct_def->attributes.Lookup("native_custom_alloc")) {
|
||||
auto native_custom_alloc =
|
||||
type.struct_def->attributes.Lookup("native_custom_alloc");
|
||||
return "std::vector<" + type_name + "," +
|
||||
native_custom_alloc->constant + "<" + type_name + ">>";
|
||||
} else {
|
||||
return "std::vector<" + type_name + ">";
|
||||
}
|
||||
return vec_type + "<" + type_name + ">";
|
||||
}
|
||||
case BASE_TYPE_STRUCT: {
|
||||
auto type_name = WrapInNameSpace(*type.struct_def);
|
||||
@@ -1957,9 +1966,10 @@ class CppGenerator : public BaseGenerator {
|
||||
field.offset64);
|
||||
}
|
||||
if (TypeHasKey(vtype)) {
|
||||
code_.SetValue("PARAM_TYPE", "std::vector<" + type + "> *");
|
||||
code_.SetValue("PARAM_TYPE", NativeVector(&field) + "<" + type + "> *");
|
||||
} else {
|
||||
code_.SetValue("PARAM_TYPE", "const std::vector<" + type + "> *");
|
||||
code_.SetValue("PARAM_TYPE",
|
||||
"const " + NativeVector(&field) + "<" + type + "> *");
|
||||
}
|
||||
code_.SetValue("PARAM_VALUE", "nullptr");
|
||||
} else {
|
||||
@@ -1985,7 +1995,7 @@ class CppGenerator : public BaseGenerator {
|
||||
const std::string& full_type =
|
||||
(cpp_type
|
||||
? (IsVector(field.value.type)
|
||||
? "std::vector<" +
|
||||
? NativeVector(&field) + "<" +
|
||||
GenTypeNativePtr(cpp_type->constant, &field,
|
||||
false) +
|
||||
"> "
|
||||
@@ -3791,7 +3801,8 @@ class CppGenerator : public BaseGenerator {
|
||||
auto vector_type = field.value.type.VectorType();
|
||||
switch (vector_type.base_type) {
|
||||
case BASE_TYPE_STRING: {
|
||||
if (NativeString(&field) == "std::string") {
|
||||
if (NativeString(&field) == "std::string" &&
|
||||
NativeVector(&field) == "std::vector") {
|
||||
code += "_fbb.CreateVectorOfStrings(" + value + ")";
|
||||
} else {
|
||||
// Use by-function serialization to emulate
|
||||
|
||||
17
tests/cpp_vec_type_test.fbs
Normal file
17
tests/cpp_vec_type_test.fbs
Normal file
@@ -0,0 +1,17 @@
|
||||
native_include "cpp_vec_type_test_impl.h";
|
||||
|
||||
namespace CppVecTest;
|
||||
|
||||
table Item {
|
||||
id: int;
|
||||
value: float;
|
||||
}
|
||||
|
||||
table Data {
|
||||
values: [int] (cpp_vec_type: "CppVecTest::CustomVec");
|
||||
items: [Item] (cpp_vec_type: "CppVecTest::CustomVec");
|
||||
strs: [string] (cpp_vec_type: "CppVecTest::CustomVec");
|
||||
regular: [int];
|
||||
}
|
||||
|
||||
root_type Data;
|
||||
15
tests/cpp_vec_type_test_impl.h
Normal file
15
tests/cpp_vec_type_test_impl.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef CPP_VEC_TYPE_TEST_IMPL_H
|
||||
#define CPP_VEC_TYPE_TEST_IMPL_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace CppVecTest {
|
||||
|
||||
template<typename T>
|
||||
struct CustomVec : public std::vector<T> {
|
||||
using std::vector<T>::vector;
|
||||
};
|
||||
|
||||
} // namespace CppVecTest
|
||||
|
||||
#endif // CPP_VEC_TYPE_TEST_IMPL_H
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "flexbuffers_test.h"
|
||||
#include "is_quiet_nan.h"
|
||||
#include "monster_test_bfbs_generated.h" // Generated using --bfbs-comments --bfbs-builtins --cpp --bfbs-gen-embed
|
||||
#include "cpp_vec_type_test_generated.h"
|
||||
#include "native_type_test_generated.h"
|
||||
#include "test_assert.h"
|
||||
#include "util_test.h"
|
||||
@@ -995,6 +996,51 @@ void NativeTypeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
void CppVecTypeTest() {
|
||||
static_assert(
|
||||
std::is_same<decltype(CppVecTest::DataT{}.values),
|
||||
CppVecTest::CustomVec<int32_t>>::value,
|
||||
"values should be CustomVec<int32_t>");
|
||||
static_assert(
|
||||
std::is_same<decltype(CppVecTest::DataT{}.regular),
|
||||
std::vector<int32_t>>::value,
|
||||
"regular should be std::vector<int32_t>");
|
||||
|
||||
CppVecTest::DataT src;
|
||||
src.values.push_back(10);
|
||||
src.values.push_back(20);
|
||||
src.values.push_back(30);
|
||||
|
||||
auto item = flatbuffers::unique_ptr<CppVecTest::ItemT>(new CppVecTest::ItemT());
|
||||
item->id = 42;
|
||||
item->value = 1.5f;
|
||||
src.items.push_back(std::move(item));
|
||||
|
||||
src.strs.push_back("hello");
|
||||
src.strs.push_back("world");
|
||||
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
fbb.Finish(CppVecTest::Data::Pack(fbb, &src));
|
||||
|
||||
auto dst = CppVecTest::UnPackData(fbb.GetBufferPointer());
|
||||
|
||||
TEST_EQ(dst->values.size(), 3U);
|
||||
TEST_EQ(dst->values[0], 10);
|
||||
TEST_EQ(dst->values[1], 20);
|
||||
TEST_EQ(dst->values[2], 30);
|
||||
|
||||
TEST_EQ(dst->items.size(), 1U);
|
||||
TEST_EQ(dst->items[0]->id, 42);
|
||||
TEST_EQ(dst->items[0]->value, 1.5f);
|
||||
|
||||
TEST_EQ(dst->strs.size(), 2U);
|
||||
TEST_EQ(dst->strs[0], std::string("hello"));
|
||||
TEST_EQ(dst->strs[1], std::string("world"));
|
||||
|
||||
TEST_EQ(dst->regular.size(), 0U);
|
||||
TEST_ASSERT(*dst == *dst);
|
||||
}
|
||||
|
||||
// Guard against -Wunused-function on platforms without file tests.
|
||||
#ifndef FLATBUFFERS_NO_FILE_TESTS
|
||||
// VS10 does not support typed enums, exclude from tests
|
||||
@@ -1830,6 +1876,7 @@ int FlatBufferTests(const std::string& tests_data_path) {
|
||||
InvalidFloatTest();
|
||||
FixedLengthArrayTest();
|
||||
NativeTypeTest();
|
||||
CppVecTypeTest();
|
||||
OptionalScalarsTest();
|
||||
ParseFlexbuffersFromJsonWithNullTest();
|
||||
FlatbuffersSpanTest();
|
||||
|
||||
Reference in New Issue
Block a user