I was in need of a vector-like type that could hold types that couldn't be copied or moved so I implemented one. I tried to make it similar to standard STL containers.
Is the use of the macro to throw the exception for the at
member function legitimate to prevent code repetition between the const and non const version of at
?
I didn't include all the code as there is a lot of repetitions for getters, and I don't have support for allocators yet.
Do you see design or implementation flaws?
Any advice to improve it?
#define FIXED_BUFF_THROW_OUT_RANGE(pos, size) (throw std::out_of_range("out of range : " + std::to_string(pos) + " >= " + std::to_string(size) + " in " + std::string(__PRETTY_FUNCTION__)))
//pretty function is a clang/gcc extension similar to __func__ be with a full function signature
template<typename T>
class fixed_buffer {
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = value_type*;
using const_iterator = const value_type*;
private:
static constexpr bool is_noexcept_destructible = noexcept(std::declval<value_type>().~value_type());
private:
iterator _begin;
iterator _end;
iterator _alloc;
public:
fixed_buffer() noexcept {
_begin = nullptr;
_end = nullptr;
_alloc = nullptr;
}
explicit fixed_buffer(size_type size) {
_begin = static_cast<iterator>(std::aligned_alloc(alignof(value_type), sizeof(value_type) * size));
_end = _begin;
_alloc = _begin + size;
}
fixed_buffer(const fixed_buffer&) = delete;
fixed_buffer operator =(const fixed_buffer&) = delete;
fixed_buffer(fixed_buffer&& other) noexcept {
_begin = other._begin;
_end = other._end;
_alloc = other._alloc;
other._begin = nullptr;
other._end = nullptr;
other._alloc = nullptr;
}
fixed_buffer& operator= (fixed_buffer&& other) noexcept(is_noexcept_destructible) {
if (_begin) {
clear();
free(_begin);
}
_begin = other._begin;
_end = other._end;
_alloc = other._alloc;
other._begin = nullptr;
other._end = nullptr;
other._alloc = nullptr;
return *this;
}
reference operator [](size_type pos) {
return _begin[pos];
}
const_reference operator [](size_type pos) const {
return _begin[pos];
}
reference at(size_type pos) {
if (pos < size())
return this->operator[](pos);
FIXED_BUFF_THROW_OUT_RANGE(pos, size());
}
const_reference at(size_type pos) const {
if (pos < size())
return this->operator[](pos);
FIXED_BUFF_THROW_OUT_RANGE(pos, size());
}
template<typename ... Ts>
void remplace_at(size_type pos, Ts&& ...ts) {
_begin[pos].~value_type();
new(_begin + pos) value_type(std::forward<Ts>(ts)...);
}
template<typename ... Ts>
void emplace(Ts&& ... ts) {
new(_end++) value_type(std::forward<Ts>(ts)...);
}
void pop_back() {
back().~value_type();
_end--;
}
void clear() noexcept(is_noexcept_destructible) {
for (iterator it = _begin; it != _end; it++) {
it->~value_type();
}
_end = _begin;
}
void swap(fixed_buffer& other) noexcept(is_noexcept_destructible) {
fixed_buffer tmp = std::move(other);
other = std::move(*this);
*this = std::move(tmp);
}
size_type size() const noexcept {
return _end - _begin;
}
size_type capacity() const noexcept {
return _alloc - _begin;
}
iterator begin() noexcept {
return _begin;
}
const_iterator begin() const noexcept {
return _begin;
}
iterator end() noexcept {
return _end;
}
const_iterator end() const noexcept {
return _end;
}
reference front() noexcept {
return *_begin;
}
const_reference front() const noexcept {
return *_begin;
}
reference back() noexcept {
return _end[-1];
}
const_reference back() const noexcept {
return _end[-1];
}
~fixed_buffer() noexcept(noexcept(std::declval<value_type>().~value_type())) {
if (_begin) {
clear();
free(_begin);
}
}
};
#undef FIXED_BUFF_THROW_OUT_RANGE
size()
is referred to insideat
, but no declaration of it is to be found). Please check and add the missing functions. \$\endgroup\$