From 32e74b9c81b57a9c0284f5374536439d581bc4b6 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Sun, 15 Jan 2023 14:47:54 -0800 Subject: Changeset: 94 Improve RingBuffer - Implement const_iterator - Add previously missing standard named requirements --- source/10-common/RingBuffer.hpp | 137 ++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 62 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 #include -template -class RingBuffer { +class RingBufferSentinel {}; + +template +class RingBufferIterator { 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; - - private: - RingBuffer* mContainer; - size_t mCurr; - bool mNeedsWrapAround; - bool mHasWrapped = false; - - public: - // NOTE: default constructed Iterator is in an undefined state - - T& operator*() const { - return mContainer->mRing[mCurr]; - } + 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; - Iterator& operator++() { - assert(*this != Sentinel{}); - ++mCurr; - if (mNeedsWrapAround && mCurr == mContainer->mCapacity) { - mHasWrapped = true; - mCurr = 0; - } - return *this; - } +public: + TContainer* container; + TContainer::size_type curr; // C++20 relaxed usage requirements of `typename`, same here + bool needsWrapAround; + bool hasWrappedAround = false; - bool operator==(const Iterator& that) const { - assert(this->mContainer == that.mContainer); - return this->mCurr == that.mCurr; - } +public: + reference operator*() const { + return container->mRing[curr]; + } - bool operator==(const Sentinel&) const { - return mCurr == mContainer->mTailIdx && (!mNeedsWrapAround || mHasWrapped); + RingBufferIterator& operator++() { + assert(*this != RingBufferSentinel{}); + ++curr; + if (needsWrapAround && curr == container->mCapacity) { + hasWrappedAround = true; + curr = 0; } - }; + return *this; + } + bool operator==(const RingBufferIterator& that) const { + assert(this->container == that.container); + return this->curr == that.curr; + } + + bool operator==(const RingBufferSentinel&) const { + return curr == container->mTailIdx && (!needsWrapAround || hasWrappedAround); + } +}; + +template +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; + using iterator = RingBufferIterator; + friend class RingBufferIterator; + using const_iterator = RingBufferIterator; + 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(const_cast(*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(const_cast(*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; -- cgit v1.2.3-70-g09d2