From b7683d45d55676ffa7bff7faefc4a1a57c24fb70 Mon Sep 17 00:00:00 2001 From: Myles Busig Date: Thu, 25 Jul 2024 22:55:47 -0600 Subject: [PATCH] Add initial vector implementation --- include/mtl/vector.hpp | 284 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 include/mtl/vector.hpp diff --git a/include/mtl/vector.hpp b/include/mtl/vector.hpp new file mode 100644 index 0000000..6738b93 --- /dev/null +++ b/include/mtl/vector.hpp @@ -0,0 +1,284 @@ +#pragma once + +#include +#include + +#include + +namespace mtl { + +/** + * \brief Resizable statically allocated array + * + * Implements most functions from std::vector, see std::vector documentation + * for function descriptions. Some operations are unimplemented due to the lack + * of dynamic memory usage + */ +template +class ivector { +private: + T* m_arr; + size_t m_size; + const size_t m_capacity; + +public: + using value_type = T; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + ivector(T* arr, size_t capacity) : m_arr(arr), m_capacity(capacity), m_size(0) {} + /** + * \brief Copy constructor + * + * \exception mtl::length_error Thrown if the source vector's size is + * larger than the new capacity + */ + ivector(T* arr, size_t capacity, const ivector& other) : m_arr(arr), m_capacity(capacity) { + if (&other == this) { + return; + } + + if (other.m_size > m_capacity) { + throw mtl::length_error(); + } + + T* x = m_arr; + T* begin = other.m_arr; + T* end = other.m_arr + other.m_size; + for (; begin != end; ++x, ++begin) { + *x = *begin; + } + + m_size = other.m_size; + } + + /** + * \brief Move constructor + * + * \exception mtl::length_error Thrown if the source vector's size is + * larger than the new capacity + */ + ivector(ivector&& other) { + if (&other == this) { + return; + } + + if (other.m_size > m_capacity) { + throw mtl::length_error(); + } + + // We're not able to simply swap pointers because dynamic memory + // is not used. However, we can still swap each individual + // element if possible. + T* x = m_arr; + T* begin = other.m_arr; + T* end = other.m_arr + other.m_size; + for (; begin != end; ++x, ++begin) { + *x = std::move(*begin); + } + + m_size = other.m_size; + } + + ~ivector() {} + + ivector& operator=(ivector rhs) { + return *this; + } + + void assign(size_t count, const T& value) { + throw mtl::system_error(); + } + /** + * \brief Assigns values to the vector + * + * \exception mtl::length_error Thrown if the number of source elements + * is larger than the capacity + */ + template + void assign(It begin, It end) { + m_size = 0; + T* x = m_arr; + + for (; begin != end; ++begin, ++x, ++m_size) { + if (m_size > m_capacity) { + //throw mtl::length_error(); + } + *x = *begin; + } + } + + /** + * \brief Bounds-checked element access + * + * \exception mtl::out_of_range Thrown if pos > size + */ + reference at(size_t pos) { + if (pos > m_size) { + throw mtl::out_of_range(); + } + + return m_arr[pos]; + } + const_reference at(size_t pos) const { + if (pos > m_size) { + throw mtl::out_of_range(); + } + + return m_arr[pos]; + } + + reference operator[](size_t pos) { + return m_arr[pos]; + } + const_reference operator[](size_t pos) const { + return m_arr[pos]; + } + + reference front() { + return m_arr[0]; + } + const_reference front() const { + return m_arr[0]; + } + + reference back() { + return m_arr[m_size - 1]; + } + const_reference back() const { + return m_arr[m_size - 1]; + } + + pointer data() { + return m_arr; + } + const_pointer data() const { + return m_arr; + } + + iterator begin() { + return m_arr; + } + const_iterator begin() const { + return m_arr; + } + const_iterator cbegin() const { + return m_arr; + } + + iterator end() { + return m_arr + m_size; + } + const_iterator end() const { + return m_arr + m_size; + } + const_iterator cend() const { + return m_arr + m_size; + } + + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(cend()); + } + const_reverse_iterator crbegin() const { + return const_reverse_iterator(cend()); + } + + reverse_iterator rend() { + return reverse_iterator(begin()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(cbegin()); + } + const_reverse_iterator crend() const { + return const_reverse_iterator(cbegin()); + } + + bool empty() const { + return m_size == 0; + } + size_t size() const { + return m_size; + } + size_t capacity() const { + return m_capacity; + } + + void clear() { + m_size = 0; + } + + iterator insert(const_iterator pos, T value) { + if (m_size == m_capacity) { + throw mtl::length_error(); + } + + iterator it = end(); + while (it != pos) { + *it = *(it - 1); + --it; + } + + *it = std::move(value); + ++m_size; + + return it; + } + + /** + * \brief Erase the element at the given iterator + * + * \param pos iterator of the element to erase + * \exception nothrow if `T` is nothrow assignable + * \returns iterator of the element after element erased + */ + iterator erase(const_iterator pos) noexcept(std::is_nothrow_assignable::value) { + iterator it = const_cast(pos); + iterator last = end() - 1; + + while (it != last) { + *it = *(it + 1); + ++it; + } + + --m_size; + + return const_cast(pos); + } + + void push_back(T value) { + if (m_size == m_capacity) { + throw mtl::length_error(); + } + + m_arr[m_size] = std::move(value); + ++m_size; + } + + void pop_back() noexcept { + if (m_size > 0) { + --m_size; + } + } +}; + +template +class vector : public ivector { +private: + T m_arr[C]; + +public: + vector() : ivector(m_arr, C) { } +}; + +} // namespace mtl