1 // Copyright (C) 2019 T. Zachary Laine 2 // 3 // Distributed under the Boost Software License, Version 1.0. (See 4 // accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 #include <boost/stl_interfaces/sequence_container_interface.hpp> 7 8 #include <algorithm> 9 #include <iterator> 10 #include <memory> 11 #include <tuple> 12 13 #include <cassert> 14 15 16 // There's a test that uses this example header; this controls whether the 17 // C++14 version (v1::) of sequence_container_interface gets used, or the 18 // C++20 version (v2::). 19 #if defined(USE_V2) 20 template<typename Derived, boost::stl_interfaces::element_layout Contiguity> 21 using sequence_container_interface = 22 boost::stl_interfaces::v2::sequence_container_interface<Derived>; 23 #else 24 template<typename Derived, boost::stl_interfaces::element_layout Contiguity> 25 using sequence_container_interface = 26 boost::stl_interfaces::v1::sequence_container_interface<Derived, Contiguity>; 27 #endif 28 29 30 //[ static_vector_defn 31 // The sections of member functions below are commented as they are in the 32 // standard for std::vector. Each section has two numbers: the number of 33 // member functions in that section, and the number that are missing, because 34 // they are provided by sequence_container_interface. The purely 35 // allocator-specific members are neither present nor part of the counts. 36 // 37 // We're passing boost::stl_interfaces::contiguous here, so that 38 // sequence_container_interface knows that it should provide data(). 39 template<typename T, std::size_t N> 40 struct static_vector : sequence_container_interface< 41 static_vector<T, N>, 42 boost::stl_interfaces::element_layout::contiguous> 43 { 44 // These are the types required for reversible containers. These must be 45 // user-defined. 46 using value_type = T; 47 using pointer = T *; 48 using const_pointer = T const *; 49 using reference = value_type &; 50 using const_reference = value_type const &; 51 using size_type = std::size_t; 52 using difference_type = std::ptrdiff_t; 53 using iterator = T *; 54 using const_iterator = T const *; 55 using reverse_iterator = boost::stl_interfaces::reverse_iterator<iterator>; 56 using const_reverse_iterator = 57 boost::stl_interfaces::reverse_iterator<const_iterator>; 58 59 // construct/copy/destroy (9 members, skipped 2) 60 // 61 // Constructors and special member functions all must be user-provided. 62 // Were they provided by sequence_container_interface, everything would break, due 63 // to the language rules related to them. However, assignment from 64 // std::initializer_list and the destructor can come from 65 // sequence_container_interface. static_vectorstatic_vector66 static_vector() noexcept : size_(0) {} static_vectorstatic_vector67 explicit static_vector(size_type n) : size_(0) { resize(n); } static_vectorstatic_vector68 explicit static_vector(size_type n, T const & x) : size_(0) 69 { 70 // Note that you must write "this->" before all the member functions 71 // provided by sequence_container_interface, which is slightly annoying. 72 this->assign(n, x); 73 } 74 template< 75 typename InputIterator, 76 typename Enable = std::enable_if_t<std::is_convertible< 77 typename std::iterator_traits<InputIterator>::iterator_category, 78 std::input_iterator_tag>::value>> static_vectorstatic_vector79 static_vector(InputIterator first, InputIterator last) : size_(0) 80 { 81 this->assign(first, last); 82 } static_vectorstatic_vector83 static_vector(std::initializer_list<T> il) : 84 static_vector(il.begin(), il.end()) 85 {} static_vectorstatic_vector86 static_vector(static_vector const & other) : size_(0) 87 { 88 this->assign(other.begin(), other.end()); 89 } static_vectorstatic_vector90 static_vector(static_vector && other) noexcept( 91 noexcept(std::declval<static_vector>().emplace_back( 92 std::move(*other.begin())))) : 93 size_(0) 94 { 95 for (auto & element : other) { 96 emplace_back(std::move(element)); 97 } 98 other.clear(); 99 } operator =static_vector100 static_vector & operator=(static_vector const & other) 101 { 102 this->clear(); 103 this->assign(other.begin(), other.end()); 104 return *this; 105 } operator =static_vector106 static_vector & operator=(static_vector && other) noexcept(noexcept( 107 std::declval<static_vector>().emplace_back(std::move(*other.begin())))) 108 { 109 this->clear(); 110 for (auto & element : other) { 111 emplace_back(std::move(element)); 112 } 113 other.clear(); 114 return *this; 115 } ~static_vectorstatic_vector116 ~static_vector() { this->clear(); } 117 118 // iterators (2 members, skipped 10) 119 // 120 // This section is the first big win. Instead of having to write 12 121 // overloads line begin, cbegin, rbegin, crbegin, etc., we can just write 122 // 2. beginstatic_vector123 iterator begin() noexcept { return reinterpret_cast<T *>(buf_); } endstatic_vector124 iterator end() noexcept 125 { 126 return reinterpret_cast<T *>(buf_ + size_ * sizeof(T)); 127 } 128 129 // capacity (6 members, skipped 2) 130 // 131 // Most of these are not even part of the general requirements, because 132 // some are specific to std::vector and related types. However, we do get 133 // empty and size from sequence_container_interface. max_sizestatic_vector134 size_type max_size() const noexcept { return N; } capacitystatic_vector135 size_type capacity() const noexcept { return N; } resizestatic_vector136 void resize(size_type sz) noexcept 137 { 138 resize_impl(sz, [] { return T(); }); 139 } resizestatic_vector140 void resize(size_type sz, T const & x) noexcept 141 { 142 resize_impl(sz, [&]() -> T const & { return x; }); 143 } reservestatic_vector144 void reserve(size_type n) noexcept { assert(n < capacity()); } shrink_to_fitstatic_vector145 void shrink_to_fit() noexcept {} 146 147 // element access (skipped 8) 148 // data access (skipped 2) 149 // 150 // Another big win. sequence_container_interface provides all of the 151 // overloads of operator[], at, front, back, and data. 152 153 // modifiers (5 members, skipped 9) 154 // 155 // In this section we again get most of the API from 156 // sequence_container_interface. 157 158 // emplace_back does not look very necessary -- just look at its trivial 159 // implementation -- but we can't provide it from 160 // sequence_container_interface, because it is an optional sequence 161 // container interface. We would not want emplace_front to suddenly 162 // appear on our std::vector-like type, and there may be some other type 163 // for which emplace_back is a bad idea. 164 // 165 // However, by providing emplace_back here, we signal to the 166 // sequence_container_interface template that our container is 167 // back-mutation-friendly, and this allows it to provide all the overloads 168 // of push_back and pop_back. 169 template<typename... Args> emplace_backstatic_vector170 reference emplace_back(Args &&... args) 171 { 172 return *emplace(end(), std::forward<Args>(args)...); 173 } 174 template<typename... Args> emplacestatic_vector175 iterator emplace(const_iterator pos, Args &&... args) 176 { 177 auto position = const_cast<T *>(pos); 178 bool const insert_before_end = position < end(); 179 if (insert_before_end) { 180 auto last = end(); 181 emplace_back(std::move(this->back())); 182 std::move_backward(position, last - 1, last); 183 } 184 new (position) T(std::forward<Args>(args)...); 185 if (!insert_before_end) 186 ++size_; 187 return position; 188 } 189 // Note: The iterator category here was upgraded to ForwardIterator 190 // (instead of vector's InputIterator), to ensure linear time complexity. 191 template< 192 typename ForwardIterator, 193 typename Enable = std::enable_if_t<std::is_convertible< 194 typename std::iterator_traits<ForwardIterator>::iterator_category, 195 std::forward_iterator_tag>::value>> 196 iterator insertstatic_vector197 insert(const_iterator pos, ForwardIterator first, ForwardIterator last) 198 { 199 auto position = const_cast<T *>(pos); 200 auto const insertions = std::distance(first, last); 201 assert(this->size() + insertions < capacity()); 202 uninitialized_generate(end(), end() + insertions, [] { return T(); }); 203 std::move_backward(position, end(), end() + insertions); 204 std::copy(first, last, position); 205 size_ += insertions; 206 return position; 207 } erasestatic_vector208 iterator erase(const_iterator f, const_iterator l) 209 { 210 auto first = const_cast<T *>(f); 211 auto last = const_cast<T *>(l); 212 auto end_ = this->end(); 213 auto it = std::move(last, end_, first); 214 for (; it != end_; ++it) { 215 it->~T(); 216 } 217 size_ -= last - first; 218 return first; 219 } swapstatic_vector220 void swap(static_vector & other) 221 { 222 size_type short_size, long_size; 223 std::tie(short_size, long_size) = 224 std::minmax(this->size(), other.size()); 225 for (auto i = size_type(0); i < short_size; ++i) { 226 using std::swap; 227 swap((*this)[i], other[i]); 228 } 229 230 static_vector * longer = this; 231 static_vector * shorter = this; 232 if (this->size() < other.size()) 233 longer = &other; 234 else 235 shorter = &other; 236 237 for (auto it = longer->begin() + short_size, last = longer->end(); 238 it != last; 239 ++it) { 240 shorter->emplace_back(std::move(*it)); 241 } 242 243 longer->resize(short_size); 244 shorter->size_ = long_size; 245 } 246 247 // Since we're getting so many overloads from 248 // sequence_container_interface, and since many of those overloads are 249 // implemented in terms of a user-defined function of the same name, we 250 // need to add quite a few using declarations here. 251 using base_type = sequence_container_interface< 252 static_vector<T, N>, 253 boost::stl_interfaces::element_layout::contiguous>; 254 using base_type::begin; 255 using base_type::end; 256 using base_type::insert; 257 using base_type::erase; 258 259 // comparisons (skipped 6) 260 261 private: 262 template<typename F> uninitialized_generatestatic_vector263 static void uninitialized_generate(iterator f, iterator l, F func) 264 { 265 for (; f != l; ++f) { 266 new (static_cast<void *>(std::addressof(*f))) T(func()); 267 } 268 } 269 template<typename F> resize_implstatic_vector270 void resize_impl(size_type sz, F func) noexcept 271 { 272 assert(sz < capacity()); 273 if (sz < this->size()) 274 erase(begin() + sz, end()); 275 if (this->size() < sz) 276 uninitialized_generate(end(), begin() + sz, func); 277 size_ = sz; 278 } 279 280 alignas(T) unsigned char buf_[N * sizeof(T)]; 281 size_type size_; 282 }; 283 //] 284