diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bf48e6ae..e41a13d5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 9e58d4be9..61e57f012 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -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; diff --git a/scripts/generate_code.py b/scripts/generate_code.py index 713b3109e..b95082a67 100755 --- a/scripts/generate_code.py +++ b/scripts/generate_code.py @@ -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", diff --git a/src/flatc.cpp b/src/flatc.cpp index ee34044fd..4b5c45415 100644 --- a/src/flatc.cpp +++ b/src/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") { diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index b836bfbcd..100564f1a 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -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 diff --git a/tests/cpp_vec_type_test.fbs b/tests/cpp_vec_type_test.fbs new file mode 100644 index 000000000..fd72e417a --- /dev/null +++ b/tests/cpp_vec_type_test.fbs @@ -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; diff --git a/tests/cpp_vec_type_test_impl.h b/tests/cpp_vec_type_test_impl.h new file mode 100644 index 000000000..6740877ad --- /dev/null +++ b/tests/cpp_vec_type_test_impl.h @@ -0,0 +1,15 @@ +#ifndef CPP_VEC_TYPE_TEST_IMPL_H +#define CPP_VEC_TYPE_TEST_IMPL_H + +#include + +namespace CppVecTest { + +template +struct CustomVec : public std::vector { + using std::vector::vector; +}; + +} // namespace CppVecTest + +#endif // CPP_VEC_TYPE_TEST_IMPL_H diff --git a/tests/test.cpp b/tests/test.cpp index 88aea9e10..14fbe1339 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -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>::value, + "values should be CustomVec"); + static_assert( + std::is_same>::value, + "regular should be std::vector"); + + CppVecTest::DataT src; + src.values.push_back(10); + src.values.push_back(20); + src.values.push_back(30); + + auto item = flatbuffers::unique_ptr(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();