// // Created by Patrick Maschek on 19.12.2023. // #ifndef CONST_CONTAINER_CONST_VEC_H_ #define CONST_CONTAINER_CONST_VEC_H_ #include #include #include #include #include "helper.h" namespace cc { template 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; using const_reverse_iterator = std::reverse_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 constexpr explicit const_vector(const value_type (&array)[N2]); constexpr const_vector(std::initializer_list values); template constexpr const_vector(InputIt first, InputIt last); constexpr const_vector(const const_vector& other) noexcept; template constexpr const_vector(const const_vector& other); constexpr const_vector(const_vector&& other) noexcept; template constexpr const_vector(const_vector&& other); constexpr ~const_vector() = default; // elements in static array should be destroyed automatically constexpr const_vector& operator=(const const_vector& other) noexcept; // needed to handle *this = *this template constexpr const_vector& operator=(const const_vector& other); constexpr const_vector& operator=(const_vector&& other) noexcept; template constexpr const_vector& operator=(const_vector&& other); //constexpr const_vector& operator=(const value_type (&array)[N]) noexcept; // not needed as functionally equivalent to templated overload, could be noexcept though template constexpr const_vector& operator=(const value_type (&array)[N2]); constexpr const_vector& operator=(std::initializer_list values); constexpr void assign(size_type count, const value_type& value) noexcept; template constexpr void assign(InputIt first, InputIt last); constexpr void assign(std::initializer_list 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(_arr + _size); }; [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator(_arr + _size); }; [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return std::reverse_iterator(_arr + _size); }; [[nodiscard]] constexpr reverse_iterator rend() noexcept { return std::reverse_iterator(_arr); }; [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return std::reverse_iterator(_arr); }; [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return std::reverse_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() { std::fill(begin(), end(), T()); } 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 constexpr iterator insert(const_iterator pos, InputIt first, InputIt last); constexpr iterator insert(const_iterator pos, std::initializer_list values); template 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 constexpr reference emplace_back(Args&&... args); constexpr void pop_back(); template constexpr void swap(const_vector& other); template friend class const_vector; }; template const_vector(A[]) -> const_vector>; template const_vector(T...) -> const_vector, sizeof...(T)>; template constexpr bool operator==(const const_vector& lhs, const const_vector& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template constexpr auto operator<=>(const const_vector& lhs, const const_vector& rhs) { return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::compare_three_way()); } template constexpr void swap(const const_vector& lhs, const const_vector& rhs) { return lhs.swap(rhs); } template constexpr const_vector::size_type erase(const_vector& 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 constexpr const_vector::size_type erase(const_vector& 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 constexpr const_vector::const_vector(const value_type &value) noexcept : _size(N) { std::fill(std::begin(_arr), std::end(_arr), value); } template constexpr const_vector::const_vector(size_type count, const value_type &value) noexcept { if (count > N) count = N; _size = count; std::fill(_arr, _arr + _size, value); } template constexpr const_vector::const_vector(const value_type (&array)[N]) noexcept : _size(N) { std::copy(std::begin(array), std::end(array), _arr); } template template constexpr const_vector::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 constexpr const_vector::const_vector(std::initializer_list 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 template constexpr const_vector::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 constexpr const_vector::const_vector(const const_vector &other) noexcept : _size(other._size) { std::copy(other.begin(), other.end(), _arr); _size = other._size; } template template constexpr const_vector::const_vector(const const_vector &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 constexpr const_vector::const_vector(const_vector &&other) noexcept : _size(other._size) { std::move(other.begin(), other.end(), _arr); } template template constexpr const_vector::const_vector(const_vector &&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 constexpr const_vector &const_vector::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 template constexpr const_vector &const_vector::operator=(const const_vector &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 constexpr const_vector &const_vector::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 template constexpr const_vector &const_vector::operator=(const_vector &&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 template constexpr const_vector &const_vector::operator=(const value_type (&array)[N2]) { assign(std::begin(array), std::end(array)); return *this; } template constexpr const_vector &const_vector::operator=(std::initializer_list values) { assign(std::move(values)); return *this; } template constexpr void const_vector::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 template constexpr void const_vector::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 constexpr void const_vector::assign(std::initializer_list 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 constexpr const_vector::reference const_vector::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 constexpr const_vector::const_reference const_vector::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 constexpr const_vector::iterator const_vector::insert(const_vector::const_iterator pos, const T &value) { if (_size == N) throw std::exception(); ptrdiff_t i = pos - _arr; std::move(_arr + i, _arr + _size, _arr + i + 1); _arr[i] = value; ++_size; return pos; } template constexpr const_vector::iterator const_vector::insert(const_vector::const_iterator pos, T &&value) { if (_size == N) throw std::exception(); ptrdiff_t i = pos - _arr; std::move(_arr + i, _arr + _size, _arr + i + 1); _arr[i] = std::move(value); ++_size; return pos; } template constexpr const_vector::iterator const_vector::insert(const_vector::const_iterator pos, const_vector::size_type count, const T &value) { if (count == 0) return pos; if (_size + count >= N) throw std::exception(); std::move(pos, _arr + _size, pos + count); std::fill(pos, pos + count, value); _size += count; return pos; } template template constexpr const_vector::iterator const_vector::insert(const_vector::const_iterator pos, InputIt first, InputIt last) { auto count = std::distance(first, last); if (first == last) return pos; if (_size + count >= N) throw std::exception(); std::move(pos, _arr + _size, pos + count); std::copy(first, last, pos); _size += count; return pos; } template constexpr const_vector::iterator const_vector::insert(const_vector::const_iterator pos, std::initializer_list values) { return insert(pos, values.begin(), values.end()); } template template constexpr const_vector::iterator const_vector::emplace(const_vector::const_iterator pos, Args &&... args) { if (_size == N) throw std::exception(); T obj(std::forward(args)...); ptrdiff_t i = pos - _arr; std::move(_arr + i, _arr + _size, _arr + i + 1); _erase_no_move(pos); _arr[i] = std::move(obj); return pos; } template constexpr const_vector::iterator const_vector::erase(const_vector::const_iterator pos) { _erase_no_move(pos); std::move(pos + 1, end(), pos); --_size; return pos; } template constexpr const_vector::iterator const_vector::erase(const_vector::const_iterator first, const_vector::const_iterator last) { _erase_no_move(first, last); std::move(last + 1, end(), first); _size -= (last - first); return first; } template constexpr void const_vector::push_back(const const_vector::value_type &value) { insert(end(), value); } template constexpr void const_vector::push_back(T&& value) { insert(end(), value); } template template constexpr const_vector::reference const_vector::emplace_back(Args&&... args) { emplace(end(), std::forward(args)...); } template constexpr void const_vector::pop_back() { _erase_no_move(end() - 1, end()); --_size; } template template constexpr void const_vector::swap(const_vector &other) { if (_size > other._len || other._size > N) throw std::exception(); cc::helper::swap_iter_range(begin(), end(), std::end(_arr), other.begin(), other.end(), std::end(other._arr)); std::swap(_size, other._size); } }; // cc #endif //CONST_CONTAINER_CONST_VEC_H_