summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2023-05-01 16:03:49 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2023-05-01 16:03:49 +0100
commit10f9c0e3aac7a3b540ac34b4ff515954219599a5 (patch)
tree883bbdf352cb340e1f28f82084384f6e74fc7431
parentInitial commit of glContainer (diff)
downloadilt-10f9c0e3aac7a3b540ac34b4ff515954219599a5.tar.bz2
ilt-10f9c0e3aac7a3b540ac34b4ff515954219599a5.tar.xz
ilt-10f9c0e3aac7a3b540ac34b4ff515954219599a5.zip
Extend glContainer with most of the interface expected of an STL container
-rw-r--r--lib/glContainer.h149
-rw-r--r--test/test-glContainer.cpp117
2 files changed, 247 insertions, 19 deletions
diff --git a/lib/glContainer.h b/lib/glContainer.h
index 9e942b4..3dff6b1 100644
--- a/lib/glContainer.h
+++ b/lib/glContainer.h
@@ -8,6 +8,10 @@
template<typename I, typename Direction> class basic_glContainer_iterator {
public:
explicit basic_glContainer_iterator(I * i) : i {i} { }
+ template<typename OtherI>
+ basic_glContainer_iterator(const basic_glContainer_iterator<OtherI, Direction> & other) : i {&*other}
+ {
+ }
auto &
operator++() noexcept
@@ -43,6 +47,29 @@ public:
}
}
+ [[nodiscard]] auto
+ operator<(const basic_glContainer_iterator & other) const noexcept
+ {
+ if constexpr (std::is_same_v<Direction, std::plus<>>) {
+ return this->i < other.i;
+ }
+ else {
+ return other.i < this->i;
+ }
+ }
+
+ auto
+ operator+(std::integral auto n) const noexcept
+ {
+ return basic_glContainer_iterator<I, Direction> {Direction {}(i, n)};
+ }
+
+ auto
+ operator-(std::integral auto n) const noexcept
+ {
+ return basic_glContainer_iterator<I, Direction> {Direction {}(i, -n)};
+ }
+
[[nodiscard]] bool
operator==(const basic_glContainer_iterator & other) const noexcept
{
@@ -81,15 +108,18 @@ public:
using const_iterator = basic_glContainer_iterator<const value_type, std::plus<>>;
using reserve_iterator = basic_glContainer_iterator<value_type, std::minus<>>;
using const_reserve_iterator = basic_glContainer_iterator<const value_type, std::minus<>>;
+ static constexpr bool is_trivial_dest = std::is_trivially_destructible_v<T>;
glContainer()
{
allocBuffer(1);
}
- ~glContainer()
+ ~glContainer() noexcept(is_trivial_dest)
{
- clear();
+ if constexpr (!is_trivial_dest) {
+ clear();
+ }
}
[[nodiscard]] iterator
@@ -302,18 +332,25 @@ public:
return;
}
- const auto maintain = std::min(newSize, capacity_);
- std::vector<T> existing;
- const auto maintaind = static_cast<typename decltype(existing)::difference_type>(maintain);
- existing.reserve(maintain);
- map();
- std::move(data_, data_ + maintain, std::back_inserter(existing));
- for (auto uninitialised = data_ + newSize; uninitialised < data_ + size_; ++uninitialised) {
- uninitialised->~T();
+ if (const auto maintain = std::min(newSize, size_)) {
+ std::vector<T> existing;
+ const auto maintaind = static_cast<typename decltype(existing)::difference_type>(maintain);
+ existing.reserve(maintain);
+ map();
+ std::move(data_, data_ + maintain, std::back_inserter(existing));
+ if constexpr (!is_trivial_dest) {
+ for (auto uninitialised = data_ + newSize; uninitialised < data_ + size_; ++uninitialised) {
+ uninitialised->~T();
+ }
+ }
+ allocBuffer(newSize);
+ mapForAdd();
+ std::move(existing.begin(), existing.begin() + maintaind, data_);
+ }
+ else {
+ allocBuffer(newSize);
+ mapForAdd();
}
- allocBuffer(newSize);
- map();
- std::move(existing.begin(), existing.begin() + maintaind, data_);
for (auto uninitialised = data_ + size_; uninitialised < data_ + newSize; ++uninitialised) {
new (uninitialised) T {};
}
@@ -337,11 +374,14 @@ public:
}
void
- clear()
+ clear() noexcept(is_trivial_dest)
{
- std::for_each(begin(), end(), [](auto && v) {
- v.~T();
- });
+ if constexpr (!is_trivial_dest) {
+ map();
+ std::for_each(begin(), end(), [](auto && v) {
+ v.~T();
+ });
+ }
size_ = 0;
}
@@ -351,23 +391,86 @@ public:
{
auto newSize = size_ + 1;
reserve(newSize);
- map();
+ mapForAdd();
new (data_ + size_) T {std::forward<P>(ps)...};
size_ = newSize;
return back();
}
+ template<typename... P>
+ iterator
+ emplace(iterator pos, P &&... ps)
+ {
+ static_assert(std::is_nothrow_constructible_v<T, P...>);
+ auto newSize = size_ + 1;
+ const auto idx = pos - begin();
+ reserve(newSize);
+ mapForAdd();
+ std::move_backward(begin() + idx, end(), end() + 1);
+ (data_ + idx)->~T();
+ new (data_ + idx) T {std::forward<P>(ps)...};
+ size_ = newSize;
+ return pos;
+ }
+
reference_type
push_back(T p)
{
auto newSize = size_ + 1;
reserve(newSize);
- map();
+ mapForAdd();
new (data_ + size_) T {std::move(p)};
size_ = newSize;
return back();
}
+ iterator
+ insert(iterator pos, T p)
+ {
+ static_assert(std::is_nothrow_move_constructible_v<T>);
+ auto newSize = size_ + 1;
+ const auto idx = pos - begin();
+ reserve(newSize);
+ mapForAdd();
+ std::move_backward(begin() + idx, end(), end() + 1);
+ (data_ + idx)->~T();
+ new (data_ + idx) T {std::move(p)};
+ size_ = newSize;
+ return pos;
+ }
+
+ void
+ pop_back()
+ {
+ if constexpr (!is_trivial_dest) {
+ map();
+ data_[--size_].~T();
+ }
+ else {
+ --size_;
+ }
+ }
+
+ void
+ erase(iterator pos)
+ {
+ erase(pos, pos + 1);
+ }
+
+ void
+ erase(iterator pos, iterator to)
+ {
+ const auto eraseSize = to - pos;
+ map();
+ std::move(to, end(), pos);
+ if constexpr (!is_trivial_dest) {
+ std::for_each(end() - eraseSize, end(), [](auto && v) {
+ v.~T();
+ });
+ }
+ size_ -= static_cast<size_type>(eraseSize);
+ }
+
protected:
void
allocBuffer(size_type newCapacity)
@@ -385,6 +488,14 @@ protected:
void
map() const
{
+ if (size_ > 0) {
+ mapForAdd();
+ }
+ }
+
+ void
+ mapForAdd() const
+ {
if (!data_) {
data_ = static_cast<T *>(glMapNamedBuffer(buffer_, GL_READ_WRITE));
assert(data_);
diff --git a/test/test-glContainer.cpp b/test/test-glContainer.cpp
index efb8f71..5df56d4 100644
--- a/test/test-glContainer.cpp
+++ b/test/test-glContainer.cpp
@@ -22,6 +22,9 @@ BOOST_AUTO_TEST_CASE(createDestroy, *boost::unit_test::timeout(1))
{
BOOST_CHECK(!data_);
BOOST_CHECK_NO_THROW(map());
+ BOOST_REQUIRE(!data_);
+ BOOST_CHECK_NO_THROW(emplace_back(0));
+ BOOST_CHECK_NO_THROW(map());
BOOST_REQUIRE(data_);
BOOST_CHECK_NO_THROW(unmap());
BOOST_CHECK(!data_);
@@ -196,12 +199,78 @@ BOOST_AUTO_TEST_CASE(random_access)
BOOST_CHECK_EQUAL(1, *i);
}
+BOOST_AUTO_TEST_CASE(insert_remove_test)
+{
+ BOOST_CHECK_NO_THROW(emplace_back(1));
+ BOOST_CHECK_NO_THROW(emplace_back(2));
+ BOOST_CHECK_NO_THROW(emplace_back(3));
+ BOOST_CHECK_NO_THROW(emplace_back(4));
+ BOOST_CHECK_NO_THROW(pop_back());
+ BOOST_CHECK_EQUAL(size_, 3);
+ BOOST_CHECK_EQUAL(capacity_, 4);
+
+ BOOST_CHECK_NO_THROW(emplace(begin(), 5));
+ BOOST_CHECK_EQUAL(size_, 4);
+ BOOST_CHECK_EQUAL(capacity_, 4);
+ {
+ std::array expected1 {5, 1, 2, 3};
+ BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end());
+ }
+
+ {
+ std::array expected1 {2, 3};
+ BOOST_CHECK_EQUAL_COLLECTIONS(begin() + 2, end(), expected1.begin(), expected1.end());
+ }
+ BOOST_CHECK_NO_THROW(insert(begin() + 2, 6));
+ BOOST_CHECK_EQUAL(size_, 5);
+ BOOST_CHECK_EQUAL(capacity_, 5);
+ {
+ std::array expected1 {5, 1, 6, 2, 3};
+ BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end());
+ }
+ erase(begin() + 1);
+ BOOST_CHECK_EQUAL(size_, 4);
+ BOOST_CHECK_EQUAL(capacity_, 5);
+ {
+ std::array expected1 {5, 6, 2, 3};
+ BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end());
+ }
+ erase(begin() + 1, end() - 1);
+ BOOST_CHECK_EQUAL(size_, 2);
+ BOOST_CHECK_EQUAL(capacity_, 5);
+ {
+ std::array expected1 {5, 3};
+ BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(stl)
+{
+ BOOST_CHECK_NO_THROW(resize(10));
+ BOOST_CHECK_NO_THROW(std::generate(begin(), end(), [x = 0]() mutable {
+ return x++;
+ }));
+ BOOST_CHECK_NO_THROW(std::sort(rbegin(), rend()));
+ {
+ std::array expected1 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+ BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end());
+ }
+ const auto newend = std::remove_if(begin(), end(), [](const auto & x) {
+ return x % 2 == 0;
+ });
+ {
+ std::array expected1 {9, 7, 5, 3, 1};
+ BOOST_CHECK_EQUAL_COLLECTIONS(begin(), newend, expected1.begin(), expected1.end());
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END();
struct C {
int x;
float y;
};
+static_assert(std::is_trivially_destructible_v<C>);
BOOST_FIXTURE_TEST_SUITE(c, glContainer<C>)
@@ -221,3 +290,51 @@ BOOST_AUTO_TEST_CASE(basic)
}
BOOST_AUTO_TEST_SUITE_END();
+
+struct CC {
+ ~CC()
+ {
+ ++x;
+ }
+ int x;
+ float y;
+};
+static_assert(!std::is_trivially_destructible_v<CC>);
+
+BOOST_FIXTURE_TEST_SUITE(cc, glContainer<CC>)
+
+BOOST_AUTO_TEST_CASE(basic)
+{
+ BOOST_CHECK_NO_THROW(emplace_back(1, 2.f));
+ BOOST_CHECK_EQUAL(1, begin()->x);
+ BOOST_CHECK_EQUAL(2.f, begin()->y);
+ BOOST_CHECK_NO_THROW(begin()->x = 3);
+ BOOST_CHECK_EQUAL(3, begin()->x);
+
+ BOOST_CHECK_NO_THROW(push_back(CC {4, 5.f}));
+ BOOST_CHECK_EQUAL(3, begin()->x);
+ BOOST_CHECK_EQUAL(2.f, begin()->y);
+ BOOST_CHECK_EQUAL(4, rbegin()->x);
+ BOOST_CHECK_EQUAL(5.f, rbegin()->y);
+ BOOST_CHECK_NO_THROW(pop_back());
+ BOOST_CHECK_EQUAL(size(), 1);
+ BOOST_CHECK_EQUAL(capacity(), 2);
+ BOOST_CHECK_NO_THROW(resize(3));
+ BOOST_CHECK_EQUAL(size(), 3);
+ BOOST_CHECK_EQUAL(capacity(), 3);
+ BOOST_CHECK_NO_THROW(resize(1));
+ BOOST_CHECK_EQUAL(size(), 1);
+ BOOST_CHECK_EQUAL(capacity(), 1);
+}
+BOOST_AUTO_TEST_CASE(insert_remove_test)
+{
+ BOOST_CHECK_NO_THROW(emplace_back(1, 2.f));
+ BOOST_CHECK_NO_THROW(emplace_back(3, 4.f));
+ BOOST_CHECK_NO_THROW(emplace(begin(), 5, 6.f));
+ BOOST_CHECK_NO_THROW(emplace(begin() + 1, 7, 8.f));
+ BOOST_CHECK_NO_THROW(emplace(begin() + 2, 9, 10.f));
+ BOOST_CHECK_EQUAL(capacity(), 5);
+ BOOST_CHECK_EQUAL(size(), 5);
+}
+
+BOOST_AUTO_TEST_SUITE_END();