31 #include <type_traits>
48 template <
typename T,
size_t Capacity>
54 maybe_storage() noexcept {}
57 maybe_storage _storage[Capacity];
59 static size_t mask(
size_t idx) {
return idx % Capacity; }
60 T* obj(
size_t idx) {
return &_storage[mask(idx)].data; }
61 const T* obj(
size_t idx)
const {
return &_storage[mask(idx)].data; }
63 static_assert((Capacity & (Capacity - 1)) == 0,
"capacity must be a power of two");
64 static_assert(std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_assignable<T>::value,
65 "circular_buffer_fixed_capacity only supports nothrow-move value types");
67 using size_type = size_t;
70 using const_reference =
const T&;
71 using const_pointer =
const T*;
72 using difference_type = ssize_t;
74 template <
typename ValueType>
76 using holder = std::conditional_t<std::is_const<ValueType>::value,
const maybe_storage, maybe_storage>;
80 cbiterator(holder* start,
size_t idx) noexcept : _start(start), _idx(idx) {}
82 using iterator_category = std::random_access_iterator_tag;
83 using value_type = ValueType;
84 using difference_type = ssize_t;
85 using pointer = ValueType*;
86 using reference = ValueType&;
89 ValueType& operator*()
const {
return _start[mask(_idx)].data; }
90 ValueType* operator->()
const {
return &operator*(); }
113 cbiterator operator+(difference_type n)
const {
119 cbiterator operator-(difference_type n)
const {
130 bool operator==(
const cbiterator& rhs)
const {
131 return _idx == rhs._idx;
133 bool operator!=(
const cbiterator& rhs)
const {
134 return _idx != rhs._idx;
137 return ssize_t(_idx - rhs._idx) < 0;
140 return ssize_t(_idx - rhs._idx) > 0;
142 bool operator<=(
const cbiterator& rhs)
const {
143 return ssize_t(_idx - rhs._idx) <= 0;
145 bool operator>=(
const cbiterator& rhs)
const {
146 return ssize_t(_idx - rhs._idx) >= 0;
148 difference_type operator-(
const cbiterator& rhs)
const {
149 return _idx - rhs._idx;
161 void push_front(
const T& data);
162 void push_front(T&& data);
163 template <
typename... A>
164 T& emplace_front(A&&... args);
165 void push_back(
const T& data);
166 void push_back(T&& data);
167 template <
typename... A>
168 T& emplace_back(A&&... args);
175 size_t capacity()
const;
176 T& operator[](
size_t idx);
181 const_iterator begin()
const {
182 return const_iterator(_storage, _begin);
185 return iterator(_storage, _end);
187 const_iterator end()
const {
188 return const_iterator(_storage, _end);
190 const_iterator cbegin()
const {
191 return const_iterator(_storage, _begin);
193 const_iterator cend()
const {
194 return const_iterator(_storage, _end);
196 iterator erase(iterator first, iterator last);
199 template <
typename T,
size_t Capacity>
202 circular_buffer_fixed_capacity<T, Capacity>::empty()
const {
203 return _begin == _end;
206 template <
typename T,
size_t Capacity>
209 circular_buffer_fixed_capacity<T, Capacity>::size()
const {
210 return _end - _begin;
213 template <
typename T,
size_t Capacity>
216 circular_buffer_fixed_capacity<T, Capacity>::capacity()
const {
220 template <
typename T,
size_t Capacity>
222 circular_buffer_fixed_capacity<T, Capacity>::circular_buffer_fixed_capacity(circular_buffer_fixed_capacity&& x) noexcept
223 : _begin(x._begin), _end(x._end) {
226 for (
auto& obj : x) {
227 new (&*dest++) T(std::move(obj));
231 template <
typename T,
size_t Capacity>
233 circular_buffer_fixed_capacity<T, Capacity>&
234 circular_buffer_fixed_capacity<T, Capacity>::operator=(circular_buffer_fixed_capacity&& x) noexcept {
236 this->~circular_buffer_fixed_capacity();
237 new (
this) circular_buffer_fixed_capacity(std::move(x));
242 template <
typename T,
size_t Capacity>
244 circular_buffer_fixed_capacity<T, Capacity>::~circular_buffer_fixed_capacity() {
248 template <
typename T,
size_t Capacity>
251 circular_buffer_fixed_capacity<T, Capacity>::push_front(
const T& data) {
252 new (obj(_begin - 1)) T(data);
256 template <
typename T,
size_t Capacity>
259 circular_buffer_fixed_capacity<T, Capacity>::push_front(T&& data) {
260 new (obj(_begin - 1)) T(std::move(data));
264 template <
typename T,
size_t Capacity>
265 template <
typename... Args>
268 circular_buffer_fixed_capacity<T, Capacity>::emplace_front(Args&&... args) {
269 auto p =
new (obj(_begin - 1)) T(std::forward<Args>(args)...);
274 template <
typename T,
size_t Capacity>
277 circular_buffer_fixed_capacity<T, Capacity>::push_back(
const T& data) {
278 new (obj(_end)) T(data);
282 template <
typename T,
size_t Capacity>
285 circular_buffer_fixed_capacity<T, Capacity>::push_back(T&& data) {
286 new (obj(_end)) T(std::move(data));
290 template <
typename T,
size_t Capacity>
291 template <
typename... Args>
294 circular_buffer_fixed_capacity<T, Capacity>::emplace_back(Args&&... args) {
295 auto p =
new (obj(_end)) T(std::forward<Args>(args)...);
300 template <
typename T,
size_t Capacity>
303 circular_buffer_fixed_capacity<T, Capacity>::front() {
307 template <
typename T,
size_t Capacity>
310 circular_buffer_fixed_capacity<T, Capacity>::back() {
311 return *obj(_end - 1);
314 template <
typename T,
size_t Capacity>
317 circular_buffer_fixed_capacity<T, Capacity>::pop_front() {
322 template <
typename T,
size_t Capacity>
325 circular_buffer_fixed_capacity<T, Capacity>::pop_back() {
330 template <
typename T,
size_t Capacity>
333 circular_buffer_fixed_capacity<T, Capacity>::operator[](
size_t idx) {
334 return *obj(_begin + idx);
337 template <
typename T,
size_t Capacity>
339 typename circular_buffer_fixed_capacity<T, Capacity>::iterator
340 circular_buffer_fixed_capacity<T, Capacity>::erase(iterator first, iterator last) {
341 static_assert(std::is_nothrow_move_assignable<T>::value,
"erase() assumes move assignment does not throw");
347 if (std::distance(begin(), first) < std::distance(last, end())) {
348 auto new_start = std::move_backward(begin(), first, last);
350 while (i < new_start) {
353 _begin = new_start.idx;
356 auto new_end = std::move(last, end(), first);
367 template <
typename T,
size_t Capacity>
370 circular_buffer_fixed_capacity<T, Capacity>::clear() {
371 for (
auto& obj : *
this) {