diff --git a/doc/stl_interfaces.qbk b/doc/stl_interfaces.qbk index 36a70aa..ff1f9c4 100644 --- a/doc/stl_interfaces.qbk +++ b/doc/stl_interfaces.qbk @@ -37,6 +37,9 @@ [/ View Examples ] [import ../example/drop_while_view.cpp] +[/ Container Examples ] +[import ../example/static_vector.cpp] + [/ Images ] [def __note__ [$images/note.png]] diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 26fb4d1..e142be2 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -26,3 +26,5 @@ add_sample(back_insert_iterator) add_sample(reverse_iterator) add_sample(drop_while_view) + +add_sample(static_vector) diff --git a/example/static_vector.cpp b/example/static_vector.cpp new file mode 100644 index 0000000..9af3fc6 --- /dev/null +++ b/example/static_vector.cpp @@ -0,0 +1,202 @@ +// Copyright (C) 2019 T. Zachary Laine +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#include + +#include +#include +#include + +#include + + +//[ static_vector_defn +template +struct static_vector : boost::stl_interfaces::container_interface< + static_vector, + boost::stl_interfaces::contiguous> +{ + // types + using value_type = T; + using pointer = T *; + using const_pointer = T const *; + using reference = value_type &; + using const_reference = value_type const &; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator = T *; + using const_iterator = T const *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + // [vector.cons], construct/copy/destroy + static_vector() noexcept : size_(0) {} + explicit static_vector(size_type n) { this->assign(n, T()); } + static_vector(size_type n, T const & value) { this->assign(n, value); } + template + static_vector(InputIterator first, InputIterator last) + { + this->assign(first, last); + } + static_vector(static_vector const & other) + { + // TODO this->assign(other.begin(), other.end()); + } + static_vector(static_vector && other) noexcept + { + for (auto & element : other) { + emplace_back(std::move(element)); + } + other.clear(); + } + static_vector(std::initializer_list il) + { + // TODO this->assign(il.begin(), il.end()); + } + ~static_vector() { this->clear(); } + static_vector & operator=(static_vector const & other) + { + this->clear(); + // TODO this->assign(other.begin(), other.end()); + return *this; + } + static_vector & operator=(static_vector && other) + { + this->clear(); + for (auto & element : other) { + emplace_back(std::move(element)); + } + other.clear(); + return *this; + } + + // iterators (2 members, skipped 10) + iterator begin() noexcept { return reinterpret_cast(buf_); } + iterator end() noexcept + { + return reinterpret_cast(buf_ + size_ * sizeof(T)); + } + + // [vector.capacity], capacity (6 members, skipped 2) + size_type max_size() const noexcept { return N; } + size_type capacity() const noexcept { return N; } + void resize(size_type sz) { resize(sz, T()); } + void resize(size_type sz, T const & x) + { + if (sz < this->size()) + erase(begin() + sz, end()); + if (this->size() < sz) + std::uninitialized_fill(end(), begin() + sz, x); + } + void reserve(size_type n) {} + void shrink_to_fit() {} + + // element access (skipped 8 members) + // [vector.data], data access (skipped 2 members) + + // [vector.modifiers], modifiers (6 members, skipped 8) + template + reference emplace_back(Args &&... args) + { + assert(this->size() < capacity()); + auto ptr = + new (buf_ + size_ * sizeof(T)) T(std::forward(args)...); + ++size_; + return *ptr; + } + template + iterator emplace(const_iterator pos, Args &&... args) + { + auto position = const_cast(pos); + if (position < end()) { + auto last = end(); + emplace_back(std::move(this->back())); + std::move_backward(position, last - 1, last); + } + new (position) T(std::forward(args)...); + ++size_; + return position; + } + // Note: This iterator category was upgraded to ForwardIterator (instead + // of vector's InputIterator), to ensure linear time complexity. + template + iterator + insert(const_iterator pos, ForwardIterator first, ForwardIterator last) + { + auto position = const_cast(pos); + auto const insertions = std::distance(first, last); + assert(this->size() + insertions < capacity()); + std::uninitialized_fill_n(end(), insertions, T()); + std::move_backward(position, end(), begin() + insertions); + auto retval = std::copy(first, last, position); + size_ += insertions; + return retval; + } + iterator erase(const_iterator pos) + { + auto position = const_cast(pos); + std::move(position + 1, end(), position); + this->back().~T(); + --size_; + return position; + } + iterator erase(const_iterator f, const_iterator last) + { + auto first = const_cast(f); +#if 0 // TODO + auto const end_ = this->cend(); + auto it = std::move(last, end_, first); + for (; it != end_; ++it) { + it->~T(); + } + size_ -= last - first; +#endif + return first; + } + void swap(static_vector & other) + { + size_type short_size, long_size; + std::tie(short_size, long_size) = + std::minmax(this->size(), other.size()); + for (auto i = size_type(0); i < short_size; ++i) { + using std::swap; + swap((*this)[i], other[i]); + } + + static_vector * longer = this; + static_vector * shorter = this; + if (this->size() < other.size()) + longer = &other; + else + shorter = &other; + + for (auto it = longer->begin() + short_size, last = longer->end(); + it != last; + ++it) { + shorter->emplace_back(std::move(*it)); + } + + longer->resize(short_size); + shorter->size_ = long_size; + } + + // TODO: Remove! + void clear() noexcept { erase(begin(), end()); } + +private: + alignas(T) unsigned char buf_[N * sizeof(T)]; + size_type size_; +}; +//] + +// TODO +template struct static_vector; + +int main() +{ + //[ static_vector_usage + static_vector sv; + //] +} diff --git a/include/boost/stl_interfaces/container_interface.hpp b/include/boost/stl_interfaces/container_interface.hpp index eeda0f9..c9d33fe 100644 --- a/include/boost/stl_interfaces/container_interface.hpp +++ b/include/boost/stl_interfaces/container_interface.hpp @@ -109,6 +109,22 @@ namespace boost { namespace stl_interfaces { template struct container_interface : view_interface { +#ifndef BOOST_STL_INTERFACES_DOXYGEN + private: + constexpr Derived & derived() noexcept + { + return static_cast(*this); + } + constexpr const Derived & derived() const noexcept + { + return static_cast(*this); + } +#endif + + public: + // TODO: size() from view_interface is actually returning a + // difference_type, nto a size_type. + template constexpr auto begin() const noexcept(noexcept(std::declval().begin())) @@ -141,7 +157,7 @@ namespace boost { namespace stl_interfaces { template< typename D = Derived, - typename Enable = std::enable_if_t>> + typename Enable = std::enable_if_t::value>> constexpr auto rbegin() noexcept(noexcept( stl_interfaces::make_reverse_iterator(std::declval().end()))) -> decltype( @@ -153,7 +169,7 @@ namespace boost { namespace stl_interfaces { } template< typename D = Derived, - typename Enable = std::enable_if_t>> + typename Enable = std::enable_if_t::value>> constexpr auto rend() noexcept(noexcept( stl_interfaces::make_reverse_iterator(std::declval().begin()))) -> decltype( @@ -229,7 +245,7 @@ namespace boost { namespace stl_interfaces { pos, detail::make_n_iter(x, n), detail::make_n_iter_end(x, n))) { return derived().insert( - pos, detail::make_n_iter(x, n), detail::make_n_iter_end(x, n))); + pos, detail::make_n_iter(x, n), detail::make_n_iter_end(x, n)); } template @@ -355,16 +371,28 @@ namespace boost { namespace stl_interfaces { noexcept(std::declval().size(), std::declval()[i])) -> decltype(std::declval().size(), std::declval()[i]) { - if (derived.size() <= i) - throw TODO; + if (derived().size() <= i) { + throw std::out_of_range( + "Bounds check failed in static_vector::at()"); + } return derived()[i]; } + + template + constexpr auto clear(typename D::size_type i) noexcept( + noexcept(std::declval().erase( + std::declval().begin(), std::declval().end()))) + -> decltype((void)std::declval().erase( + std::declval().begin(), std::declval().end())) + { + return derived().erase(derived().begin(), derived().end()); + } }; /** Implementation of free function `swap()` for all containers derived from `container_interface`. */ template - friend constexpr auto swap( + constexpr auto swap( ContainerInterface & lhs, ContainerInterface & rhs) noexcept(noexcept(lhs.swap(rhs))) -> decltype( diff --git a/include/boost/stl_interfaces/reverse_iterator.hpp b/include/boost/stl_interfaces/reverse_iterator.hpp index 1206666..5b34073 100644 --- a/include/boost/stl_interfaces/reverse_iterator.hpp +++ b/include/boost/stl_interfaces/reverse_iterator.hpp @@ -6,7 +6,7 @@ #ifndef BOOST_STL_INTERFACES_REVERSE_ITERATOR_HPP #define BOOST_STL_INTERFACES_REVERSE_ITERATOR_HPP -#include +#include namespace boost { namespace stl_interfaces { diff --git a/include/boost/stl_interfaces/view_interface.hpp b/include/boost/stl_interfaces/view_interface.hpp index 4c008ba..e785397 100644 --- a/include/boost/stl_interfaces/view_interface.hpp +++ b/include/boost/stl_interfaces/view_interface.hpp @@ -216,5 +216,118 @@ namespace boost { namespace stl_interfaces { } }} +#if 0 +namespace std { + template> + class vector { + public: + // types + using value_type = T; + using allocator_type = Allocator; + using pointer = typename allocator_traits::pointer; + using const_pointer = typename allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = implementation-defined; // see [container.requirements] + using difference_type = implementation-defined; // see [container.requirements] + using iterator = implementation-defined; // see [container.requirements] + using const_iterator = implementation-defined; // see [container.requirements] + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + // [vector.cons], construct/copy/destroy + constexpr vector() noexcept(noexcept(Allocator())) : vector(Allocator()) { } + constexpr explicit vector(const Allocator&) noexcept; + constexpr explicit vector(size_type n, const Allocator& = Allocator()); + constexpr vector(size_type n, const T& value, const Allocator& = Allocator()); + template + constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); + constexpr vector(const vector& x); + constexpr vector(vector&&) noexcept; + constexpr vector(const vector&, const Allocator&); + constexpr vector(vector&&, const Allocator&); + constexpr vector(initializer_list, const Allocator& = Allocator()); + constexpr ~vector(); + constexpr vector& operator=(const vector& x); + constexpr vector& operator=(vector&& x) + noexcept(allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value); + constexpr vector& operator=(initializer_list); + template + constexpr void assign(InputIterator first, InputIterator last); + constexpr void assign(size_type n, const T& u); + constexpr void assign(initializer_list); + constexpr allocator_type get_allocator() const noexcept; + + // iterators + constexpr iterator begin() noexcept; + constexpr const_iterator begin() const noexcept; + constexpr iterator end() noexcept; + constexpr const_iterator end() const noexcept; + constexpr reverse_iterator rbegin() noexcept; + constexpr const_reverse_iterator rbegin() const noexcept; + constexpr reverse_iterator rend() noexcept; + constexpr const_reverse_iterator rend() const noexcept; + + constexpr const_iterator cbegin() const noexcept; + constexpr const_iterator cend() const noexcept; + constexpr const_reverse_iterator crbegin() const noexcept; + constexpr const_reverse_iterator crend() const noexcept; + + // [vector.capacity], capacity + [[nodiscard]] constexpr bool empty() const noexcept; + constexpr size_type size() const noexcept; + constexpr size_type max_size() const noexcept; + constexpr size_type capacity() const noexcept; + constexpr void resize(size_type sz); + constexpr void resize(size_type sz, const T& c); + constexpr void reserve(size_type n); + constexpr void shrink_to_fit(); + + // element access + constexpr reference operator[](size_type n); + constexpr const_reference operator[](size_type n) const; + constexpr const_reference at(size_type n) const; + constexpr reference at(size_type n); + constexpr reference front(); + constexpr const_reference front() const; + constexpr reference back(); + constexpr const_reference back() const; + + // [vector.data], data access + constexpr T* data() noexcept; + constexpr const T* data() const noexcept; + + // [vector.modifiers], modifiers + template constexpr reference emplace_back(Args&&... args); + constexpr void push_back(const T& x); + constexpr void push_back(T&& x); + constexpr void pop_back(); + + template constexpr iterator emplace(const_iterator position, Args&&... args); + constexpr iterator insert(const_iterator position, const T& x); + constexpr iterator insert(const_iterator position, T&& x); + constexpr iterator insert(const_iterator position, size_type n, const T& x); + template + constexpr iterator insert(const_iterator position, + InputIterator first, InputIterator last); + constexpr iterator insert(const_iterator position, initializer_list il); + constexpr iterator erase(const_iterator position); + constexpr iterator erase(const_iterator first, const_iterator last); + constexpr void swap(vector&) + noexcept(allocator_traits::propagate_on_container_swap::value || + allocator_traits::is_always_equal::value); + constexpr void clear() noexcept; + }; + + template>> + vector(InputIterator, InputIterator, Allocator = Allocator()) + -> vector, Allocator>; + + // swap + template + constexpr void swap(vector& x, vector& y) + noexcept(noexcept(x.swap(y))); +} +#endif #endif