553 lines
19 KiB
C++
553 lines
19 KiB
C++
//
|
|
// Created by Patrick Maschek on 19.12.2023.
|
|
//
|
|
|
|
#ifndef CONST_CONTAINER_CONST_VEC_H_
|
|
#define CONST_CONTAINER_CONST_VEC_H_
|
|
|
|
#include <algorithm>
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <utility>
|
|
#include "helper.h"
|
|
|
|
namespace cc {
|
|
|
|
template<typename T, std::size_t N>
|
|
class const_vector {
|
|
|
|
static_assert(N > 0, "Capacity of const_vector has to be greater than 0");
|
|
|
|
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 pointer = value_type *;
|
|
using const_pointer = const value_type *;
|
|
using iterator = pointer;
|
|
using const_iterator = const_pointer;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
|
|
protected:
|
|
static constexpr const size_type _len = N;
|
|
T _arr[N] = {};
|
|
size_type _size = 0;
|
|
|
|
public:
|
|
|
|
constexpr const_vector() noexcept = default;
|
|
|
|
constexpr explicit const_vector(const value_type &value) noexcept;
|
|
constexpr const_vector(size_type count, const value_type &value) noexcept;
|
|
|
|
constexpr const_vector(const value_type (&array)[N]) noexcept;
|
|
template <std::size_t N2>
|
|
constexpr explicit const_vector(const value_type (&array)[N2]);
|
|
|
|
constexpr const_vector(std::initializer_list<value_type> values);
|
|
|
|
template<std::input_iterator InputIt>
|
|
constexpr const_vector(InputIt first, InputIt last);
|
|
|
|
constexpr const_vector(const const_vector<value_type, N>& other) noexcept;
|
|
template <std::size_t N2>
|
|
constexpr const_vector(const const_vector<value_type, N2>& other);
|
|
constexpr const_vector(const_vector&& other) noexcept;
|
|
template <std::size_t N2>
|
|
constexpr const_vector(const_vector<value_type, N2>&& other);
|
|
|
|
constexpr ~const_vector() = default; // elements in static array should be destroyed automatically
|
|
|
|
constexpr const_vector<T, N>& operator=(const const_vector& other) noexcept; // needed to handle *this = *this
|
|
template <std::size_t N2>
|
|
constexpr const_vector<T, N>& operator=(const const_vector<value_type, N2>& other);
|
|
constexpr const_vector<T, N>& operator=(const_vector&& other) noexcept;
|
|
template <std::size_t N2>
|
|
constexpr const_vector<T, N>& operator=(const_vector<value_type, N2>&& other);
|
|
//constexpr const_vector<T, N>& operator=(const value_type (&array)[N]) noexcept; // not needed as functionally equivalent to templated overload, could be noexcept though
|
|
template <std::size_t N2>
|
|
constexpr const_vector<T, N>& operator=(const value_type (&array)[N2]);
|
|
constexpr const_vector<T, N>& operator=(std::initializer_list<value_type> values);
|
|
|
|
constexpr void assign(size_type count, const value_type& value) noexcept;
|
|
template<std::input_iterator InputIt>
|
|
constexpr void assign(InputIt first, InputIt last);
|
|
constexpr void assign(std::initializer_list<value_type> values);
|
|
|
|
constexpr reference at(size_type pos);
|
|
constexpr const_reference at(size_type pos) const;
|
|
|
|
constexpr T& operator[](size_type pos) { return _arr[pos]; }
|
|
constexpr const T& operator[](size_type pos) const { return _arr[pos]; }
|
|
|
|
[[nodiscard]] constexpr reference front() noexcept { return _arr[0]; }
|
|
[[nodiscard]] constexpr const_reference front() const noexcept { return _arr[0]; }
|
|
|
|
[[nodiscard]] constexpr reference back() noexcept { return _arr[_size - 1]; }
|
|
[[nodiscard]] constexpr const_reference back() const noexcept { return _arr[_size - 1]; }
|
|
|
|
[[nodiscard]] constexpr value_type * data() noexcept { return _arr; }
|
|
[[nodiscard]] constexpr const value_type * data() const noexcept { return _arr; }
|
|
|
|
[[nodiscard]] constexpr iterator begin() noexcept { return _arr; };
|
|
[[nodiscard]] constexpr const_iterator begin() const noexcept { return _arr; };
|
|
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return _arr; };
|
|
|
|
[[nodiscard]] constexpr iterator end() noexcept { return _arr + _size; };
|
|
[[nodiscard]] constexpr const_iterator end() const noexcept { return _arr + _size; };
|
|
[[nodiscard]] constexpr const_iterator cend() const noexcept { return _arr + _size; };
|
|
|
|
[[nodiscard]] constexpr reverse_iterator rbegin() noexcept { return std::reverse_iterator<iterator>(_arr + _size); };
|
|
[[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator<const_iterator>(_arr + _size); };
|
|
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return std::reverse_iterator<const_iterator>(_arr + _size); };
|
|
|
|
[[nodiscard]] constexpr reverse_iterator rend() noexcept { return std::reverse_iterator<iterator>(_arr); };
|
|
[[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return std::reverse_iterator<const_iterator>(_arr); };
|
|
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return std::reverse_iterator<const_iterator>(_arr); };
|
|
|
|
[[nodiscard]] constexpr bool empty() const noexcept { return begin() == end(); } // vector standard defines empty as this
|
|
[[nodiscard]] constexpr size_type max_size() const noexcept { return _len; }
|
|
[[nodiscard]] constexpr size_type capacity() const noexcept { return _len; }
|
|
[[nodiscard]] constexpr size_type size() const noexcept { return _size; }
|
|
|
|
constexpr void clear();
|
|
|
|
constexpr iterator insert(const_iterator pos, const T& value);
|
|
constexpr iterator insert(const_iterator pos, T&& value);
|
|
constexpr iterator insert(const_iterator pos, size_type count, const T& value);
|
|
template<std::input_iterator InputIt>
|
|
constexpr iterator insert(const_iterator pos, InputIt first, InputIt last);
|
|
constexpr iterator insert(const_iterator pos, std::initializer_list<T> values);
|
|
|
|
template<typename ...Args>
|
|
constexpr iterator emplace(const_iterator pos, Args&&... args);
|
|
|
|
constexpr iterator erase(const_iterator pos);
|
|
constexpr iterator erase(const_iterator first, const_iterator last);
|
|
|
|
constexpr void push_back(const T& value);
|
|
constexpr void push_back(T&& value);
|
|
|
|
template<typename ...Args>
|
|
constexpr reference emplace_back(Args&&... args);
|
|
|
|
constexpr void pop_back();
|
|
|
|
template<std::size_t N2>
|
|
constexpr void swap(const_vector<T, N2>& other);
|
|
|
|
template <typename T2, std::size_t N2>
|
|
friend class const_vector;
|
|
|
|
};
|
|
|
|
template<typename A>
|
|
const_vector(A[]) -> const_vector<A, helper::array_size<A>>;
|
|
|
|
template<typename ...T>
|
|
const_vector(T...) -> const_vector<std::common_type_t<T...>, sizeof...(T)>;
|
|
|
|
|
|
template<typename T1, typename T2, std::size_t N1, std::size_t N2>
|
|
constexpr bool operator==(const const_vector<T1, N1>& lhs,
|
|
const const_vector<T2, N2>& rhs)
|
|
{
|
|
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
|
|
}
|
|
|
|
template<typename T1, typename T2, std::size_t N1, std::size_t N2>
|
|
constexpr auto operator<=>(const const_vector<T1, N1>& lhs,
|
|
const const_vector<T2, N2>& rhs)
|
|
{
|
|
return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::compare_three_way());
|
|
}
|
|
|
|
template<typename T, std::size_t N1, std::size_t N2>
|
|
constexpr void swap(const const_vector<T, N1>& lhs,
|
|
const const_vector<T, N2>& rhs)
|
|
{
|
|
return lhs.swap(rhs);
|
|
}
|
|
|
|
template<typename T, std::size_t N, typename U>
|
|
constexpr const_vector<T, N>::size_type erase(const_vector<T, N>& vec, const U& value)
|
|
{
|
|
auto it = std::remove(vec.begin(), vec.end(), value);
|
|
auto r = vec.end() - it;
|
|
vec.erase(it, vec.end());
|
|
|
|
return r;
|
|
}
|
|
|
|
template<typename T, std::size_t N, typename Pred>
|
|
constexpr const_vector<T, N>::size_type erase(const_vector<T, N>& vec, Pred pred)
|
|
{
|
|
auto it = std::remove_if(vec.begin(), vec.end(), pred);
|
|
auto r = vec.end() - it;
|
|
vec.erase(it, vec.end());
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::const_vector(const value_type &value) noexcept
|
|
: _size(N)
|
|
{
|
|
std::fill(std::begin(_arr), std::end(_arr), value);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::const_vector(size_type count, const value_type &value) noexcept
|
|
{
|
|
if (count > N) count = N;
|
|
_size = count;
|
|
std::fill(_arr, _arr + _size, value);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::const_vector(const value_type (&array)[N]) noexcept
|
|
: _size(N)
|
|
{
|
|
std::copy(std::begin(array), std::end(array), _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::size_t N2>
|
|
constexpr const_vector<T, N>::const_vector(const value_type (&array)[N2])
|
|
: _size(N2)
|
|
{
|
|
if (N < N2) throw std::invalid_argument("Size of array has to be smaller or equal to the capacity: " + std::to_string(N2) + ">=" + std::to_string(N));
|
|
std::copy(std::begin(array), std::end(array), _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::const_vector(std::initializer_list<value_type> values)
|
|
: _size(values.size())
|
|
{
|
|
if (values.size() > N) throw std::invalid_argument("Initializer list in assign has more elements than size" + std::to_string(values.size()) + ">=" + std::to_string(N));
|
|
std::move(values.begin(), values.end(), _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::input_iterator InputIt>
|
|
constexpr const_vector<T, N>::const_vector(InputIt first, InputIt last)
|
|
: _size(std::distance(first, last))
|
|
{
|
|
if (std::distance(first, last) > N) throw std::invalid_argument("tried inserting more elements than const_vector is in size");
|
|
std::copy(first, last, _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::const_vector(const const_vector &other) noexcept
|
|
: _size(other._size)
|
|
{
|
|
std::copy(other.begin(), other.end(), _arr);
|
|
_size = other._size;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::size_t N2>
|
|
constexpr const_vector<T, N>::const_vector(const const_vector<const_vector::value_type, N2> &other)
|
|
: _size(other.size())
|
|
{
|
|
if (_len <= other.size()) throw std::invalid_argument("size of other has to be equal to or smaller than this");
|
|
std::copy(other.begin(), other.end(), _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::const_vector(const_vector &&other) noexcept
|
|
: _size(other._size)
|
|
{
|
|
std::move(other.begin(), other.end(), _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::size_t N2>
|
|
constexpr const_vector<T, N>::const_vector(const_vector<value_type, N2> &&other)
|
|
: _size(other._size)
|
|
{
|
|
if (_len <= other.size()) throw std::invalid_argument("size of other has to be equal to or smaller than this");
|
|
std::move(other.begin(), other.end(), _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N> &const_vector<T, N>::operator=(const const_vector &other) noexcept
|
|
{
|
|
if (this == &other) return *this;
|
|
|
|
// event though assign may throw an exception noexcept for this operator should be fine,
|
|
// as std::distance(other.begin(), other.end()) cannot be greater than the capacity,
|
|
// because the signatures would have to differ
|
|
assign(other.begin(), other.end());
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::size_t N2>
|
|
constexpr const_vector<T, N> &const_vector<T, N>::operator=(const const_vector<const_vector::value_type, N2> &other)
|
|
{
|
|
if (other.size() > _len) throw std::invalid_argument("size of other has to be equal to or smaller than this");
|
|
|
|
// this is not necessary, as it should call the non templated operator=
|
|
// (and throws an error without a cast)
|
|
//if (this == &other) return *this;
|
|
|
|
assign(other.begin(), other.end());
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N> &const_vector<T, N>::operator=(const_vector &&other) noexcept
|
|
{
|
|
// cannot occur, otherwise signature would differ
|
|
//static_assert(N == other._len, "Cannot assign const_vector to other with different size");
|
|
//if (N != other._len) throw std::exception();
|
|
|
|
clear();
|
|
std::move(other.begin(), other.end(), _arr);
|
|
_size = other._size;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::size_t N2>
|
|
constexpr const_vector<T, N> &const_vector<T, N>::operator=(const_vector<value_type, N2> &&other)
|
|
{
|
|
if (other.size() > _len) throw std::invalid_argument("size of other has to be equal to or smaller than this");
|
|
|
|
clear();
|
|
std::move(other.begin(), other.end(), _arr);
|
|
_size = other._size;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::size_t N2>
|
|
constexpr const_vector<T, N> &const_vector<T, N>::operator=(const value_type (&array)[N2])
|
|
{
|
|
assign(std::begin(array), std::end(array));
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N> &const_vector<T, N>::operator=(std::initializer_list<value_type> values)
|
|
{
|
|
assign(std::move(values));
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr void const_vector<T, N>::assign(const_vector::size_type count, const value_type &value) noexcept
|
|
{
|
|
if (count > N) count = N;
|
|
_size = count;
|
|
|
|
std::fill(std::begin(_arr), std::end(_arr), value);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::input_iterator InputIt>
|
|
constexpr void const_vector<T, N>::assign(InputIt first, InputIt last)
|
|
{
|
|
auto distance = std::distance(first, last);
|
|
if (distance > N) throw std::invalid_argument("Iterator distance in assign surpasses size " + std::to_string(distance) + " >= " + std::to_string(N));
|
|
_size = distance;
|
|
std::copy(first, last, begin());
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr void const_vector<T, N>::assign(std::initializer_list<value_type> values)
|
|
{
|
|
if (values.size() > N) throw std::invalid_argument("Initializer list in assign has more elements than size" + std::to_string(values.size()) + ">=" + std::to_string(N));
|
|
_size = values.size();
|
|
std::copy(values.begin(), values.end(), _arr);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::reference const_vector<T, N>::at(const_vector::size_type pos)
|
|
{
|
|
if (pos >= _size) throw std::out_of_range("Pos " + std::to_string(pos) + " is out of range");
|
|
return _arr[pos];
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::const_reference const_vector<T, N>::at(const_vector::size_type pos) const
|
|
{
|
|
if (pos >= _size) throw std::out_of_range("Pos " + std::to_string(pos) + " is out of range");
|
|
return _arr[pos];
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr void const_vector<T, N>::clear()
|
|
{
|
|
std::destroy(begin(), end());
|
|
_size = 0;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::iterator const_vector<T, N>::insert(const_vector::const_iterator pos, const T &value)
|
|
{
|
|
if (_size == N) throw std::length_error("No space left in vector");
|
|
|
|
auto it = const_cast<iterator>(pos);
|
|
|
|
if (pos != end()) {
|
|
std::move_backward(it, end(), end() + 1);
|
|
}
|
|
*it = value;
|
|
++_size;
|
|
|
|
return it;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::iterator const_vector<T, N>::insert(const_vector::const_iterator pos, T &&value)
|
|
{
|
|
if (_size == N) throw std::length_error("No space left in vector");
|
|
|
|
auto it = const_cast<iterator>(pos);
|
|
|
|
if (pos != end()) {
|
|
std::move_backward(it, end(), end() + 1);
|
|
}
|
|
*it = std::forward<value_type>(value);
|
|
++_size;
|
|
|
|
return it;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::iterator
|
|
const_vector<T, N>::insert(const_vector::const_iterator pos, const_vector::size_type count, const T &value)
|
|
{
|
|
auto it = const_cast<iterator>(pos);
|
|
|
|
if (count == 0) return it;
|
|
if (_size + count > N) throw std::length_error("Not enough space left in vector for " + std::to_string(count) + " elements");
|
|
|
|
std::move_backward(it, end(), end() + count);
|
|
std::fill(it, it + count, value);
|
|
|
|
_size += count;
|
|
return it;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::input_iterator InputIt>
|
|
constexpr const_vector<T, N>::iterator
|
|
const_vector<T, N>::insert(const_vector::const_iterator pos, InputIt first, InputIt last)
|
|
{
|
|
auto count = std::distance(first, last);
|
|
auto it = const_cast<iterator>(pos);
|
|
|
|
if (first == last) return it;
|
|
if (_size + count > N) throw std::length_error("Not enough space left in vector for " + std::to_string(count) + " elements");
|
|
|
|
std::move_backward(it, end(), end() + count);
|
|
std::copy(first, last, it);
|
|
|
|
_size += count;
|
|
return it;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::iterator
|
|
const_vector<T, N>::insert(const_vector::const_iterator pos, std::initializer_list<T> values)
|
|
{
|
|
return insert(pos, values.begin(), values.end());
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<typename... Args>
|
|
constexpr const_vector<T, N>::iterator const_vector<T, N>::emplace(const_vector::const_iterator pos, Args &&... args)
|
|
{
|
|
if (_size == N) throw std::length_error("No space left in vector");
|
|
|
|
auto it = const_cast<iterator>(pos);
|
|
|
|
if (it != end()) {
|
|
std::move_backward(it, end(), end() + 1);
|
|
}
|
|
*it = T(std::forward<Args>(args)...);
|
|
|
|
++_size;
|
|
return it;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::iterator const_vector<T, N>::erase(const_vector::const_iterator pos)
|
|
{
|
|
auto it = const_cast<iterator>(pos);
|
|
|
|
std::destroy_n(it, 1);
|
|
std::move(it + 1, end(), it);
|
|
--_size;
|
|
|
|
return it;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr const_vector<T, N>::iterator
|
|
const_vector<T, N>::erase(const_vector::const_iterator first, const_vector::const_iterator last)
|
|
{
|
|
auto _first = const_cast<iterator>(first);
|
|
auto _last = const_cast<iterator>(last);
|
|
|
|
if (_first == _last) return _last;
|
|
|
|
std::destroy(_first, _last);
|
|
std::move(_last, end(), _first);
|
|
_size -= std::distance(_first, _last);
|
|
|
|
return _first;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr void const_vector<T, N>::push_back(const const_vector::value_type &value)
|
|
{
|
|
insert(end(), value);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr void const_vector<T, N>::push_back(T&& value)
|
|
{
|
|
insert(end(), value);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<typename ...Args>
|
|
constexpr const_vector<T, N>::reference const_vector<T, N>::emplace_back(Args&&... args)
|
|
{
|
|
return *emplace(cend(), std::forward<Args>(args)...);
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
constexpr void const_vector<T, N>::pop_back()
|
|
{
|
|
std::destroy_n(end() - 1, 1);
|
|
--_size;
|
|
}
|
|
|
|
template<typename T, std::size_t N>
|
|
template<std::size_t N2>
|
|
constexpr void const_vector<T, N>::swap(const_vector<T, N2> &other)
|
|
{
|
|
if (_size > other._len || other._size > N) throw std::invalid_argument("Cannot swap elements, one does not fit into the other");
|
|
|
|
cc::helper::swap_iter_range(begin(), end(), other.begin(), other.end());
|
|
std::swap(_size, other._size);
|
|
}
|
|
|
|
}; // cc
|
|
|
|
#endif //CONST_CONTAINER_CONST_VEC_H_
|