|
#include <fmt/core.h> |
|
#include "catch2/catch_test_macros.hpp" |
|
#include <algorithm> |
|
#include <array> |
|
#include <cstdlib> |
|
#include <exception> |
|
#include <iterator> |
|
#include <stdexcept> |
|
#include <string.h> |
|
|
|
template <typename T, size_t N> |
|
class fixed_vector { |
|
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>; |
|
|
|
public: |
|
constexpr fixed_vector() noexcept {}; |
|
|
|
constexpr fixed_vector(size_type count, const_reference init_val) |
|
: m_size(count) { |
|
if (m_size > N) { |
|
throw std::out_of_range("count exceeds capacity"); |
|
} |
|
std::fill(std::begin(m_data), std::begin(m_data) + count, init_val); |
|
} |
|
|
|
template <std::input_iterator Iter> |
|
constexpr fixed_vector(Iter first, Iter last) |
|
: m_size(std::distance(first, last)) { |
|
if (m_size > N) { |
|
throw std::out_of_range( |
|
"std::distance(first,last) exceeds capacity"); |
|
} |
|
std::copy(first, last, std::begin(m_data)); |
|
} |
|
|
|
constexpr fixed_vector(std::initializer_list<value_type> init_list) |
|
: m_size(init_list.size()) { |
|
if (init_list.size() > N) { |
|
throw std::out_of_range( |
|
"size of initializer_list exceeds capacity"); |
|
} |
|
std::copy(std::begin(init_list), std::end(init_list), |
|
std::begin(m_data)); |
|
} |
|
|
|
// emement access |
|
|
|
constexpr reference at(size_type pos) { |
|
if (pos >= m_size) { |
|
throw std::out_of_range("pos out of range"); |
|
} else { |
|
return m_data.at(pos); |
|
} |
|
} |
|
|
|
constexpr const_reference at(size_type pos) const { |
|
if (pos >= m_size) { |
|
throw std::out_of_range("pos out of range"); |
|
} else { |
|
return m_data.at(pos); |
|
} |
|
} |
|
|
|
constexpr reference operator[](size_type pos) { return m_data[pos]; } |
|
|
|
constexpr const_reference operator[](size_type pos) const { |
|
return m_data[pos]; |
|
} |
|
|
|
constexpr reference front() { return m_data.front(); } |
|
|
|
constexpr const_reference front() const { return m_data.front(); } |
|
|
|
constexpr reference back() { return m_data[m_size - 1]; } |
|
|
|
constexpr const_reference back() const { return m_data[m_size - 1]; } |
|
|
|
constexpr T* data() noexcept { return m_data.data(); } |
|
|
|
constexpr const T* data() const noexcept { return m_data.data(); } |
|
|
|
// iterators |
|
constexpr iterator begin() noexcept { return &(*m_data.begin()); } |
|
|
|
constexpr const_iterator begin() const noexcept { return &(*m_data.begin()); } |
|
|
|
constexpr const_iterator cbegin() const noexcept { return &(*m_data.cbegin()); } |
|
|
|
constexpr iterator end() noexcept { return &(*(m_data.begin())) + m_size; } |
|
|
|
constexpr const_iterator end() const noexcept { return &(*(m_data.begin())) + m_size; } |
|
|
|
constexpr const_iterator cend() const noexcept { return &(*(m_data.begin())) + m_size; } |
|
|
|
constexpr iterator rbegin() noexcept { return &(*(m_data.begin())) + m_size; } |
|
|
|
constexpr const_iterator rbegin() const noexcept { return &(*(m_data.begin())) + m_size; } |
|
|
|
constexpr const_iterator crbegin() const noexcept { return &(*(m_data.begin())) + m_size; } |
|
|
|
constexpr iterator rend() noexcept { return &(*m_data.begin()); } |
|
|
|
constexpr const_iterator rend() const noexcept { return &(*m_data.begin()); } |
|
|
|
constexpr const_iterator crend() const noexcept { return &(*m_data.begin()); } |
|
|
|
// capacity |
|
[[nodiscard]] constexpr bool empty() const noexcept { return m_size == 0; } |
|
|
|
[[nodiscard]] constexpr bool full() const noexcept { return m_size == N; } |
|
|
|
constexpr size_type size() const noexcept { return m_size; } |
|
|
|
constexpr size_type max_size() const noexcept { return N; } |
|
|
|
constexpr size_type capacity() const noexcept { return N; } |
|
|
|
// modifiers |
|
constexpr void clear() noexcept { |
|
// if (!std::is_trivially_destructible<value_type>::value) { |
|
// std::for_each(begin(), end(), [](reference r) { r.~value_type(); }); |
|
// } |
|
m_size = 0; |
|
} |
|
|
|
constexpr void push_back(const T& value) { |
|
if (full()) { |
|
throw std::out_of_range("can't push_back already full"); |
|
} |
|
m_size++; |
|
at(m_size - 1) = value; |
|
} |
|
|
|
constexpr void push_back(T&& value) { |
|
if (full()) { |
|
throw std::out_of_range("can't push_back already full"); |
|
} |
|
m_size++; |
|
at(m_size - 1) = std::move(value); |
|
} |
|
|
|
template <class... Args> |
|
constexpr reference emplace_back(Args&&... args) { |
|
if (full()) { |
|
throw std::out_of_range("can't emplace_back already full"); |
|
} |
|
m_size++; |
|
reference ref = at(m_size - 1); |
|
ref = value_type(std::forward<Args>(args)...); |
|
return ref; |
|
} |
|
|
|
constexpr void pop_back() { |
|
if (empty()) { |
|
throw std::out_of_range("can't pop_back already empty"); |
|
} |
|
m_size--; |
|
} |
|
|
|
constexpr void swap(fixed_vector& other) { |
|
std::swap(m_data, other.m_data); |
|
std::swap(m_size, other.m_size); |
|
} |
|
|
|
|
|
constexpr void resize( size_type count, const value_type& value ){ |
|
if(count > N){ |
|
throw std::out_of_range("count exceeds capacity"); |
|
}else if(m_size > count){ |
|
while(m_size > count){ |
|
pop_back(); |
|
} |
|
}else if(m_size < count){ |
|
while(m_size < count){ |
|
push_back(value); |
|
} |
|
} |
|
// else count is equal to m_size, do nothing |
|
} |
|
|
|
constexpr void resize( size_type count ){ |
|
if(count > N){ |
|
throw std::out_of_range("count exceeds capacity"); |
|
}else if(m_size > count){ |
|
while(m_size > count){ |
|
pop_back(); |
|
} |
|
}else{ |
|
m_size = count; |
|
} |
|
} |
|
|
|
private: |
|
std::array<T, N> m_data; |
|
size_t m_size{0}; |
|
}; |
|
namespace FixedVectorUtils { |
|
template <typename T, size_t N> |
|
constexpr std::vector<T> fixed_vector_to_vector(const fixed_vector<T, N>& fv) { |
|
return {std::begin(fv), std::end(fv)}; |
|
} |
|
|
|
template <typename T, size_t N> |
|
constexpr fixed_vector<T, N> vector_to_fixed_vector(const std::vector<T>& v) { |
|
return fixed_vector<T, N>(std::begin(v), |
|
std::begin(v) + std::min(N, v.size())); |
|
} |
|
|
|
template <typename T, size_t N> |
|
constexpr size_t copy_vector_to_fixed_vector(const std::vector<T>& src, |
|
fixed_vector<T, N>& dest) { |
|
const size_t numToCopy = std::min(N, src.size()); |
|
dest.resize(numToCopy); |
|
std::copy(std::begin(src), std::end(src), std::begin(dest)); |
|
return numToCopy; |
|
} |
|
template<size_t N, size_t CStrLen> |
|
constexpr size_t copy_string_vector_to_cstr_fixed_vector(const std::vector<std::string>& src, |
|
fixed_vector<char[CStrLen], N>& dest){ |
|
const size_t numToCopy = std::min(N, src.size()); |
|
dest.resize(numToCopy); |
|
for(auto i = 0; i < numToCopy; ++i){ |
|
strncpy(dest[i], src[i].c_str(), CStrLen); |
|
} |
|
return numToCopy; |
|
} |
|
} // namespace FixedVectorUtils |
|
|
|
|
|
TEST_CASE("fixed_vector()", "fixed_vector") { |
|
fixed_vector<int, 10> fv; |
|
REQUIRE(fv.size() == 0); |
|
REQUIRE(fv.empty()); |
|
REQUIRE(fv.max_size() == 10); |
|
REQUIRE(fv.capacity() == 10); |
|
} |
|
|
|
TEST_CASE("fixed_vector(size_type count, const_reference init_val) 1", |
|
"fixed_vector") { |
|
fixed_vector<int, 10> fv(5, 33); |
|
REQUIRE(fv.size() == 5); |
|
REQUIRE(fv.max_size() == 10); |
|
REQUIRE(fv.capacity() == 10); |
|
REQUIRE(std::all_of(std::begin(fv), std::end(fv), |
|
[](const auto& v) { return v == 33; })); |
|
REQUIRE_THROWS(fv.at(5)); |
|
} |
|
|
|
TEST_CASE("fixed_vector(size_type count, const_reference init_val) 2", |
|
"fixed_vector") { |
|
REQUIRE_THROWS(fixed_vector<int, 10>(11, 33)); |
|
} |
|
|
|
TEST_CASE("fixed_vector(std::initializer_list<value_type> init_list) 1", |
|
"fixed_vector") { |
|
fixed_vector<int, 10> fv({1, 2, 3}); |
|
REQUIRE(fv.size() == 3); |
|
REQUIRE(fv.max_size() == 10); |
|
REQUIRE(fv.capacity() == 10); |
|
REQUIRE(fv.at(0) == 1); |
|
REQUIRE(fv.at(1) == 2); |
|
REQUIRE(fv.at(2) == 3); |
|
} |
|
|
|
TEST_CASE("fixed_vector(std::initializer_list<value_type> init_list) 2", |
|
"fixed_vector") { |
|
fixed_vector<int, 10> fv{10, 20, 30}; |
|
REQUIRE(fv.size() == 3); |
|
REQUIRE(fv.max_size() == 10); |
|
REQUIRE(fv.capacity() == 10); |
|
REQUIRE(fv.at(0) == 10); |
|
REQUIRE(fv.at(1) == 20); |
|
REQUIRE(fv.at(2) == 30); |
|
} |
|
|
|
TEST_CASE("fixed_vector(Iter first, Iter last) 1", "fixed_vector") { |
|
std::vector vec{1, 2, 3, 4}; |
|
fixed_vector<int, 4> fv(vec.begin(), vec.end()); |
|
|
|
REQUIRE(fv.size() == 4); |
|
REQUIRE(fv.max_size() == 4); |
|
REQUIRE(fv.full()); |
|
|
|
REQUIRE(fv[0] == 1); |
|
REQUIRE(fv[1] == 2); |
|
REQUIRE(fv[2] == 3); |
|
REQUIRE(fv[3] == 4); |
|
} |
|
|
|
TEST_CASE("fixed_vector(Iter first, Iter last) 2", "fixed_vector") { |
|
std::vector vec{1, 2, 3, 4, 5}; |
|
REQUIRE_THROWS(fixed_vector<int, 4>(vec.begin(), vec.end())); |
|
} |
|
|
|
TEST_CASE("sizeof test 1", "fixed_vector") { |
|
fixed_vector<int, 10> fv; |
|
REQUIRE(sizeof(fv) == (10 * sizeof(int) + sizeof(size_t))); |
|
} |
|
|
|
TEST_CASE("sizeof test 2", "fixed_vector") { |
|
fixed_vector<int, 10> fv; |
|
std::array<int, 10> arr; |
|
REQUIRE(sizeof(fv) == (sizeof(arr) + sizeof(size_t))); |
|
} |
|
|
|
TEST_CASE("test at throw", "fixed_vector") { |
|
fixed_vector<int, 10> fv({1, 2, 3}); |
|
REQUIRE_THROWS(fv.at(4)); |
|
} |
|
|
|
TEST_CASE("test front/last", "fixed_vector") { |
|
fixed_vector<int, 10> fv({1, 2, 3}); |
|
REQUIRE(fv.front() == 1); |
|
REQUIRE(fv.back() == 3); |
|
} |
|
|
|
TEST_CASE("test data", "fixed_vector") { |
|
fixed_vector<int, 10> fv({1, 2, 3}); |
|
|
|
int* rawData = fv.data(); |
|
REQUIRE(rawData[0] == 1); |
|
REQUIRE(rawData[2] == 3); |
|
} |
|
|
|
TEST_CASE("const test data", "fixed_vector") { |
|
fixed_vector<int, 10> fv({4, 5, 6}); |
|
|
|
const int* rawData = fv.data(); |
|
REQUIRE(rawData[0] == 4); |
|
REQUIRE(rawData[2] == 6); |
|
} |
|
|
|
TEST_CASE("clear", "fixed_vector") { |
|
fixed_vector<int, 10> fv({4, 5, 6}); |
|
|
|
REQUIRE(fv.size() == 3); |
|
REQUIRE(fv.max_size() == 10); |
|
fv.clear(); |
|
REQUIRE(fv.size() == 0); |
|
REQUIRE(fv.max_size() == 10); |
|
} |
|
|
|
TEST_CASE("begin/end", "fixed_vector") { |
|
fixed_vector<int, 10> fv({4, 5, 6}); |
|
|
|
auto startIt = fv.begin(); |
|
auto endIt = fv.end(); |
|
|
|
REQUIRE(*startIt == 4); |
|
REQUIRE(*std::next(startIt) == 5); |
|
REQUIRE(*std::prev(endIt) == 6); |
|
} |
|
|
|
TEST_CASE("cbegin/cend", "fixed_vector") { |
|
fixed_vector<int, 10> fv({4, 5, 6}); |
|
|
|
auto startIt = fv.cbegin(); |
|
auto endIt = fv.cend(); |
|
|
|
REQUIRE(*startIt == 4); |
|
REQUIRE(*std::next(startIt) == 5); |
|
REQUIRE(*std::prev(endIt) == 6); |
|
} |
|
|
|
TEST_CASE("const begin/end", "fixed_vector") { |
|
const fixed_vector<int, 10> fv({4, 5, 6}); |
|
|
|
const auto startIt = fv.begin(); |
|
const auto endIt = fv.end(); |
|
|
|
REQUIRE(*startIt == 4); |
|
REQUIRE(*std::next(startIt) == 5); |
|
REQUIRE(*std::prev(endIt) == 6); |
|
} |
|
|
|
TEST_CASE("push_back element 1", "fixed_vector") { |
|
fixed_vector<int, 10> fv; |
|
|
|
fv.push_back(33); |
|
REQUIRE(fv.back() == 33); |
|
REQUIRE(fv.size() == 1); |
|
|
|
fv.push_back(66); |
|
REQUIRE(fv.back() == 66); |
|
REQUIRE(fv.size() == 2); |
|
|
|
fv.push_back(99); |
|
REQUIRE(fv.back() == 99); |
|
REQUIRE(fv.size() == 3); |
|
} |
|
|
|
TEST_CASE("push_back element 2", "fixed_vector") { |
|
fixed_vector<int, 2> fv; |
|
|
|
fv.push_back(33); |
|
fv.push_back(66); |
|
REQUIRE_THROWS(fv.push_back(99)); |
|
} |
|
|
|
TEST_CASE("emplace_back element", "fixed_vector") { |
|
fixed_vector<int, 10> fv; |
|
|
|
const int n0 = 33; |
|
REQUIRE(fv.emplace_back(n0) == n0); |
|
REQUIRE(fv.back() == n0); |
|
REQUIRE(fv.size() == 1); |
|
|
|
const int n1 = 66; |
|
REQUIRE(fv.emplace_back(n1) == n1); |
|
REQUIRE(fv.back() == n1); |
|
REQUIRE(fv.size() == 2); |
|
|
|
const int n2 = 99; |
|
REQUIRE(fv.emplace_back(n2) == n2); |
|
REQUIRE(fv.back() == n2); |
|
REQUIRE(fv.size() == 3); |
|
} |
|
|
|
TEST_CASE("FixedVectorUtils::fixed_vector_to_vector", "fixed_vector") { |
|
const fixed_vector<int, 10> fv({4, 5, 6}); |
|
std::vector<int> v = FixedVectorUtils::fixed_vector_to_vector(fv); |
|
REQUIRE(v.size() == 3); |
|
REQUIRE(v.at(0) == 4); |
|
REQUIRE(v.at(1) == 5); |
|
REQUIRE(v.at(2) == 6); |
|
} |
|
|
|
TEST_CASE("FixedVectorUtils::vector_to_fixedvector", "fixed_vector") { |
|
std::vector<int> v{4, 5, 6}; |
|
auto fv = FixedVectorUtils::vector_to_fixed_vector<int, 10>(v); |
|
REQUIRE(fv.size() == 3); |
|
REQUIRE(fv.capacity() == 10); |
|
REQUIRE(fv.at(0) == 4); |
|
REQUIRE(fv.at(1) == 5); |
|
REQUIRE(fv.at(2) == 6); |
|
} |
|
|
|
TEST_CASE("FixedVectorUtils::copy_vector_to_fixed_vector", "fixed_vector") { |
|
const std::vector<int> v{4, 5, 6}; |
|
fixed_vector<int, 10> fv{10, 11}; |
|
FixedVectorUtils::copy_vector_to_fixed_vector(v, fv); |
|
REQUIRE(fv.size() == 3); |
|
REQUIRE(fv.capacity() == 10); |
|
REQUIRE(fv.at(0) == 4); |
|
REQUIRE(fv.at(1) == 5); |
|
REQUIRE(fv.at(2) == 6); |
|
} |
|
|
|
TEST_CASE("FixedVectorUtils::copy_string_vector_to_cstr_fixed_vector", "fixed_vector") { |
|
const std::vector<std::string> v{"Hello", "world", "string"}; |
|
fixed_vector<char[10], 10> fv; |
|
FixedVectorUtils::copy_string_vector_to_cstr_fixed_vector(v, fv); |
|
REQUIRE(fv.size() == 3); |
|
REQUIRE(fv.capacity() == 10); |
|
REQUIRE(strcmp(fv.at(0), "Hello") == 0); |
|
REQUIRE(strcmp(fv.at(1), "world") == 0); |
|
REQUIRE(strcmp(fv.at(2), "string") == 0); |
|
} |
|
|
|
template <typename T> |
|
using fixed_vec_of_five = fixed_vector< T, 5>; |
|
|
|
TEST_CASE("Derived Type with fixed length", "fixed_vector"){ |
|
|
|
fixed_vec_of_five<int> fv5{1,2,3,4,5}; |
|
REQUIRE(fv5.capacity() == 5); |
|
REQUIRE(fv5.size() == 5); |
|
|
|
const std::vector<int> vec = FixedVectorUtils::fixed_vector_to_vector(fv5); |
|
REQUIRE(vec.size() == 5); |
|
} |