diff options
-rw-r--r-- | source/10-common/RingBuffer.hpp | 133 |
1 files changed, 73 insertions, 60 deletions
diff --git a/source/10-common/RingBuffer.hpp b/source/10-common/RingBuffer.hpp index c4ff851..de8227c 100644 --- a/source/10-common/RingBuffer.hpp +++ b/source/10-common/RingBuffer.hpp @@ -5,66 +5,68 @@ #include <cstddef> #include <iterator> -template <typename T> -class RingBuffer { -public: - class Sentinel {}; - class Iterator { - public: - friend class RingBuffer; - using difference_type = ptrdiff_t; - using value_type = T; - using pointer = T*; - using reference = T&; - using iterator_category = std::random_access_iterator_tag; +class RingBufferSentinel {}; - private: - RingBuffer* mContainer; - size_t mCurr; - bool mNeedsWrapAround; - bool mHasWrapped = false; +template <typename TContainer> +class RingBufferIterator { +public: + using difference_type = TContainer::difference_type; + using value_type = TContainer::value_type; // C++20 relaxed usage requirements of `typename`, now locations where a type is required (like here in a using statement) it's no longer mandatory + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::random_access_iterator_tag; - public: - // NOTE: default constructed Iterator is in an undefined state +public: + TContainer* container; + TContainer::size_type curr; // C++20 relaxed usage requirements of `typename`, same here + bool needsWrapAround; + bool hasWrappedAround = false; - T& operator*() const { - return mContainer->mRing[mCurr]; - } +public: + reference operator*() const { + return container->mRing[curr]; + } - Iterator& operator++() { - assert(*this != Sentinel{}); - ++mCurr; - if (mNeedsWrapAround && mCurr == mContainer->mCapacity) { - mHasWrapped = true; - mCurr = 0; - } - return *this; + RingBufferIterator& operator++() { + assert(*this != RingBufferSentinel{}); + ++curr; + if (needsWrapAround && curr == container->mCapacity) { + hasWrappedAround = true; + curr = 0; } + return *this; + } - bool operator==(const Iterator& that) const { - assert(this->mContainer == that.mContainer); - return this->mCurr == that.mCurr; - } + bool operator==(const RingBufferIterator& that) const { + assert(this->container == that.container); + return this->curr == that.curr; + } - bool operator==(const Sentinel&) const { - return mCurr == mContainer->mTailIdx && (!mNeedsWrapAround || mHasWrapped); - } - }; + bool operator==(const RingBufferSentinel&) const { + return curr == container->mTailIdx && (!needsWrapAround || hasWrappedAround); + } +}; +template <typename T> +class RingBuffer { +public: using value_type = T; using reference = T&; using const_reference = const T&; - using iterator = Iterator; - // using const_iterator = void; // TODO + friend class RingBufferIterator<RingBuffer>; + using iterator = RingBufferIterator<RingBuffer>; + friend class RingBufferIterator<const RingBuffer>; + using const_iterator = RingBufferIterator<const RingBuffer>; + using sentinel = RingBufferSentinel; // Not a part of C++'s Container named requirements, added here for convenience using difference_type = ptrdiff_t; using size_type = size_t; private: T* mRing = nullptr; - size_t mHeadIdx = 0; - size_t mTailIdx = 0; - size_t mCapacity = 0; - size_t mSize = 0; + size_type mHeadIdx = 0; + size_type mTailIdx = 0; + size_type mCapacity = 0; + size_type mSize = 0; public: RingBuffer() noexcept = default; @@ -101,26 +103,37 @@ public: return *this; } - Iterator begin() { - Iterator it; - it.mContainer = this; - it.mCurr = mHeadIdx; - it.mNeedsWrapAround = mHeadIdx >= mTailIdx; - return it; + [[nodiscard]] iterator begin() { + return { + .container = this, + .curr = mHeadIdx, + .needsWrapAround = mHeadIdx >= mTailIdx, + }; } - Sentinel end() { - return Sentinel{}; + [[nodiscard]] const_iterator begin() const { return cbegin(); } + + // Same type for both const this and non-const `this` + [[nodiscard]] sentinel end() const { return sentinel{}; } + + [[nodiscard]] const_iterator cbegin() const { + return { + .container = this, + .curr = mHeadIdx, + .needsWrapAround = mHeadIdx >= mTailIdx, + }; } - const T& operator[](size_t i) const { - size_t idx = mHeadIdx + i; + [[nodiscard]] sentinel cend() const { return sentinel{}; } + + [[nodiscard]] T& operator[](size_type i) { return const_cast<T&>(const_cast<const RingBuffer&>(*this)[i]); } + [[nodiscard]] const T& operator[](size_type i) const { + size_type idx = mHeadIdx + i; if (idx >= mCapacity) { idx -= mCapacity; } return mRing[idx]; } - T& operator[](size_t i) { return const_cast<T&>(const_cast<const RingBuffer&>(*this)[i]); } void push_back(T t) { if (mTailIdx == mCapacity) { @@ -147,19 +160,19 @@ public: } } - [[nodiscard]] size_t capacity() const { + [[nodiscard]] size_type capacity() const { return mCapacity; } - [[nodiscard]] size_t size() const { + [[nodiscard]] size_type size() const { return mSize; } [[nodiscard]] T* GetBuffer() const { return mRing; } - [[nodiscard]] size_t GetHeadIdx() const { return mHeadIdx; } - [[nodiscard]] size_t GetTailIdx() const { return mTailIdx; } + [[nodiscard]] size_type GetHeadIdx() const { return mHeadIdx; } + [[nodiscard]] size_type GetTailIdx() const { return mTailIdx; } - void resize(size_t newCapacity) { + void resize(size_type newCapacity) { auto size = this->size(); auto oldRing = mRing; |