diff --git a/include/crucible/spanner.h b/include/crucible/spanner.h new file mode 100644 index 0000000..8679f95 --- /dev/null +++ b/include/crucible/spanner.h @@ -0,0 +1,167 @@ +#ifndef CRUCIBLE_SPANNER_H +#define CRUCIBLE_SPANNER_H + +#include "crucible/error.h" + +#include + +namespace crucible { + + using namespace std; + + // C++20 is already using the name "span" for something similar. + template + class Spanner { + public: + using iterator = Iter; + using head_pointer = Head; + using value_type = T; + + template + Spanner(Container& container); + + Spanner(head_pointer begin, iterator end); + Spanner(size_t size, head_pointer begin); + Spanner() = default; + Spanner &operator=(const Spanner &that) = default; + iterator begin() const; + iterator end() const; + value_type *data() const; + value_type &at(size_t n) const; + size_t size() const; + bool empty() const; + void clear(); + value_type &operator[](size_t n) const; + iterator erase(iterator first, iterator last); + iterator erase(iterator first); + private: + head_pointer m_begin; + size_t m_size; + }; + + template + Spanner make_spanner(Container &container) + { + return Spanner(container); + } + + // This template is an attempt to turn a shared_ptr to a container + // into a range view that can be cheaply passed around. + // It probably doesn't quite work in the general case. + template , class Iter = typename Container::value_type *> + Spanner make_spanner(shared_ptr &cont_ptr) + { + shared_ptr head(cont_ptr, cont_ptr->data()); + size_t const size = cont_ptr->size(); + return Spanner(size, head); + } + + template + template + Spanner::Spanner(Container &container) : + m_begin(container.data()), + m_size(container.size()) + { + } + + template + Spanner::Spanner(head_pointer begin, iterator end) : + m_begin(begin), + m_size(end - begin) + { + } + + template + Spanner::Spanner(size_t size, head_pointer begin) : + m_begin(begin), + m_size(size) + { + } + + template + typename Spanner::iterator + Spanner::erase(iterator first, iterator last) + { + auto end = m_begin + m_size; + if (first == m_begin) { + THROW_CHECK0(invalid_argument, last <= end); + m_begin = last; + return last; + } + if (last == end) { + THROW_CHECK0(invalid_argument, m_begin <= first); + m_size = first - m_begin; + return first; + } + THROW_ERROR(invalid_argument, "first != begin() and last != end()"); + } + + template + typename Spanner::iterator + Spanner::erase(iterator first) + { + return erase(first, first + 1); + } + + template + typename Spanner::value_type & + Spanner::operator[](size_t n) const + { + return at(n); + } + + template + void + Spanner::clear() + { + m_begin = head_pointer(); + m_size = 0; + } + + template + bool + Spanner::empty() const + { + return m_size == 0; + } + + template + size_t + Spanner::size() const + { + return m_size; + } + + template + typename Spanner::value_type * + Spanner::data() const + { + return &(*m_begin); + } + + template + typename Spanner::iterator + Spanner::begin() const + { + return data(); + } + + template + typename Spanner::iterator + Spanner::end() const + { + return data() + m_size; + } + + template + typename Spanner::value_type & + Spanner::at(size_t n) const + { + THROW_CHECK2(out_of_range, n, size(), n < size()); + return *(data() + n); + } + +} + + +#endif // CRUCIBLE_SPANNER_H