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
+17
View File
@@ -78,6 +78,9 @@ cc_test(
"vector_table_naked_ptr/vector_table_naked_ptr_generated.h",
"vector_table_naked_ptr_test.h",
"vector_table_naked_ptr_test.cpp",
"test_vector_type.h",
"cpp_vector_type_test.h",
"cpp_vector_type_test.cpp",
],
copts = [
"-DFLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE",
@@ -136,6 +139,7 @@ cc_test(
deps = [
":alignment_test_cc_fbs",
":arrays_test_cc_fbs",
":cpp_vector_type_cc_fbs",
":default_vectors_strings_test_cc_fbs",
":monster_extra_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(
name = "alignment_test_cc_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 "test_assert.h"
#include "util_test.h"
#include "cpp_vector_type_test.h"
#include "vector_table_naked_ptr_test.h"
void FlatBufferBuilderTest();
@@ -1744,6 +1745,8 @@ int FlatBufferTests(const std::string& tests_data_path) {
AlignmentTest();
CppVectorTypeTest();
#ifndef FLATBUFFERS_NO_FILE_TESTS
ParseAndGenerateTextTest(tests_data_path, false);
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_