As the title states, this is my 4th revision of this class. Here is the previous post if needed.
I'm hoping someone experienced with things such as noexcept
, [[nodiscard]]
, etc can go over the uses in the class below any make sure I'm on the right track?
Further more, I've attempted to implement the copy and swap and would like to know if my implementation looks accurate?
I've changed data
private member variable to std::array
from the previous "c-style" array, was that a reasonable thing to do?
I've noticed that std::aligned_storage
is in <type_traits>
which also appears to supply an std::move
and std::forward
but this mentions forward is in <utility>
, that site also shows std::move
in <utility>
or <algorithm>
how do I know if I'm including the correct files?
I'm also looking for any feedback on overlooked bugs/problems with my implementation, as well as any other feature suggestions. (I'm attempting to use this class as a standard for my future work so I'm trying to pack as much knowledge into it as possible.)
#pragma once // not looking for feedback on include guard.
#include <new> // std::launder
#include <array> // std::array
#include <cstddef> // std::size_t
#include <cassert> // assert
#include <type_traits> // std::aligned_storage_t, std::forward, std::move
#include "../LangMacros.h" // NO_DISCARD
namespace datastructures {
template<class T, std::size_t N>
class RingBuffer {
std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> data;
std::size_t head;
std::size_t tail;
bool isFull;
NO_DISCARD T* ptr_to(std::size_t pos) noexcept {
return std::launder(reinterpret_cast<T*>(&data[pos]));
}
NO_DISCARD const T* ptr_to(std::size_t pos) const noexcept {
return std::launder(reinterpret_cast<const T*>(&data[pos]));
}
public:
// default
constexpr RingBuffer() noexcept :
data{},
head{0},
tail{0},
isFull{false} {
};
// copy
RingBuffer(const RingBuffer& other) :
data{},
head{other.head},
tail{other.tail},
isFull{other.isFull}
{
// can this function be noexcept?
if (!is_empty())
// ignore bug with this for loop iteration
for (std::size_t i = tail; i < head; i = (i + 1) % N)
new (&data[i]) T(*other.ptr_to(i)); // `other.data[i]` give cosnt _Ty which causes error, why?
}
// move
/* replaced with "swap-move" below instead
RingBuffer(RingBuffer&& other) :
data{},
head{other.head},
tail{other.tail},
isFull{other.isFull}
{
// note: not exception safe
if (!is_empty())
for (std::size_t i = tail; i < head; i = (i + 1) % N)
new (&data[i]) T(std::move(other.data[i]));
}
*/
// swap-move
RingBuffer(RingBuffer&& other) :
RingBuffer() // initialize via default constructor, C++11 only
{
swap(*this, other);
}
~RingBuffer() noexcept {
clear();
}
// using https://stackoverflow.com/a/3279550/4831051 as a reference, can this be constexpr / noexcept?
friend void swap(RingBuffer& first, RingBuffer& second) /*nothrow*/ {
using std::swap;
swap(first.data, second.data); //TODO is this safe?!
swap(first.head, second.head);
swap(first.tail, second.tail);
swap(first.isFull, second.isFull);
}
RingBuffer& operator=(RingBuffer other) {
swap(*this, other);
return *this;
}
void clear() noexcept {
while (!is_empty()) pop();
}
template<typename ...Args>
void emplace_push(Args&&... args) noexcept {
assert(!isFull && "error: buffer is full!");
new (&data[head]) T(std::forward<Args>(args)...);
head = (head + 1) % N;
isFull = head == tail;
}
void push(T item) noexcept {
emplace_push(std::move(item));
}
void pop() noexcept {
assert(!is_empty() && "error: buffer is empty!");
ptr_to(tail)->~T();
tail = (tail + 1) % N;
isFull = false;
}
NO_DISCARD constexpr T& peek() noexcept {
assert(!is_empty() && "error: buffer is empty!");
return *ptr_to(tail);
}
NO_DISCARD constexpr const T& peek() const noexcept {
assert(!is_empty() && "error: buffer is empty!");
return *ptr_to(tail);
}
NO_DISCARD constexpr bool is_empty() const noexcept {
return !isFull && tail == head;
}
NO_DISCARD constexpr std::size_t get_capacity() const noexcept {
return N;
}
NO_DISCARD constexpr std::size_t get_size() const noexcept {
if (isFull)
return N;
if (head >= tail)
return head - tail;
return N + head - tail;
}
};
}
move
. The one you are looking for is declared in<utility>
. \$\endgroup\$ – L. F. May 27 '19 at 9:47