forked from BigfootDev/flatbuffers
vector-type
This commit is contained in:
@@ -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"],
|
||||
|
||||
@@ -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 "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);
|
||||
|
||||
@@ -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