Add initial vector implementation

This commit is contained in:
Myles Busig 2024-07-25 22:55:47 -06:00
parent f4e4ef353a
commit b7683d45d5

284
include/mtl/vector.hpp Normal file
View File

@ -0,0 +1,284 @@
#pragma once
#include <cstddef>
#include <iterator>
#include <mtl/exception.hpp>
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 <typename T>
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<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_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 <typename It>
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<T, T>::value) {
iterator it = const_cast<iterator>(pos);
iterator last = end() - 1;
while (it != last) {
*it = *(it + 1);
++it;
}
--m_size;
return const_cast<iterator>(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 <typename T, size_t C>
class vector : public ivector<T> {
private:
T m_arr[C];
public:
vector() : ivector<T>(m_arr, C) { }
};
} // namespace mtl