#pragma once #include "glArrays.h" #include #include #include #include template class basic_glContainer_iterator { public: explicit basic_glContainer_iterator(I * i) : i {i} { } template basic_glContainer_iterator(const basic_glContainer_iterator & other) : i {&*other} { } auto & operator++() noexcept { i = Direction {}(i, 1); return *this; } auto operator++(int) noexcept { return basic_glContainer_iterator {std::exchange(i, Direction {}(i, 1))}; } auto & operator--() noexcept { i = Direction {}(i, -1); return *this; } auto operator--(int) noexcept { return basic_glContainer_iterator {std::exchange(i, Direction {}(i, -1))}; } [[nodiscard]] auto operator-(const basic_glContainer_iterator & other) const noexcept { if constexpr (std::is_same_v>) { return this->i - other.i; } else { return other.i - this->i; } } [[nodiscard]] auto operator<(const basic_glContainer_iterator & other) const noexcept { if constexpr (std::is_same_v>) { return this->i < other.i; } else { return other.i < this->i; } } auto operator+(std::integral auto n) const noexcept { return basic_glContainer_iterator {Direction {}(i, n)}; } auto operator-(std::integral auto n) const noexcept { return basic_glContainer_iterator {Direction {}(i, -n)}; } [[nodiscard]] bool operator==(const basic_glContainer_iterator & other) const noexcept { return this->i == other.i; } [[nodiscard]] bool operator!=(const basic_glContainer_iterator & other) const noexcept { return this->i != other.i; } [[nodiscard]] auto operator->() const noexcept { return i; } [[nodiscard]] auto & operator*() const noexcept { return *i; } private: I * i; }; template class glContainer { public: using value_type = T; using reference_type = T &; using const_reference_type = const T &; using pointer_type = T *; using const_pointer_type = const T *; using size_type = std::size_t; using iterator = basic_glContainer_iterator>; using const_iterator = basic_glContainer_iterator>; using reserve_iterator = basic_glContainer_iterator>; using const_reserve_iterator = basic_glContainer_iterator>; static constexpr bool is_trivial_dest = std::is_trivially_destructible_v; glContainer() { allocBuffer(1); } ~glContainer() noexcept(is_trivial_dest) { if constexpr (!is_trivial_dest) { clear(); } } [[nodiscard]] iterator begin() { map(); return iterator {data_}; } [[nodiscard]] iterator end() { map(); return iterator {data_ + size_}; } [[nodiscard]] const_iterator begin() const { map(); return const_iterator {data_}; } [[nodiscard]] const_iterator end() const { map(); return const_iterator {data_ + size_}; } [[nodiscard]] const_iterator cbegin() const { map(); return const_iterator {data_}; } [[nodiscard]] const_iterator cend() const { map(); return const_iterator {data_ + size_}; } [[nodiscard]] reserve_iterator rbegin() { map(); return reserve_iterator {data_ + size_ - 1}; } [[nodiscard]] reserve_iterator rend() { map(); return reserve_iterator {data_ - 1}; } [[nodiscard]] const_reserve_iterator rbegin() const { map(); return const_reserve_iterator {data_ + size_ - 1}; } [[nodiscard]] const_reserve_iterator rend() const { map(); return const_reserve_iterator {data_ - 1}; } [[nodiscard]] const_reserve_iterator crbegin() const { map(); return const_reserve_iterator {data_ + size_ - 1}; } [[nodiscard]] const_reserve_iterator crend() const { map(); return const_reserve_iterator {data_ - 1}; } [[nodiscard]] const auto & bufferName() const { return buffer_; } [[nodiscard]] size_type size() const { return size_; } [[nodiscard]] reference_type at(size_type pos) { if (pos >= size()) { throw std::out_of_range {__FUNCTION__}; } map(); return data_[pos]; } [[nodiscard]] const_reference_type at(size_type pos) const { if (pos >= size()) { throw std::out_of_range {__FUNCTION__}; } map(); return data_[pos]; } [[nodiscard]] reference_type operator[](size_type pos) { map(); return data_[pos]; } [[nodiscard]] const_reference_type operator[](size_type pos) const { map(); return data_[pos]; } [[nodiscard]] pointer_type data() { map(); return data_; } [[nodiscard]] const_pointer_type data() const { map(); return data_; } [[nodiscard]] reference_type front() { map(); return *data_; } [[nodiscard]] reference_type back() { map(); return *(data_ + size_ - 1); } [[nodiscard]] const_reference_type front() const { map(); return *data_; } [[nodiscard]] const_reference_type back() const { map(); return *(data_ + size_ - 1); } [[nodiscard]] bool empty() const { return !size(); } [[nodiscard]] size_type capacity() const { return capacity_; } void unmap() const { if (data_) { glUnmapNamedBuffer(buffer_); data_ = nullptr; } } void reserve(size_type newCapacity) { if (newCapacity <= capacity_) { return; } newCapacity = std::max(newCapacity, capacity_ * 2); std::vector existing; existing.reserve(size_); map(); std::move(begin(), end(), std::back_inserter(existing)); allocBuffer(newCapacity); map(); std::move(existing.begin(), existing.end(), begin()); } void resize(size_type newSize) { if (newSize == size_) { return; } if (const auto maintain = std::min(newSize, size_)) { std::vector existing; const auto maintaind = static_cast(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(); } for (auto uninitialised = data_ + size_; uninitialised < data_ + newSize; ++uninitialised) { new (uninitialised) T {}; } size_ = newSize; } void shrink_to_fit() { if (capacity_ <= size_) { return; } std::vector existing; existing.reserve(size_); map(); std::move(begin(), end(), std::back_inserter(existing)); allocBuffer(size_); map(); std::move(existing.begin(), existing.end(), begin()); } void clear() noexcept(is_trivial_dest) { if constexpr (!is_trivial_dest) { map(); std::for_each(begin(), end(), [](auto && v) { v.~T(); }); } size_ = 0; } template reference_type emplace_back(P &&... ps) { auto newSize = size_ + 1; reserve(newSize); mapForAdd(); new (data_ + size_) T {std::forward

(ps)...}; size_ = newSize; return back(); } template iterator emplace(iterator pos, P &&... ps) { static_assert(std::is_nothrow_constructible_v); 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

(ps)...}; size_ = newSize; return pos; } reference_type push_back(T p) { auto newSize = size_ + 1; reserve(newSize); 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); 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(eraseSize); } protected: void allocBuffer(size_type newCapacity) { if (newCapacity == 0) { return allocBuffer(1); } glBindBuffer(GL_ARRAY_BUFFER, buffer_); glBufferData(GL_ARRAY_BUFFER, static_cast(sizeof(T) * newCapacity), nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); capacity_ = newCapacity; data_ = nullptr; } void map() const { if (size_ > 0) { mapForAdd(); } } void mapForAdd() const { if (!data_) { data_ = static_cast(glMapNamedBuffer(buffer_, GL_READ_WRITE)); assert(data_); } } glBuffer buffer_; std::size_t capacity_ {}; std::size_t size_ {}; mutable T * data_ {}; }; template struct std::iterator_traits> { using difference_type = ssize_t; using value_type = T; using pointer = T *; using reference = T &; using iterator_category = std::random_access_iterator_tag; };