#pragma once #include "glContainer.h" #include <cassert> #include <special_members.h> #include <utility> template<typename T> class InstanceVertices : protected glContainer<T> { using base = glContainer<T>; public: class [[nodiscard]] InstanceProxy { public: InstanceProxy(InstanceVertices * iv, std::size_t idx) : instances {iv}, index {idx} { } InstanceProxy(InstanceProxy && other) noexcept : instances {std::exchange(other.instances, nullptr)}, index {other.index} { } NO_COPY(InstanceProxy); ~InstanceProxy() { if (instances) { instances->release(index); } } InstanceProxy & operator=(InstanceProxy && other) noexcept { if (instances) { instances->release(index); } instances = std::exchange(other.instances, nullptr); index = other.index; return *this; } template<typename U> InstanceProxy & operator=(U && v) { instances->lookup(index) = std::forward<U>(v); return *this; } // NOLINTNEXTLINE)hicpp-explicit-conversions [[nodiscard]] operator T &() { return instances->lookup(index); } // NOLINTNEXTLINE)hicpp-explicit-conversions [[nodiscard]] operator const T &() const { return instances->lookup(index); } [[nodiscard]] T * get() { return &instances->lookup(index); } [[nodiscard]] const T * get() const { return &instances->lookup(index); } [[nodiscard]] T * operator->() { return get(); } [[nodiscard]] const T * operator->() const { return get(); } [[nodiscard]] T & operator*() { return instances->lookup(index); } [[nodiscard]] const T & operator*() const { return instances->lookup(index); } private: InstanceVertices<T> * instances; std::size_t index; }; template<typename... Params> [[nodiscard]] InstanceProxy acquire(Params &&... params) { if (!unused.empty()) { auto idx = unused.back(); unused.pop_back(); index[idx] = base::size(); reverseIndex.emplace_back(idx); base::emplace_back(std::forward<Params>(params)...); return InstanceProxy {this, idx}; } index.emplace_back(base::size()); reverseIndex.push_back(base::size()); base::emplace_back(std::forward<Params>(params)...); return InstanceProxy {this, index.size() - 1}; } using base::bufferName; [[nodiscard]] auto size() const { base::unmap(); return base::size(); } template<typename Pred> glContainer<T>::iterator partition(Pred pred) { return partition(base::begin(), base::end(), pred); } protected: static constexpr auto npos = static_cast<size_t>(-1); friend InstanceProxy; void release(const size_t pidx) { if (const size_t last = base::size() - 1; last != index[pidx]) { lookup(pidx) = std::move(base::back()); const auto movedKey = reverseIndex[last]; index[movedKey] = std::exchange(index[pidx], npos); reverseIndex[index[movedKey]] = movedKey; } base::pop_back(); reverseIndex.pop_back(); if (pidx == index.size() - 1) { index.pop_back(); } else { index[pidx] = npos; // Remember p.index is free index now unused.emplace_back(pidx); } } [[nodiscard]] T & lookup(size_t pindex) { return base::data()[index[pindex]]; } template<typename Pred> glContainer<T>::iterator partition(glContainer<T>::iterator first, glContainer<T>::iterator last, Pred pred) { while (first < last) { first = std::find_if_not(first, last, pred); last = --std::find_if(std::make_reverse_iterator(last), std::make_reverse_iterator(first), pred).base(); if (first < last) { std::iter_swap(first, last); const auto fidx = static_cast<size_t>(first - base::begin()), lidx = static_cast<size_t>(last - base::begin()); std::swap(index[reverseIndex[fidx]], index[reverseIndex[lidx]]); std::swap(reverseIndex[fidx], reverseIndex[lidx]); } } return first; } // Index into buffer given to nth proxy std::vector<size_t> index; std::vector<size_t> reverseIndex; // List of free spaces in index std::vector<size_t> unused; };