├── .travis.yml ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── include └── chobo │ ├── flat_map.hpp │ ├── memory_view.hpp │ ├── optional.hpp │ ├── small_vector.hpp │ ├── static_vector.hpp │ ├── vector.hpp │ ├── vector_ptr.hpp │ └── vector_view.hpp ├── test ├── .gitignore ├── CMakeLists.txt ├── README.md ├── doctest │ └── doctest.h └── test.cpp └── tools └── vs └── chobo_static_vector.natvis /.travis.yml: -------------------------------------------------------------------------------- 1 | if: tag IS blank # don't build tags 2 | sudo: true # travis-ci/travis-ci#9033 3 | os: linux 4 | language: cpp 5 | compiler: gcc 6 | notifications: 7 | email: 8 | on_success: change 9 | on_failure: always 10 | git: 11 | depth: 5 12 | 13 | matrix: 14 | include: 15 | # GCC 5 16 | - env: BUILD_TYPE=Debug 17 | - env: BUILD_TYPE=Release 18 | 19 | # Clang 20 | - compiler: clang 21 | env: BUILD_TYPE=Debug 22 | - compiler: clang 23 | env: BUILD_TYPE=Release 24 | 25 | script: 26 | - cd test 27 | - mkdir -p build 28 | - cd build 29 | - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE 30 | - make -j 31 | - ctest --output-on-failure 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Chobo Single-Header Libraries 2 | Copyright (c) 2016-2018 Chobolabs Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Chobo Single-Header Libraries 2 | ============================= 3 | 4 | > **NO LONGER SUPPORTED!** The successor is at [iboB/itlib](https://github.com/iboB/itlib). New libraries and updates to the existing ones will be added there. 5 | 6 | A collection of small single-header C++11 libraries by [Chobolabs](http://www.chobolabs.com/). 7 | 8 | [![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/) [![Standard](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 9 | 10 | ## Build Status 11 | 12 | OS and compiler | Build status 13 | -----|----- 14 | Linux / GCC 4.9 and 5.0 | [![Travis CI](https://travis-ci.org/Chobolabs/chobo-shl.svg?branch=master)](https://travis-ci.org/Chobolabs/chobo-shl) 15 | Windows / MSVC 14 (2015) 32 and 64 bit | [![Appveyor](https://ci.appveyor.com/api/projects/status/vq4932w0wbo83jwg?svg=true)](https://ci.appveyor.com/project/iboB/chobo-shl) 16 | 17 | ## Libraries 18 | 19 | Every `.hpp` file in `include/chobo` is a standalone library and has no dependencies other than the standard lib. 20 | 21 | library | description 22 | --------------------- | -------------------------------- 23 | [**optional.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/optional.hpp) [![Try it online](https://img.shields.io/badge/try%20it-online-yellowgreen.svg)](http://melpon.org/wandbox/permlink/qEYr5Tx3YvTf2k5F) | A class, which can hold an object by value but provides an invalid state as well. Similar to [`boost::optional`](http://www.boost.org/doc/libs/1_61_0/libs/optional/doc/html/index.html). 24 | [**static_vector.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/static_vector.hpp) [![Try it online](https://img.shields.io/badge/try%20it-online-yellowgreen.svg)](http://melpon.org/wandbox/permlink/3b0jJpio1ggfZiwp) | A mix between `std::vector` and `std::array`: A dynamically sized container with fixed capacity (supplied as a template parameter). This allows you to have dynamically sized vectors on the stack or as cache-local value members, as long as you know a big enough capacity beforehand. Similar to [`boost::static_vector`](http://www.boost.org/doc/libs/1_61_0/doc/html/boost/container/static_vector.html). 25 | [**flat_map.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/flat_map.hpp) [![Try it online](https://img.shields.io/badge/try%20it-online-yellowgreen.svg)](http://melpon.org/wandbox/permlink/paeJbUpTq2zBpGF9) | A class with the interface of `std::map` but implemented with an underlying `std::vector`-type container, thus providing better cache locality of the elements. Similar to [`boost::flat_map`](http://www.boost.org/doc/libs/1_61_0/doc/html/boost/container/flat_map.html) with the notable difference that the underlying container can be changed via a template argument (thus making the class not strictly an `std::map` drop-in replacement) 26 | [**vector_ptr.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/vector_ptr.hpp) [![Try it online](https://img.shields.io/badge/try%20it-online-yellowgreen.svg)](http://melpon.org/wandbox/permlink/kUr2aITUjOFwGNSK) | A non-owning `std::vector` pointer. Its purpose is to be used in generic code, which requires a vector. 27 | [**vector_view.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/vector_view.hpp) [![Try it online](https://img.shields.io/badge/try%20it-online-yellowgreen.svg)](http://melpon.org/wandbox/permlink/wX8pazpgYbVhE9Rz) | A dangerous class which gives a view of an `std::vector`, changing the `value_type`. A strictly "I-know-what-I'm-doing" library, it's supposed to be used to obtain a view of `std::vector` as something very similar to `std::vector`. It is **HIGHLY** recommended that X and Y are POD types, where one's size is a multiple of the other. 28 | [**small_vector.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/small_vector.hpp) [![Try it online](https://img.shields.io/badge/try%20it-online-yellowgreen.svg)](http://melpon.org/wandbox/permlink/KkKlXkedPZ7pmaC8) | A mix between a `vector` and a `static_vector`. It's a dynamic array, optimized for use when the number of elements is small. Like `static_vector` is has a static buffer with a given capacity, but can fall back to dynamically allocated memory, should the size exceed it. Similar to [`boost::small_vector`](http://www.boost.org/doc/libs/1_61_0/doc/html/boost/container/small_vector.html) 29 | [**memory_view.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/memory_view.hpp) [![Try it online](https://img.shields.io/badge/try%20it-online-yellowgreen.svg)](http://melpon.org/wandbox/permlink/nJwTnGWRy87WLgdl) | A class which provides a std::vector like interface (sans the methods which might change the size or capacity) to a chunk of memory. 30 | [**vector.hpp**](https://github.com/Chobolabs/chobo-shl/blob/master/include/chobo/vector.hpp) | A `std::vector` drop-in replacement which has no debug checks. To be used in places where the performance is critical, even in "Debug" mode. 31 | 32 | 33 | More detailed documentation is available in each header file. 34 | 35 | The libraries use the C++11 standard and there are no plans for support of earlier ones. 36 | 37 | ## Usage 38 | 39 | Choose one or more libraries that you like and copy somewhere in your include paths. 40 | 41 | Supported compilers: 42 | * VC 2015 or newer 43 | * GCC 4.9 or newer 44 | * Clang 3.2 or newer 45 | 46 | Other C++11 compliant compilers may also work. 47 | 48 | ## Contributing 49 | 50 | Pull requests and issues are welcome. 51 | 52 | Please make one pull request and issue per library, tagging them with the library name in the title with brackets. Example: 53 | 54 | * *[vector_view] Added insert methods* 55 | * *[flat_map] Crash when using with xxxx container* 56 | 57 | ## Copyright 58 | 59 | Copyright © 2016-2018 [Chobolabs Inc.](http://www.chobolabs.com/) 60 | 61 | This libraries are distributed under the MIT Software License. See LICENSE.txt for 62 | further details or copy [here](http://opensource.org/licenses/MIT). 63 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # http://www.appveyor.com/docs/appveyor-yml 2 | 3 | notifications: 4 | - provider: Email 5 | to: 6 | - borislav@chobolabs.com 7 | on_build_status_changed: true 8 | on_build_failure: true 9 | on_build_success: false 10 | 11 | clone_depth: 5 12 | branches: 13 | only: 14 | - master 15 | 16 | matrix: 17 | fast_finish: false 18 | 19 | environment: 20 | matrix: 21 | - platform: x86 22 | configuration: Debug 23 | arch: "Win32" 24 | VS_GEN: "Visual Studio 14 2015" 25 | - platform: x86 26 | configuration: Release 27 | arch: "Win32" 28 | VS_GEN: "Visual Studio 14 2015" 29 | - platform: x64 30 | configuration: Debug 31 | arch: "x64" 32 | VS_GEN: "Visual Studio 14 2015 Win64" 33 | - platform: x64 34 | configuration: Release 35 | arch: "x64" 36 | VS_GEN: "Visual Studio 14 2015 Win64" 37 | 38 | before_build: 39 | - cd test 40 | - if not exist vcbuild mkdir vcbuild 41 | - cd vcbuild 42 | - cmake .. -G "%VS_GEN%" 43 | build_script: 44 | - msbuild chobo-shl-test.sln /p:Configuration=%Configuration%;Platform=%arch% /maxcpucount 45 | test_script: 46 | - ctest -C %configuration% --output-on-failure 47 | -------------------------------------------------------------------------------- /include/chobo/flat_map.hpp: -------------------------------------------------------------------------------- 1 | // chobo-flat-map v1.02 2 | // 3 | // std::map-like class with an underlying vector 4 | // 5 | // MIT License: 6 | // Copyright(c) 2016-2020 Chobolabs Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files(the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and / or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions : 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | // 28 | // VERSION HISTORY 29 | // 30 | // 1.02 (2020-10-14) Added forgotten forward of arguments of emplace 31 | // 1.01 (2016-09-27) Fix for keys with no operator==. Clean up of assignment. 32 | // Added swap method. 33 | // 1.00 (2016-09-23) First public release 34 | // 35 | // 36 | // DOCUMENTATION 37 | // 38 | // Simply include this file wherever you need. 39 | // It defines the class chobo::flat_map, which is an almsot drop-in replacement 40 | // of std::map. Flat map has an optional underlying container which by default 41 | // is std::vector. Thus the items in the map are in a continuous block of 42 | // memory. Thus iterating over the map is cache friendly, at the cost of 43 | // O(n) for insert and erase. 44 | // 45 | // The elements inside (like in std::map) are kept in an order sorted by key. 46 | // Getting a value by key is O(log2 n) 47 | // 48 | // It generally performs much faster than std::map for smaller sets of elements 49 | // 50 | // The difference with std::map, which makes flat_map an not-exactly-drop-in 51 | // replacement is the last template argument: 52 | // * std::map has 53 | // * chobo::flat_map has 54 | // The container must be an std::vector compatible type (chobo::static_vector 55 | // and chobo::vector_ptr are, for example, viable). The container value type 56 | // must be std::pair. 57 | // 58 | // Changing the allocator. 59 | // 60 | // If you want to change the allocator of flat map, you'll have to provide a 61 | // container with the appriate one. Example: 62 | // 63 | // chobo::flat_map< 64 | // string, 65 | // int, 66 | // less, 67 | // std::vector, MyAllocator> 68 | // > mymap 69 | // 70 | // 71 | // Configuration 72 | // 73 | // chobo::flat_map has two configurable settings: 74 | // 75 | // 1. Throw 76 | // Whether to throw exceptions: when `at` is called with a non-existent key. 77 | // By default, like std::map, it throws an std::out_of_range exception. If you define 78 | // CHOBO_FLAT_MAP_NO_THROW before including this header, the exception will 79 | // be substituted by an assertion. 80 | // 81 | // 2. const char* overloads 82 | // By default chobo::flat_map provides overloads for the access methods 83 | // (at, operator[], find, lower_bound, count) for const char* for cases when 84 | // std::string is the key, so that no allocations happen when accessing with 85 | // a C-string of a string literal. 86 | // However if const char* or any other class with implicit conversion from 87 | // const char* is the key, they won't compile. 88 | // If you plan on using flat_map with such keys, you'll need to define 89 | // CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS before including the header 90 | // 91 | // 92 | // TESTS 93 | // 94 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 95 | // To run them, define CHOBO_FLAT_MAP_TEST_WITH_DOCTEST before including 96 | // the header in a file which has doctest.h already included. 97 | // 98 | // Additionally if chobo::static_vector is also available you may define 99 | // CHOBO_FLAT_MAP_TEST_STATIC_VECTOR_WITH_DOCTEST to test flat_map with an 100 | // unrelying static_vector 101 | // 102 | // Additionally if chobo::vector_ptr is also available you may define 103 | // CHOBO_FLAT_MAP_TEST_VECTOR_PTR_WITH_DOCTEST to test flat_map with an 104 | // unrelying vector_ptr 105 | // 106 | #pragma once 107 | 108 | #include 109 | #include 110 | #include 111 | 112 | #if !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS) 113 | #include 114 | #endif 115 | 116 | #if !defined(CHOBO_FLAT_MAP_NO_THROW) 117 | # include 118 | # define _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE() throw std::out_of_range("chobo::flat_map out of range") 119 | #else 120 | # include 121 | # define _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE() assert(false && "chobo::flat_map out of range") 122 | #endif 123 | 124 | namespace chobo 125 | { 126 | 127 | template , typename Container = std::vector>> 128 | class flat_map 129 | { 130 | public: 131 | typedef Key key_type; 132 | typedef T mapped_type; 133 | typedef std::pair value_type; 134 | typedef Container container_type; 135 | typedef Compare key_compare; 136 | typedef value_type& reference; 137 | typedef const value_type& const_reference; 138 | typedef typename container_type::allocator_type allocator_type; 139 | typedef typename std::allocator_traits::pointer pointer; 140 | typedef typename std::allocator_traits::pointer const_pointer; 141 | typedef typename container_type::iterator iterator; 142 | typedef typename container_type::const_iterator const_iterator; 143 | typedef typename container_type::reverse_iterator reverse_iterator; 144 | typedef typename container_type::const_reverse_iterator const_reverse_iterator; 145 | typedef typename container_type::difference_type difference_type; 146 | typedef typename container_type::size_type size_type; 147 | 148 | flat_map() 149 | {} 150 | 151 | explicit flat_map(const key_compare& comp, const allocator_type& alloc = allocator_type()) 152 | : m_cmp(comp) 153 | , m_container(alloc) 154 | {} 155 | 156 | flat_map(const flat_map& x) = default; 157 | flat_map(flat_map&& x) = default; 158 | 159 | flat_map& operator=(const flat_map& x) 160 | { 161 | m_cmp = x.m_cmp; 162 | m_container = x.m_container; 163 | return *this; 164 | } 165 | flat_map& operator=(flat_map&& x) 166 | { 167 | m_cmp = std::move(x.m_cmp); 168 | m_container = std::move(x.m_container); 169 | return *this; 170 | } 171 | 172 | iterator begin() noexcept { return m_container.begin(); } 173 | const_iterator begin() const noexcept { return m_container.begin(); } 174 | iterator end() noexcept { return m_container.end(); } 175 | const_iterator end() const noexcept { return m_container.end(); } 176 | reverse_iterator rbegin() noexcept { return m_container.rbegin(); } 177 | const_reverse_iterator rbegin() const noexcept { return m_container.rbegin(); } 178 | reverse_iterator rend() noexcept { return m_container.rend(); } 179 | const_reverse_iterator rend() const noexcept { return m_container.rend(); } 180 | const_iterator cbegin() const noexcept { return m_container.cbegin(); } 181 | const_iterator cend() const noexcept { return m_container.cend(); } 182 | 183 | bool empty() const noexcept { return m_container.empty(); } 184 | size_type size() const noexcept { return m_container.size(); } 185 | size_type max_size() const noexcept { return m_container.max_size(); } 186 | 187 | void reserve(size_type count) { return m_container.reserve(count); } 188 | size_type capacity() const noexcept { return m_container.capacity(); } 189 | 190 | void clear() noexcept { m_container.clear(); } 191 | 192 | iterator lower_bound(const key_type& k) 193 | { 194 | return std::lower_bound(m_container.begin(), m_container.end(), k, m_cmp); 195 | } 196 | 197 | const_iterator lower_bound(const key_type& k) const 198 | { 199 | return std::lower_bound(m_container.begin(), m_container.end(), k, m_cmp); 200 | } 201 | 202 | iterator find(const key_type& k) 203 | { 204 | auto i = lower_bound(k); 205 | if (i != end() && !m_cmp(k, *i)) 206 | return i; 207 | 208 | return end(); 209 | } 210 | 211 | const_iterator find(const key_type& k) const 212 | { 213 | auto i = lower_bound(k); 214 | if (i != end() && !m_cmp(k, *i)) 215 | return i; 216 | 217 | return end(); 218 | } 219 | 220 | size_t count(const key_type& k) const 221 | { 222 | return find(k) == end() ? 0 : 1; 223 | } 224 | 225 | template 226 | std::pair insert(P&& val) 227 | { 228 | auto i = lower_bound(val.first); 229 | if (i != end() && !m_cmp(val.first, *i)) 230 | { 231 | return { i, false }; 232 | } 233 | 234 | return{ m_container.emplace(i, std::forward

(val)), true }; 235 | } 236 | 237 | std::pair insert(const value_type& val) 238 | { 239 | auto i = lower_bound(val.first); 240 | if (i != end() && !m_cmp(val.first, *i)) 241 | { 242 | return { i, false }; 243 | } 244 | 245 | return{ m_container.emplace(i, val), true }; 246 | } 247 | 248 | template 249 | std::pair emplace(Args&&... args) 250 | { 251 | value_type val(std::forward(args)...); 252 | return insert(std::move(val)); 253 | } 254 | 255 | iterator erase(iterator pos) 256 | { 257 | return m_container.erase(pos); 258 | } 259 | 260 | size_type erase(const key_type& k) 261 | { 262 | auto i = find(k); 263 | if (i == end()) 264 | { 265 | return 0; 266 | } 267 | 268 | erase(i); 269 | return 1; 270 | } 271 | 272 | mapped_type& operator[](const key_type& k) 273 | { 274 | auto i = lower_bound(k); 275 | if (i != end() && !m_cmp(k, *i)) 276 | { 277 | return i->second; 278 | } 279 | 280 | i = m_container.emplace(i, k, mapped_type()); 281 | return i->second; 282 | } 283 | 284 | mapped_type& operator[](key_type&& k) 285 | { 286 | auto i = lower_bound(k); 287 | if (i != end() && !m_cmp(k, *i)) 288 | { 289 | return i->second; 290 | } 291 | 292 | i = m_container.emplace(i, std::forward(k), mapped_type()); 293 | return i->second; 294 | } 295 | 296 | mapped_type& at(const key_type& k) 297 | { 298 | auto i = lower_bound(k); 299 | if (i == end() || m_cmp(*i, k)) 300 | { 301 | _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE(); 302 | } 303 | 304 | return i->second; 305 | } 306 | 307 | const mapped_type& at(const key_type& k) const 308 | { 309 | auto i = lower_bound(k); 310 | if (i == end() || m_cmp(*i, k)) 311 | { 312 | _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE(); 313 | } 314 | 315 | return i->second; 316 | } 317 | 318 | void swap(flat_map& x) 319 | { 320 | std::swap(m_cmp, x.m_cmp); 321 | m_container.swap(x.m_container); 322 | } 323 | 324 | const container_type& container() const noexcept 325 | { 326 | return m_container; 327 | } 328 | 329 | // DANGER! If you're not careful with this function, you may irreversably break the map 330 | container_type& modify_container() noexcept 331 | { 332 | return m_container; 333 | } 334 | 335 | #if !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS) 336 | /////////////////////////////////////////////////////////////////////////////////// 337 | // const char* overloads for maps with an std::string key to avoid allocs 338 | iterator lower_bound(const char* k) 339 | { 340 | static_assert(std::is_same::value, "flat_map::lower_bound(const char*) works only for std::strings"); 341 | static_assert(std::is_same, key_compare>::value, "flat_map::lower_bound(const char*) works only for std::string-s, compared with std::less"); 342 | return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool 343 | { 344 | return strcmp(a.first.c_str(), b) < 0; 345 | }); 346 | } 347 | 348 | const_iterator lower_bound(const char* k) const 349 | { 350 | static_assert(std::is_same::value, "flat_map::lower_bound(const char*) works only for std::strings"); 351 | static_assert(std::is_same, key_compare>::value, "flat_map::lower_bound(const char*) works only for std::string-s, compared with std::less"); 352 | return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool 353 | { 354 | return strcmp(a.first.c_str(), b) < 0; 355 | }); 356 | } 357 | 358 | mapped_type& operator[](const char* k) 359 | { 360 | auto i = lower_bound(k); 361 | if (i != end() && i->first == k) 362 | { 363 | return i->second; 364 | } 365 | 366 | i = m_container.emplace(i, k, mapped_type()); 367 | return i->second; 368 | } 369 | 370 | mapped_type& at(const char* k) 371 | { 372 | auto i = lower_bound(k); 373 | if (i == end() || i->first != k) 374 | { 375 | _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE(); 376 | } 377 | 378 | return i->second; 379 | } 380 | 381 | const mapped_type& at(const char* k) const 382 | { 383 | auto i = lower_bound(k); 384 | if (i == end() || i->first != k) 385 | { 386 | _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE(); 387 | } 388 | 389 | return i->second; 390 | } 391 | 392 | iterator find(const char* k) 393 | { 394 | auto i = lower_bound(k); 395 | if (i != end() && i->first == k) 396 | return i; 397 | 398 | return end(); 399 | } 400 | 401 | const_iterator find(const char* k) const 402 | { 403 | auto i = lower_bound(k); 404 | if (i != end() && i->first == k) 405 | return i; 406 | 407 | return end(); 408 | } 409 | 410 | size_t count(const char* k) const 411 | { 412 | return find(k) == end() ? 0 : 1; 413 | } 414 | 415 | #endif // !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS) 416 | 417 | private: 418 | struct pair_compare 419 | { 420 | pair_compare() = default; 421 | pair_compare(const key_compare& kc) : kcmp(kc) {} 422 | bool operator()(const value_type& a, const key_type& b) const 423 | { 424 | return kcmp(a.first, b); 425 | } 426 | 427 | bool operator()(const key_type& a, const value_type& b) const 428 | { 429 | return kcmp(a, b.first); 430 | } 431 | 432 | key_compare kcmp; 433 | }; 434 | pair_compare m_cmp; 435 | container_type m_container; 436 | }; 437 | 438 | template 439 | bool operator==(const flat_map& a, const flat_map& b) 440 | { 441 | return a.container() == b.container(); 442 | } 443 | 444 | template 445 | bool operator!=(const flat_map& a, const flat_map& b) 446 | { 447 | return a.container() != b.container(); 448 | } 449 | 450 | } 451 | 452 | #if defined(CHOBO_FLAT_MAP_TEST_WITH_DOCTEST) 453 | 454 | #include 455 | 456 | namespace chobo_flat_map_test 457 | { 458 | 459 | // struct with no operator== 460 | struct int_wrap 461 | { 462 | int_wrap() = default; 463 | int_wrap(int i) : val(i) {} 464 | int val; 465 | 466 | struct compare 467 | { 468 | bool operator()(const int_wrap& a, const int_wrap& b) const 469 | { 470 | return a.val < b.val; 471 | } 472 | }; 473 | }; 474 | 475 | } 476 | 477 | TEST_CASE("[flat_map] test") 478 | { 479 | using namespace chobo; 480 | using namespace chobo_flat_map_test; 481 | 482 | flat_map ifmap; 483 | CHECK(ifmap.empty()); 484 | CHECK(ifmap.size() == 0); 485 | CHECK(ifmap.capacity() == 0); 486 | CHECK(ifmap.begin() == ifmap.end()); 487 | 488 | ifmap[1] = 3.2f; 489 | CHECK(ifmap.size() == 1); 490 | 491 | auto ifit = ifmap.begin(); 492 | CHECK(ifit->first == 1); 493 | CHECK(ifit->second == 3.2f); 494 | CHECK(ifmap[1] == 3.2f); 495 | CHECK(ifmap.at(1) == 3.2f); 496 | CHECK(ifmap.count(1) == 1); 497 | CHECK(ifmap.count(5) == 0); 498 | 499 | ++ifit; 500 | CHECK(ifit == ifmap.end()); 501 | 502 | auto res = ifmap.insert(std::make_pair(6, 3.14f)); 503 | CHECK(res.second); 504 | CHECK(res.first == ifmap.begin() + 1); 505 | 506 | res = ifmap.emplace(3, 5.5f); 507 | CHECK(res.second); 508 | CHECK(res.first == ifmap.begin() + 1); 509 | 510 | res = ifmap.emplace(6, 8.f); 511 | CHECK(!res.second); 512 | CHECK(res.first == ifmap.begin() + 2); 513 | 514 | ifmap[2] = 5; 515 | ifmap[52] = 15; 516 | ifmap[12] = 1; 517 | CHECK(ifmap.size() == 6); 518 | 519 | auto cmp = [](const flat_map::value_type& a, const flat_map::value_type& b) -> bool 520 | { 521 | return a.first < b.first; 522 | }; 523 | 524 | CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp)); 525 | 526 | ifmap.erase(12); 527 | CHECK(ifmap.size() == 5); 528 | 529 | CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp)); 530 | 531 | ifit = ifmap.find(12); 532 | CHECK(ifit == ifmap.end()); 533 | 534 | ifit = ifmap.find(6); 535 | CHECK(ifit != ifmap.end()); 536 | ifmap.erase(ifit); 537 | 538 | CHECK(ifmap.size() == 4); 539 | CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp)); 540 | ifit = ifmap.find(6); 541 | CHECK(ifit == ifmap.end()); 542 | 543 | // 544 | 545 | flat_map simap; 546 | 547 | CHECK(simap["123"] == 0); 548 | 549 | CHECK(simap.begin()->first.c_str() == "123"); 550 | 551 | ++simap["asd"]; 552 | 553 | auto siit = simap.find("asd"); 554 | CHECK(siit != simap.end()); 555 | CHECK(siit->second == 1); 556 | CHECK(siit == simap.begin() + 1); 557 | 558 | CHECK(simap.count("bababa") == 0); 559 | CHECK(simap.count("asd") == 1); 560 | 561 | std::string asd = "asd"; 562 | CHECK(simap.at(asd) == simap.at("asd")); 563 | 564 | simap["0The quick brown fox jumps over the lazy dog"] = 555; 565 | CHECK(simap.begin()->first[1] == 'T'); 566 | const void* cstr = simap.begin()->first.c_str(); 567 | 568 | auto simap2 = std::move(simap); 569 | CHECK(simap.empty()); 570 | CHECK(simap2.begin()->first.c_str() == cstr); 571 | 572 | simap = std::move(simap2); 573 | CHECK(simap2.empty()); 574 | CHECK(simap.begin()->first.c_str() == cstr); 575 | 576 | CHECK(simap2 != simap); 577 | simap2 = simap; 578 | CHECK(simap2 == simap); 579 | 580 | // no == comparable tests 581 | flat_map iwmap; 582 | iwmap[5] = 1; 583 | iwmap[20] = 15; 584 | iwmap[10] = 5; 585 | 586 | auto iwi = iwmap.emplace(3, 4); 587 | CHECK(iwi.second == true); 588 | CHECK(iwi.first == iwmap.begin()); 589 | 590 | CHECK(iwmap.begin()->first.val == 3); 591 | CHECK(iwmap.begin()->second == 4); 592 | CHECK(iwmap.rbegin()->first.val == 20); 593 | CHECK(iwmap.rbegin()->second == 15); 594 | CHECK(iwmap.at(10) == 5); 595 | 596 | iwi = iwmap.insert(std::pair(11, 6)); 597 | CHECK(iwi.second == true); 598 | CHECK(iwi.first + 2 == iwmap.end()); 599 | 600 | CHECK(iwmap[11] == 6); 601 | 602 | iwi = iwmap.emplace(10, 55); 603 | CHECK(iwi.second == false); 604 | CHECK(iwi.first->second == 5); 605 | 606 | CHECK(iwmap.find(18) == iwmap.end()); 607 | CHECK(iwmap.find(11) != iwmap.end()); 608 | 609 | const auto ciwmap = iwmap; 610 | 611 | CHECK(ciwmap.begin()->first.val == 3); 612 | CHECK(ciwmap.begin()->second == 4); 613 | CHECK(ciwmap.rbegin()->first.val == 20); 614 | CHECK(ciwmap.rbegin()->second == 15); 615 | CHECK(ciwmap.at(10) == 5); 616 | 617 | CHECK(ciwmap.find(18) == ciwmap.end()); 618 | CHECK(ciwmap.find(11) != ciwmap.end()); 619 | 620 | // swap 621 | flat_map m1, m2; 622 | m1.reserve(10); 623 | m1[1] = 2; 624 | m1[2] = 5; 625 | auto m1c = m1.capacity(); 626 | 627 | CHECK(m2.capacity() == 0); 628 | m1.swap(m2); 629 | 630 | CHECK(m2.size() == 2); 631 | CHECK(m2.capacity() == m1c); 632 | CHECK(m1.capacity() == 0); 633 | 634 | // self usurp 635 | m2 = m2; 636 | CHECK(m2.size() == 2); 637 | CHECK(m2.capacity() == m1c); 638 | } 639 | 640 | #if defined(CHOBO_FLAT_MAP_TEST_STATIC_VECTOR_WITH_DOCTEST) 641 | 642 | TEST_CASE("[flat_map] static_vector test") 643 | { 644 | using namespace chobo; 645 | 646 | flat_map, static_vector, 10>> smap; 647 | CHECK(smap.empty()); 648 | CHECK(smap.size() == 0); 649 | CHECK(smap.capacity() == 10); 650 | CHECK(smap.begin() == smap.end()); 651 | 652 | smap[1] = 3; 653 | CHECK(smap.size() == 1); 654 | 655 | auto ifit = smap.begin(); 656 | CHECK(ifit->first == 1); 657 | CHECK(ifit->second == 3); 658 | CHECK(smap[1] == 3); 659 | CHECK(smap.at(1) == 3); 660 | CHECK(smap.count(1) == 1); 661 | CHECK(smap.count(5) == 0); 662 | 663 | ++ifit; 664 | CHECK(ifit == smap.end()); 665 | 666 | auto res = smap.insert(std::make_pair(6, 3)); 667 | CHECK(res.second); 668 | CHECK(res.first == smap.begin() + 1); 669 | 670 | res = smap.emplace(3, 5); 671 | CHECK(res.second); 672 | CHECK(res.first == smap.begin() + 1); 673 | 674 | res = smap.emplace(6, 8); 675 | CHECK(!res.second); 676 | CHECK(res.first == smap.begin() + 2); 677 | 678 | smap[2] = 5; 679 | smap[52] = 15; 680 | smap[12] = 1; 681 | CHECK(smap.size() == 6); 682 | 683 | auto cmp = [](const flat_map::value_type& a, const flat_map::value_type& b) -> bool 684 | { 685 | return a.first < b.first; 686 | }; 687 | 688 | CHECK(std::is_sorted(smap.begin(), smap.end(), cmp)); 689 | 690 | smap.erase(12); 691 | CHECK(smap.size() == 5); 692 | 693 | CHECK(std::is_sorted(smap.begin(), smap.end(), cmp)); 694 | 695 | ifit = smap.find(12); 696 | CHECK(ifit == smap.end()); 697 | 698 | ifit = smap.find(6); 699 | CHECK(ifit != smap.end()); 700 | smap.erase(ifit); 701 | 702 | CHECK(smap.size() == 4); 703 | CHECK(std::is_sorted(smap.begin(), smap.end(), cmp)); 704 | ifit = smap.find(6); 705 | CHECK(ifit == smap.end()); 706 | } 707 | 708 | #endif 709 | 710 | #if defined(CHOBO_FLAT_MAP_TEST_VECTOR_PTR_WITH_DOCTEST) 711 | 712 | TEST_CASE("[flat_map] vector_ptr test") 713 | { 714 | using namespace chobo; 715 | flat_map, vector_ptr>> smap; 716 | 717 | std::vector> vec; 718 | smap.modify_container().reset(&vec); 719 | 720 | smap[1] = '1'; 721 | smap[3] = '3'; 722 | 723 | CHECK(smap.at(3) == '3'); 724 | 725 | auto smap2 = smap; 726 | CHECK(smap2.size() == 2); 727 | CHECK(smap2[1] == '1'); 728 | CHECK(smap2.at(3) == '3'); 729 | 730 | smap2[0] = '0'; 731 | 732 | CHECK(smap.size() == 3); 733 | CHECK(smap[0] == '0'); 734 | 735 | smap.clear(); 736 | 737 | CHECK(smap2.empty()); 738 | } 739 | 740 | #endif 741 | 742 | 743 | #endif 744 | 745 | -------------------------------------------------------------------------------- /include/chobo/memory_view.hpp: -------------------------------------------------------------------------------- 1 | // chobo-memory-view v1.02 2 | // 3 | // A view of a chunk of memory which makes it look as a std::vector sans 4 | // the size modifying functions 5 | // 6 | // MIT License: 7 | // Copyright(c) 2016-2017 Chobolabs Inc. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files(the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and / or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions : 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | // 29 | // VERSION HISTORY 30 | // 31 | // 1.02 (2017-06-20) Explicit operator bool 32 | // 1.01 (2017-03-10) Added const_memory_view constructor from memory_view 33 | // 1.00 (2016-11-09) First public release 34 | // 35 | // 36 | // DOCUMENTATION 37 | // 38 | // Simply include this file wherever you need. 39 | // A memory view is a class which attaches to an chunk of memory and provides 40 | // a view to it (optionally changing its type) with a std::vector-like 41 | // interface, excluding the methods which change a vector's size. 42 | // 43 | // The library includes two classes, for viewing const and non-const memory: 44 | // memory_view, and const_memory_view. To automatically generate 45 | // the appropriate pointer, use `make_memory_view(ptr, size)`. 46 | // where size is the number of elements your view will have 47 | // 48 | // `make_memory_view` also has an overload for std::vector like types which 49 | // can be useful if you want a view at a slise of the vector 50 | // you can use it like this: `make_memory_view(vector, offset = 0)` 51 | // where the offset is from the beginning of the vector. 52 | // 53 | // It is the responsibility of the user to make sure the pointer given to a 54 | // view remains valid throughout the view's lifetime. All methods of the library 55 | // assume this. 56 | // 57 | // Copying a memory view will copy the unrerlying pointer (and not its data), 58 | // resulting in another view to the same memory. 59 | // 60 | // To copy the data from one memory view to another, you can use std::copy 61 | // 62 | // Example: 63 | // 64 | // int* buf = new int[10]; 65 | // auto view = make_memory_view(buf, 10); 66 | // for (auto& i : view) i = 0; // fill buf with zeroes 67 | // 68 | // Reference: 69 | // 70 | // memory_view has the most common std::vector methods and operators 71 | // const_memory_view has the most common std::vector const methods and operators 72 | // Both exclude the methods and operators which would result in changing the 73 | // a vector's size. 74 | // 75 | // Besides those, both classes define a ptr-like interface: 76 | // 77 | // void reset(void* ptr = nullptr, size_t size = 0) - reset the internal pointer 78 | // void reset(size_t size) - change the size, without changing the pointer 79 | // operator bool() const - returns whether the internal pointer is valid 80 | // T* get() noexcept - return the internal pointer (same as data()) 81 | // 82 | // 83 | // Configuration 84 | // 85 | // chobo::memory_view a single configurable setting: 86 | // 87 | // Config bounds checks: 88 | // 89 | // By default bounds checks are made in debug mode (via an assert) when accessing 90 | // elements (with `at` or `[]`). Iterators are not checked (yet...) 91 | // 92 | // To disable them, you can define CHOBO_MEMORY_VIEW_NO_DEBUG_BOUNDS_CHECK 93 | // before including the header. 94 | // 95 | // TESTS 96 | // 97 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 98 | // To run them, define CHOBO_MEMORY_VIEW_TEST_WITH_DOCTEST before including 99 | // the header in a file which has doctest.h already included. 100 | // 101 | #pragma once 102 | 103 | #include 104 | #include 105 | 106 | #if defined(CHOBO_MEMORY_VIEW_NO_DEBUG_BOUNDS_CHECK) 107 | # define _CHOBO_MEMORY_VIEW_BOUNDS_CHECK(i) 108 | #else 109 | # include 110 | # define _CHOBO_MEMORY_VIEW_BOUNDS_CHECK(i) assert((i) < this->size()) 111 | #endif 112 | 113 | namespace chobo 114 | { 115 | 116 | template 117 | class memory_view 118 | { 119 | public: 120 | 121 | using value_type = T; 122 | using size_type = size_t; 123 | using difference_type = ptrdiff_t; 124 | using reference = T&; 125 | using const_reference = const T&; 126 | using pointer = T*; 127 | using const_pointer = const T*; 128 | using iterator = T*; 129 | using const_iterator = const T*; 130 | using reverse_iterator = std::reverse_iterator; 131 | using const_reverse_iterator = std::reverse_iterator; 132 | 133 | memory_view() = default; 134 | 135 | memory_view(T* ptr, size_t size) 136 | : m_ptr(ptr) 137 | , m_size(size) 138 | {} 139 | 140 | memory_view(const memory_view&) = default; 141 | memory_view(memory_view&&) = default; 142 | 143 | memory_view& operator=(const memory_view&) = default; 144 | memory_view& operator=(memory_view&&) = default; 145 | 146 | void reset(T* ptr = nullptr, size_t size = 0) 147 | { 148 | m_ptr = ptr; 149 | m_size = size; 150 | } 151 | 152 | void reset(size_t size) 153 | { 154 | m_size = size; 155 | } 156 | 157 | explicit operator bool() const 158 | { 159 | return !!m_ptr; 160 | } 161 | 162 | pointer get() noexcept { return m_ptr; } 163 | const_pointer get() const noexcept { return m_ptr; } 164 | 165 | const_reference at(size_type i) const 166 | { 167 | _CHOBO_MEMORY_VIEW_BOUNDS_CHECK(i); 168 | return *(m_ptr + i); 169 | } 170 | 171 | reference at(size_type i) 172 | { 173 | _CHOBO_MEMORY_VIEW_BOUNDS_CHECK(i); 174 | return *(m_ptr + i); 175 | } 176 | 177 | const_reference operator[](size_type i) const 178 | { 179 | return at(i); 180 | } 181 | 182 | reference operator[](size_type i) 183 | { 184 | return at(i); 185 | } 186 | 187 | const_reference front() const 188 | { 189 | return at(0); 190 | } 191 | 192 | reference front() 193 | { 194 | return at(0); 195 | } 196 | 197 | const_reference back() const 198 | { 199 | return at(m_size - 1); 200 | } 201 | 202 | reference back() 203 | { 204 | return at(m_size - 1); 205 | } 206 | 207 | const_pointer data() const noexcept 208 | { 209 | return m_ptr; 210 | } 211 | 212 | pointer data() noexcept 213 | { 214 | return m_ptr; 215 | } 216 | 217 | // iterators 218 | iterator begin() noexcept 219 | { 220 | return data(); 221 | } 222 | 223 | const_iterator begin() const noexcept 224 | { 225 | return data(); 226 | } 227 | 228 | const_iterator cbegin() const noexcept 229 | { 230 | return data(); 231 | } 232 | 233 | iterator end() noexcept 234 | { 235 | return data() + m_size; 236 | } 237 | 238 | const_iterator end() const noexcept 239 | { 240 | return data() + m_size; 241 | } 242 | 243 | const_iterator cend() const noexcept 244 | { 245 | return data() + m_size; 246 | } 247 | 248 | reverse_iterator rbegin() noexcept 249 | { 250 | return reverse_iterator(end()); 251 | } 252 | 253 | const_reverse_iterator rbegin() const noexcept 254 | { 255 | return const_reverse_iterator(end()); 256 | } 257 | 258 | const_reverse_iterator crbegin() const noexcept 259 | { 260 | return const_reverse_iterator(end()); 261 | } 262 | 263 | reverse_iterator rend() noexcept 264 | { 265 | return reverse_iterator(begin()); 266 | } 267 | 268 | const_reverse_iterator rend() const noexcept 269 | { 270 | return const_reverse_iterator(begin()); 271 | } 272 | 273 | const_reverse_iterator crend() const noexcept 274 | { 275 | return const_reverse_iterator(begin()); 276 | } 277 | 278 | // capacity 279 | bool empty() const noexcept 280 | { 281 | return m_size == 0; 282 | } 283 | 284 | size_t size() const noexcept 285 | { 286 | return m_size; 287 | } 288 | 289 | private: 290 | T* m_ptr = nullptr; 291 | size_t m_size = 0; 292 | }; 293 | 294 | /////////////////////////////////////////////////////////////////////////////// 295 | 296 | template 297 | class const_memory_view 298 | { 299 | public: 300 | 301 | using value_type = T; 302 | using size_type = size_t; 303 | using difference_type = ptrdiff_t; 304 | using reference = T&; 305 | using const_reference = const T&; 306 | using pointer = T*; 307 | using const_pointer = const T*; 308 | using iterator = T*; 309 | using const_iterator = const T*; 310 | using reverse_iterator = std::reverse_iterator; 311 | using const_reverse_iterator = std::reverse_iterator; 312 | 313 | const_memory_view() = default; 314 | 315 | const_memory_view(const T* ptr, size_t size) 316 | : m_ptr(ptr) 317 | , m_size(size) 318 | {} 319 | 320 | const_memory_view(const memory_view& view) 321 | { 322 | m_ptr = view.data(); 323 | m_size = view.size(); 324 | } 325 | 326 | const_memory_view(const const_memory_view&) = default; 327 | const_memory_view(const_memory_view&&) = default; 328 | 329 | const_memory_view& operator=(const const_memory_view&) = default; 330 | const_memory_view& operator=(const_memory_view&&) = default; 331 | 332 | void reset(const T* ptr = nullptr, size_t size = 0) 333 | { 334 | m_ptr = ptr; 335 | m_size = size; 336 | } 337 | 338 | void reset(size_t size) 339 | { 340 | m_size = size; 341 | } 342 | 343 | explicit operator bool() const 344 | { 345 | return !!m_ptr; 346 | } 347 | 348 | const_pointer get() const noexcept { return m_ptr; } 349 | 350 | const_reference at(size_type i) const 351 | { 352 | _CHOBO_MEMORY_VIEW_BOUNDS_CHECK(i); 353 | return *(m_ptr + i); 354 | } 355 | 356 | const_reference operator[](size_type i) const 357 | { 358 | return at(i); 359 | } 360 | 361 | const_reference front() const 362 | { 363 | return at(0); 364 | } 365 | 366 | const_reference back() const 367 | { 368 | return at(m_size - 1); 369 | } 370 | 371 | const_pointer data() const noexcept 372 | { 373 | return m_ptr; 374 | } 375 | 376 | // iterators 377 | 378 | const_iterator begin() const noexcept 379 | { 380 | return data(); 381 | } 382 | 383 | const_iterator cbegin() const noexcept 384 | { 385 | return data(); 386 | } 387 | 388 | const_iterator end() const noexcept 389 | { 390 | return data() + m_size; 391 | } 392 | 393 | const_iterator cend() const noexcept 394 | { 395 | return data() + m_size; 396 | } 397 | 398 | const_reverse_iterator rbegin() const noexcept 399 | { 400 | return const_reverse_iterator(end()); 401 | } 402 | 403 | const_reverse_iterator crbegin() const noexcept 404 | { 405 | return const_reverse_iterator(end()); 406 | } 407 | 408 | const_reverse_iterator rend() const noexcept 409 | { 410 | return const_reverse_iterator(begin()); 411 | } 412 | 413 | const_reverse_iterator crend() const noexcept 414 | { 415 | return const_reverse_iterator(begin()); 416 | } 417 | 418 | // capacity 419 | bool empty() const noexcept 420 | { 421 | return m_size == 0; 422 | } 423 | 424 | size_t size() const noexcept 425 | { 426 | return m_size; 427 | } 428 | 429 | private: 430 | const T* m_ptr = nullptr; 431 | size_t m_size = 0; 432 | }; 433 | 434 | /////////////////////////////////////////////////////////////////////////////// 435 | 436 | template 437 | memory_view make_memory_view(T* ptr, size_t size) 438 | { 439 | return memory_view(ptr, size); 440 | } 441 | 442 | template 443 | const_memory_view make_memory_view(const T* ptr, size_t size) 444 | { 445 | return const_memory_view(ptr, size); 446 | } 447 | 448 | template 449 | const_memory_view make_memory_view(const T(&ar)[N]) 450 | { 451 | return const_memory_view(ar, N); 452 | } 453 | 454 | template 455 | auto make_memory_view(Container& c, size_t offset = 0) -> memory_view 456 | { 457 | return memory_view(c.data() + offset, c.size() - offset); 458 | } 459 | 460 | template 461 | auto make_memory_view(const Container& c, size_t offset = 0) -> const_memory_view 462 | { 463 | return const_memory_view(c.data() + offset, c.size() - offset); 464 | } 465 | 466 | } 467 | 468 | #if defined(CHOBO_MEMORY_VIEW_TEST_WITH_DOCTEST) 469 | 470 | #include 471 | 472 | TEST_CASE("[memory_view] test") 473 | { 474 | using namespace chobo; 475 | 476 | memory_view e; 477 | CHECK(!e); 478 | CHECK(e.size() == 0); 479 | CHECK(e.begin() == e.end()); 480 | CHECK(e.cbegin() == e.cend()); 481 | CHECK(e.empty()); 482 | CHECK(e.data() == nullptr); 483 | CHECK(e.get() == nullptr); 484 | 485 | int i[] = { 0,2,3,4 }; 486 | 487 | e.reset(i, 4); 488 | CHECK(e); 489 | CHECK(e.size() == 4); 490 | CHECK(e.begin() + 4 == e.end()); 491 | CHECK(e.cbegin() + 4 == e.cend()); 492 | CHECK(!e.empty()); 493 | CHECK(e.data() == i); 494 | CHECK(e.get() == i); 495 | 496 | e.front() = 1; 497 | CHECK(i[0] == 1); 498 | 499 | auto iview = make_memory_view(i, 4); 500 | static_assert(std::is_same, decltype(iview)>::value, "iview must be memory_view"); 501 | CHECK(iview.size() == 4); 502 | CHECK(iview.data() == e.data()); 503 | CHECK(iview.at(0) == *e.begin()); 504 | 505 | auto iview2 = iview; 506 | CHECK(iview2.data() == iview.get()); 507 | 508 | std::vector ivec = { 6, 7, 8, 9, 10 }; 509 | 510 | auto vecview = make_memory_view(ivec); 511 | static_assert(std::is_same, decltype(vecview)>::value, "vecview must be memory_view"); 512 | CHECK(vecview.size() == 5); 513 | CHECK(vecview.data() == ivec.data()); 514 | 515 | auto slice = make_memory_view(ivec, 1); 516 | static_assert(std::is_same, decltype(slice)>::value, "slice must be memory_view"); 517 | CHECK(slice.size() == 4); 518 | 519 | auto sb = slice.begin(); 520 | for (auto i : iview) 521 | { 522 | CHECK(i + 6 == *sb); 523 | ++sb; 524 | } 525 | 526 | sb = vecview.begin(); 527 | for (auto i : slice) 528 | { 529 | CHECK(i - 1 == *sb); 530 | ++sb; 531 | } 532 | } 533 | 534 | 535 | 536 | TEST_CASE("[const_memory_view] test") 537 | { 538 | using namespace chobo; 539 | 540 | const_memory_view e; 541 | CHECK(!e); 542 | CHECK(e.size() == 0); 543 | CHECK(e.begin() == e.end()); 544 | CHECK(e.cbegin() == e.cend()); 545 | CHECK(e.empty()); 546 | CHECK(e.data() == nullptr); 547 | CHECK(e.get() == nullptr); 548 | 549 | const int i[] = { 1,2,3,4 }; 550 | 551 | e.reset(i, 4); 552 | CHECK(e); 553 | CHECK(e.size() == 4); 554 | CHECK(e.begin() + 4 == e.end()); 555 | CHECK(e.cbegin() + 4 == e.cend()); 556 | CHECK(!e.empty()); 557 | CHECK(e.data() == i); 558 | CHECK(e.get() == i); 559 | 560 | auto iview = make_memory_view(i, 4); 561 | static_assert(std::is_same, decltype(iview)>::value, "iview must be memory_view"); 562 | CHECK(iview.size() == 4); 563 | CHECK(iview.data() == e.data()); 564 | CHECK(iview.at(0) == *e.begin()); 565 | 566 | auto iview2 = iview; 567 | CHECK(iview2.data() == iview.get()); 568 | 569 | const std::vector ivec = { 6, 7, 8, 9, 10 }; 570 | 571 | auto vecview = make_memory_view(ivec); 572 | CHECK(vecview.size() == 5); 573 | static_assert(std::is_same, decltype(vecview)>::value, "vecview must be memory_view"); 574 | CHECK(vecview.size() == 5); 575 | CHECK(vecview.data() == ivec.data()); 576 | 577 | auto slice = make_memory_view(ivec, 1); 578 | static_assert(std::is_same, decltype(slice)>::value, "slice must be memory_view"); 579 | 580 | CHECK(slice.size() == 4); 581 | 582 | auto sb = slice.begin(); 583 | for (auto i : iview) 584 | { 585 | CHECK(i + 6 == *sb); 586 | ++sb; 587 | } 588 | 589 | sb = vecview.begin(); 590 | for (auto i : slice) 591 | { 592 | CHECK(i - 1 == *sb); 593 | ++sb; 594 | } 595 | } 596 | 597 | #endif 598 | -------------------------------------------------------------------------------- /include/chobo/optional.hpp: -------------------------------------------------------------------------------- 1 | // chobo-optional v1.02 2 | // 3 | // A value wrapper with an optional invalid state. 4 | // 5 | // MIT License: 6 | // Copyright(c) 2016 Chobolabs Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files(the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and / or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions : 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | // 28 | // VERSION HISTORY 29 | // 30 | // 1.02 (2017-06-20) Explicit operator bool 31 | // 1.01 (2016-09-27) Qualified operator new 32 | // 1.00 (2016-09-23) First public release 33 | // 34 | // 35 | // DOCUMENTATION 36 | // 37 | // Simply include this file wherever you need. 38 | // chobo::optional is similar to boost::optional. It's a value wrapper 39 | // class. It simply holds a value of some type, but has a boolean context 40 | // which gives you an invalid state for the value. 41 | // 42 | // Example: 43 | // 44 | // chobo::optional foo; // no string is constructed 45 | // bool b = foo; // b is false 46 | // std::string xxx = *foo; // ERROR! undefined behavior, foo has no internal string 47 | // foo = "asd"; // a string is constructed within foo 48 | // b = foo; // b is true, foo is valid 49 | // const std::string& asd = *foo; // asd is a reference to the internal string object of foo 50 | // size_t s = foo->length(); // s is 3 51 | // 52 | // chobo::optional i = 0; 53 | // bool b = i; // b is true. Although the internal int is zero, i is a valid optional 54 | // b = *i; // b is false since operator* dereferences optional to its internal object 55 | // 56 | // Reference: 57 | // 58 | // optional() - constructs empty invalid optional 59 | // optional(const optional& o) - copy constructor. Copies the internal value if any. 60 | // optional(optional&& o) - move constructor. Moves the internal value if any. 61 | // optional(const value_type& v) - constructs optional with a copy of v 62 | // optional(value_type&& v) - constructs optional by moving v 63 | // optional(bool condition, const value_type& val) - constructs optional based on 64 | // condition. If true, copies val, otherwise constructs an invalid optional 65 | // 66 | // optional& operator=(const optional& o) 67 | // optional& operator=(optional&& o) 68 | // optional& operator=(const value_type& t) 69 | // optional& operator=(value_type&& t) - assignment operators. Destroy the previous 70 | // object if valid. 71 | // 72 | // bool is_valid() const 73 | // operator bool() const - true if the underlying object is in a valid state 74 | // 75 | // template 76 | // void construct(Args&&... args) - constructs the underlying object by calling 77 | // its constructor with args. Destroys the previous one if valid. 78 | // void destroy() - destroys the underlying object and switches to invalid state 79 | // void clear() - alias of destroy() 80 | // 81 | // T& get() 82 | // T& get() const 83 | // T& operator*() 84 | // const T& operator*() const - access the underlying object 85 | // 86 | // T& get_or(T& val) 87 | // const T& get_or(const T& val) const - get the underlying object or return val 88 | // if invalid. 89 | // 90 | // T* operator->() 91 | // const T* operator->() const - call methods of the underlying object 92 | // 93 | // External: 94 | // 95 | // template 96 | // bool operator==(const optional& a, const optional& b) 97 | // template 98 | // bool operator!=(const optional& a, const optional& b) - compare underlying 99 | // objects if both are valid, else compare validity 100 | // 101 | // template 102 | // bool operator==(const optional& a, const T& b) 103 | // template 104 | // bool operator!=(const optional& a, const T& b) - compare underlying objects 105 | // if a is valid, else check a validity 106 | // 107 | // 108 | // TESTS 109 | // 110 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 111 | // To run them, define CHOBO_OPTIONAL_TEST_WITH_DOCTEST before including 112 | // the header in a file which has doctest.h already included. 113 | // 114 | #pragma once 115 | 116 | #include 117 | #include 118 | 119 | namespace chobo 120 | { 121 | 122 | template 123 | class optional 124 | { 125 | public: 126 | typedef T value_type; 127 | 128 | optional() 129 | {} 130 | 131 | optional(const optional& o) 132 | { 133 | if (o.is_valid()) 134 | { 135 | construct(o.get()); 136 | } 137 | } 138 | 139 | optional(optional&& o) 140 | { 141 | if (o.is_valid()) 142 | { 143 | construct(std::move(o.get())); 144 | } 145 | } 146 | 147 | optional(const value_type& v) 148 | { 149 | construct(v); 150 | } 151 | 152 | optional(value_type&& v) 153 | { 154 | construct(std::move(v)); 155 | } 156 | 157 | // constructs with val if condition 158 | optional(bool condition, const value_type& val) 159 | { 160 | if (condition) 161 | { 162 | construct(val); 163 | } 164 | } 165 | 166 | ~optional() 167 | { 168 | destroy(); 169 | } 170 | 171 | optional& operator=(const optional& o) 172 | { 173 | if (is_valid()) 174 | { 175 | if (o.is_valid()) 176 | { 177 | get() = o.get(); 178 | } 179 | else 180 | { 181 | destroy(); 182 | } 183 | } 184 | else 185 | { 186 | if (o.is_valid()) 187 | { 188 | construct(o.get()); 189 | } 190 | } 191 | 192 | return *this; 193 | } 194 | 195 | optional& operator=(optional&& o) 196 | { 197 | if (is_valid()) 198 | { 199 | if (o.is_valid()) 200 | { 201 | get() = std::move(o.get()); 202 | } 203 | else 204 | { 205 | destroy(); 206 | } 207 | } 208 | else 209 | { 210 | if (o.is_valid()) 211 | { 212 | construct(std::move(o.get())); 213 | } 214 | } 215 | 216 | return *this; 217 | } 218 | 219 | optional& operator=(const value_type& t) 220 | { 221 | if (is_valid()) 222 | { 223 | get() = t; 224 | } 225 | else 226 | { 227 | construct(t); 228 | } 229 | 230 | return *this; 231 | } 232 | 233 | optional& operator=(value_type&& t) 234 | { 235 | if (is_valid()) 236 | { 237 | get() = std::move(t); 238 | } 239 | else 240 | { 241 | construct(std::move(t)); 242 | } 243 | 244 | return *this; 245 | } 246 | 247 | template 248 | void construct(Args&&... args) 249 | { 250 | destroy(); 251 | void* p = &m_data; 252 | ::new (p) value_type(std::forward(args)...); 253 | m_is_valid = true; 254 | } 255 | 256 | void destroy() 257 | { 258 | if (is_valid()) 259 | { 260 | get().~T(); 261 | m_is_valid = false; 262 | } 263 | } 264 | 265 | void clear() 266 | { 267 | destroy(); 268 | } 269 | 270 | T& get() 271 | { 272 | return reinterpret_cast(m_data); 273 | } 274 | 275 | const T& get() const 276 | { 277 | return reinterpret_cast(m_data); 278 | } 279 | 280 | T& operator*() 281 | { 282 | return get(); 283 | } 284 | 285 | const T& operator*() const 286 | { 287 | return get(); 288 | } 289 | 290 | T* operator->() 291 | { 292 | return &get(); 293 | } 294 | 295 | const T* operator->() const 296 | { 297 | return &get(); 298 | } 299 | 300 | T& get_or(T& val) 301 | { 302 | if (is_valid()) 303 | { 304 | return get(); 305 | } 306 | else 307 | { 308 | return val; 309 | } 310 | } 311 | 312 | const T& get_or(const T& val) const 313 | { 314 | if (is_valid()) 315 | { 316 | return get(); 317 | } 318 | else 319 | { 320 | return val; 321 | } 322 | } 323 | 324 | bool is_valid() const 325 | { 326 | return m_is_valid; 327 | } 328 | 329 | explicit operator bool() const 330 | { 331 | return is_valid(); 332 | } 333 | 334 | private: 335 | typename std::aligned_storage::value>::type m_data; 336 | bool m_is_valid = false; 337 | }; 338 | 339 | template 340 | bool operator==(const optional& a, const optional& b) 341 | { 342 | if (a.is_valid()) 343 | { 344 | if (b.is_valid()) 345 | { 346 | return a.get() == b.get(); 347 | } 348 | 349 | return false; 350 | } 351 | else 352 | { 353 | return !b.is_valid(); 354 | } 355 | } 356 | 357 | template 358 | bool operator!=(const optional& a, const optional& b) 359 | { 360 | if (a.is_valid()) 361 | { 362 | if (b.is_valid()) 363 | { 364 | return a.get() != b.get(); 365 | } 366 | 367 | return true; 368 | } 369 | else 370 | { 371 | return b.is_valid(); 372 | } 373 | } 374 | 375 | template 376 | bool operator==(const optional& a, const T& b) 377 | { 378 | return a.is_valid() && a.get() == b; 379 | } 380 | 381 | template 382 | bool operator!=(const optional& a, const T& b) 383 | { 384 | return !a.is_valid() || a.get() != b; 385 | } 386 | 387 | 388 | } 389 | 390 | #if defined(CHOBO_OPTIONAL_TEST_WITH_DOCTEST) 391 | 392 | #include 393 | 394 | TEST_CASE("[optional] test") 395 | { 396 | using namespace chobo; 397 | using namespace std; 398 | 399 | optional iopt; 400 | optional iinvalid; 401 | CHECK(!iopt); 402 | CHECK(iopt != 5); 403 | CHECK(iopt == iinvalid); 404 | iopt = 0; 405 | CHECK(iopt); 406 | CHECK(!*iopt); 407 | 408 | auto iopt2 = iopt; 409 | CHECK(iopt2); 410 | CHECK(iopt2 == iopt); 411 | 412 | iopt = 5; 413 | CHECK(iopt != iopt2); 414 | CHECK(iopt == 5); 415 | CHECK(iopt != 0); 416 | 417 | iopt2 = iopt; 418 | CHECK(iopt2 == 5); 419 | CHECK(iopt2.get_or(15) == 5); 420 | 421 | iopt2.clear(); 422 | CHECK(!iopt2); 423 | CHECK(iopt2 != iopt); 424 | 425 | CHECK(iopt2.get_or(15) == 15); 426 | 427 | vector foo = { 1, 2, 3 }; 428 | optional> vopt = foo; 429 | CHECK(vopt); 430 | CHECK(vopt == foo); 431 | 432 | auto vopt2 = std::move(vopt); 433 | CHECK(vopt); 434 | CHECK(vopt2); 435 | CHECK(vopt2->size() == 3); 436 | CHECK(vopt->empty()); 437 | 438 | vopt = std::move(vopt2); 439 | CHECK(vopt); 440 | CHECK(vopt2); 441 | CHECK(vopt->size() == 3); 442 | CHECK(vopt2->empty()); 443 | 444 | 445 | } 446 | 447 | #endif 448 | -------------------------------------------------------------------------------- /include/chobo/small_vector.hpp: -------------------------------------------------------------------------------- 1 | // chobo-small-vector v1.03 2 | // 3 | // std::vector-like class with a static buffer for initial capacity 4 | // 5 | // MIT License: 6 | // Copyright(c) 2016-2018 Chobolabs Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files(the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and / or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions : 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | // 28 | // VERSION HISTORY 29 | // 30 | // 1.03 (2018-11-29) Removed references to deprecated std::allocator members 31 | // 1.02 (2018-04-24) Class inehrits from its allocator to make use of the 32 | // empty base class optimization. 33 | // emplace_back returns a reference to the inserted element 34 | // as per the c++17 standard. 35 | // 1.01 (2017-04-02) Fixed compilation error on (count, value) constructor and 36 | // assign, and insert methods when count or value is 0 37 | // 1.00 (2016-11-08) First public release 38 | // 39 | // 40 | // DOCUMENTATION 41 | // 42 | // Simply include this file wherever you need. 43 | // It defines the class chobo::small_vector, which is a drop-in replacement of 44 | // std::vector, but with an initial capacity as a template argument. 45 | // It gives you the benefits of using std::vector, at the cost of having a statically 46 | // allocated buffer for the initial capacity, which gives you cache-local data 47 | // when the vector is small (smaller than the initial capacity). 48 | // 49 | // When the size exceeds the capacity, the vector allocates memory via the provided 50 | // allocator, falling back to classic std::vector behavior. 51 | // 52 | // The second size_t template argument, RevertToStaticSize, is used when a 53 | // small_vector which has already switched to dynamically allocated size reduces 54 | // its size to a number smaller than that. In this case the vector's buffer 55 | // switches back to the staticallly allocated one 56 | // 57 | // A default value for the initial static capacity is provided so a replacement 58 | // in an existing code is possible with minimal changes to it. 59 | // 60 | // Example: 61 | // 62 | // chobo::small_vector myvec; // a small_vector of size 0, initial capacity 4, and revert size 4 (smaller than 5) 63 | // myvec.resize(2); // vector is {0,0} in static buffer 64 | // myvec[1] = 11; // vector is {0,11} in static buffer 65 | // myvec.push_back(7); // vector is {0,11,7} in static buffer 66 | // myvec.insert(myvec.begin() + 1, 3); // vector is {0,3,11,7} in static buffer 67 | // myvec.push_back(5); // vector is {0,3,11,7,5} in dynamically allocated memory buffer 68 | // myvec.erase(myvec.begin()); // vector is {3,11,7,5} back in static buffer 69 | // myvec.resize(5); // vector is {3,11,7,5,0} back in dynamically allocated memory 70 | // 71 | // 72 | // Reference: 73 | // 74 | // chobo::small_vector is fully compatible with std::vector with 75 | // the following exceptions: 76 | // * when reducing the size with erase or resize the new size may fall below 77 | // RevertToStaticSize (if it is not 0). In such a case the vector will 78 | // revert to using its static buffer, invalidating all iterators (contrary 79 | // to the standard) 80 | // * a method is added `revert_to_static()` which reverts to the static buffer 81 | // if possible, but doesn't free the dynamically allocated one 82 | // 83 | // Other notes: 84 | // 85 | // * the default value for RevertToStaticSize is zero. This means that once a dynamic 86 | // buffer is allocated the data will never be put into the static one, even if the 87 | // size allows it. Even if clear() is called. The only way to do so is to call 88 | // shrink_to_fit() or revert_to_static() 89 | // * shrink_to_fit will free and reallocate if size != capacity and the data 90 | // doesn't fit into the static buffer. It also will revert to the static buffer 91 | // whenever possible regardless of the RevertToStaticSize value 92 | // 93 | // 94 | // Configuration 95 | // 96 | // The library has two configuration options. They can be set as #define-s 97 | // before including the header file, but it is recommended to change the code 98 | // of the library itself with the values you want, especially if you include 99 | // the library in many compilation units (as opposed to, say, a precompiled 100 | // header or a central header). 101 | // 102 | // Config out of range error handling 103 | // 104 | // An out of range error is a runtime error which is triggered when a method is 105 | // called with an iterator that doesn't belong to the vector's current range. 106 | // For example: vec.erase(vec.end() + 1); 107 | // 108 | // This is set by defining CHOBO_SMALL_VECTOR_ERROR_HANDLING to one of the 109 | // following values: 110 | // * CHOBO_SMALL_VECTOR_ERROR_HANDLING_NONE - no error handling. Crashes WILL 111 | // ensue if the error is triggered. 112 | // * CHOBO_SMALL_VECTOR_ERROR_HANDLING_THROW - std::out_of_range is thrown. 113 | // * CHOBO_SMALL_VECTOR_ERROR_HANDLING_ASSERT - asserions are triggered. 114 | // * CHOBO_SMALL_VECTOR_ERROR_HANDLING_ASSERT_AND_THROW - combines assert and 115 | // throw to catch errors more easily in debug mode 116 | // 117 | // To set this setting by editing the file change the line: 118 | // ``` 119 | // # define CHOBO_SMALL_VECTOR_ERROR_HANDLING CHOBO_SMALL_VECTOR_ERROR_HANDLING_THROW 120 | // ``` 121 | // to the default setting of your choice 122 | // 123 | // Config bounds checks: 124 | // 125 | // By default bounds checks are made in debug mode (via an asser) when accessing 126 | // elements (with `at` or `[]`). Iterators are not checked (yet...) 127 | // 128 | // To disable them, you can define CHOBO_SMALL_VECTOR_NO_DEBUG_BOUNDS_CHECK 129 | // before including the header. 130 | // 131 | // 132 | // TESTS 133 | // 134 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 135 | // To run them, define CHOBO_SMALL_VECTOR_TEST_WITH_DOCTEST before including 136 | // the header in a file which has doctest.h already included. 137 | // 138 | #pragma once 139 | 140 | #include 141 | #include 142 | #include 143 | 144 | #define CHOBO_SMALL_VECTOR_ERROR_HANDLING_NONE 0 145 | #define CHOBO_SMALL_VECTOR_ERROR_HANDLING_THROW 1 146 | #define CHOBO_SMALL_VECTOR_ERROR_HANDLING_ASSERT 2 147 | #define CHOBO_SMALL_VECTOR_ERROR_HANDLING_ASSERT_AND_THROW 3 148 | 149 | #if !defined(CHOBO_SMALL_VECTOR_ERROR_HANDLING) 150 | # define CHOBO_SMALL_VECTOR_ERROR_HANDLING CHOBO_SMALL_VECTOR_ERROR_HANDLING_THROW 151 | #endif 152 | 153 | 154 | #if CHOBO_SMALL_VECTOR_ERROR_HANDLING == CHOBO_SMALL_VECTOR_ERROR_HANDLING_NONE 155 | # define _CHOBO_SMALL_VECTOR_OUT_OF_RANGE_IF(cond) 156 | #elif CHOBO_SMALL_VECTOR_ERROR_HANDLING == CHOBO_SMALL_VECTOR_ERROR_HANDLING_THROW 157 | # include 158 | # define _CHOBO_SMALL_VECTOR_OUT_OF_RANGE_IF(cond) if (cond) throw std::out_of_range("chobo::small_vector out of range") 159 | #elif CHOBO_SMALL_VECTOR_ERROR_HANDLING == CHOBO_SMALL_VECTOR_ERROR_HANDLING_ASSERT 160 | # include 161 | # define _CHOBO_SMALL_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) assert(!(cond) && "chobo::small_vector out of range") 162 | #elif CHOBO_SMALL_VECTOR_ERROR_HANDLING == CHOBO_SMALL_VECTOR_ERROR_HANDLING_ASSERT_AND_THROW 163 | # include 164 | # include 165 | # define _CHOBO_SMALL_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) \ 166 | do { if (cond) { assert(false && "chobo::small_vector out of range"); throw std::out_of_range("chobo::small_vector out of range"); } } while(false) 167 | #else 168 | #error "Unknown CHOBO_SMALL_VECTOR_ERRROR_HANDLING" 169 | #endif 170 | 171 | 172 | #if defined(CHOBO_SMALL_VECTOR_NO_DEBUG_BOUNDS_CHECK) 173 | # define _CHOBO_SMALL_VECTOR_BOUNDS_CHECK(i) 174 | #else 175 | # include 176 | # define _CHOBO_SMALL_VECTOR_BOUNDS_CHECK(i) assert((i) < this->size()) 177 | #endif 178 | 179 | namespace chobo 180 | { 181 | 182 | template> 183 | struct small_vector: Alloc 184 | { 185 | static_assert(RevertToStaticSize <= StaticCapacity + 1, "chobo::small_vector: the revert-to-static size shouldn't exceed the static capacity by more than one"); 186 | 187 | using atraits = std::allocator_traits; 188 | public: 189 | using allocator_type = Alloc; 190 | using value_type = typename atraits::value_type; 191 | using size_type = typename atraits::size_type; 192 | using difference_type = typename atraits::difference_type; 193 | using reference = T&; 194 | using const_reference = const T&; 195 | using pointer = typename atraits::pointer; 196 | using const_pointer = typename atraits::const_pointer; 197 | using iterator = pointer; 198 | using const_iterator = const_pointer; 199 | using reverse_iterator = std::reverse_iterator; 200 | using const_reverse_iterator = std::reverse_iterator; 201 | 202 | static constexpr size_t static_capacity = StaticCapacity; 203 | static constexpr intptr_t revert_to_static_size = RevertToStaticSize; 204 | 205 | small_vector() 206 | : small_vector(Alloc()) 207 | {} 208 | 209 | small_vector(const Alloc& alloc) 210 | : Alloc(alloc) 211 | , m_capacity(StaticCapacity) 212 | , m_dynamic_capacity(0) 213 | , m_dynamic_data(nullptr) 214 | { 215 | m_begin = m_end = static_begin_ptr(); 216 | } 217 | 218 | explicit small_vector(size_t count, const Alloc& alloc = Alloc()) 219 | : small_vector(alloc) 220 | { 221 | resize(count); 222 | } 223 | 224 | explicit small_vector(size_t count, const T& value, const Alloc& alloc = Alloc()) 225 | : small_vector(alloc) 226 | { 227 | assign_impl(count, value); 228 | } 229 | 230 | template ())> 231 | small_vector(InputIterator first, InputIterator last, const Alloc& alloc = Alloc()) 232 | : small_vector(alloc) 233 | { 234 | assign_impl(first, last); 235 | } 236 | 237 | small_vector(std::initializer_list l, const Alloc& alloc = Alloc()) 238 | : small_vector(alloc) 239 | { 240 | assign_impl(l); 241 | } 242 | 243 | small_vector(const small_vector& v) 244 | : small_vector(v, atraits::select_on_container_copy_construction(v.get_allocator())) 245 | {} 246 | 247 | small_vector(const small_vector& v, const Alloc& alloc) 248 | : Alloc(alloc) 249 | , m_dynamic_capacity(0) 250 | , m_dynamic_data(nullptr) 251 | { 252 | if (v.size() > StaticCapacity) 253 | { 254 | m_dynamic_capacity = v.size(); 255 | m_begin = m_end = m_dynamic_data = atraits::allocate(get_alloc(), m_dynamic_capacity); 256 | m_capacity = v.size(); 257 | } 258 | else 259 | { 260 | m_begin = m_end = static_begin_ptr(); 261 | m_capacity = StaticCapacity; 262 | } 263 | 264 | for (auto p = v.m_begin; p != v.m_end; ++p) 265 | { 266 | atraits::construct(get_alloc(), m_end, *p); 267 | ++m_end; 268 | } 269 | } 270 | 271 | small_vector(small_vector&& v) 272 | : Alloc(std::move(v.get_alloc())) 273 | , m_capacity(v.m_capacity) 274 | , m_dynamic_capacity(v.m_dynamic_capacity) 275 | , m_dynamic_data(v.m_dynamic_data) 276 | { 277 | if (v.m_begin == v.static_begin_ptr()) 278 | { 279 | m_begin = m_end = static_begin_ptr(); 280 | for (auto p = v.m_begin; p != v.m_end; ++p) 281 | { 282 | atraits::construct(get_alloc(), m_end, std::move(*p)); 283 | ++m_end; 284 | } 285 | 286 | v.clear(); 287 | } 288 | else 289 | { 290 | m_begin = v.m_begin; 291 | m_end = v.m_end; 292 | } 293 | 294 | v.m_dynamic_capacity = 0; 295 | v.m_dynamic_data = nullptr; 296 | v.m_begin = v.m_end = v.static_begin_ptr(); 297 | v.m_capacity = StaticCapacity; 298 | } 299 | 300 | ~small_vector() 301 | { 302 | clear(); 303 | 304 | if (m_dynamic_data) 305 | { 306 | atraits::deallocate(get_alloc(), m_dynamic_data, m_dynamic_capacity); 307 | } 308 | } 309 | 310 | small_vector& operator=(const small_vector& v) 311 | { 312 | if (this == &v) 313 | { 314 | // prevent self usurp 315 | return *this; 316 | } 317 | 318 | clear(); 319 | 320 | m_begin = m_end = choose_data(v.size()); 321 | 322 | for (auto p = v.m_begin; p != v.m_end; ++p) 323 | { 324 | atraits::construct(get_alloc(), m_end, *p); 325 | ++m_end; 326 | } 327 | 328 | update_capacity(); 329 | 330 | return *this; 331 | } 332 | 333 | small_vector& operator=(small_vector&& v) 334 | { 335 | clear(); 336 | 337 | get_alloc() = std::move(v.get_alloc()); 338 | m_capacity = v.m_capacity; 339 | m_dynamic_capacity = v.m_dynamic_capacity; 340 | m_dynamic_data = v.m_dynamic_data; 341 | 342 | if (v.m_begin == v.static_begin_ptr()) 343 | { 344 | m_begin = m_end = static_begin_ptr(); 345 | for (auto p = v.m_begin; p != v.m_end; ++p) 346 | { 347 | atraits::construct(get_alloc(), m_end, std::move(*p)); 348 | ++m_end; 349 | } 350 | 351 | v.clear(); 352 | } 353 | else 354 | { 355 | m_begin = v.m_begin; 356 | m_end = v.m_end; 357 | } 358 | 359 | v.m_dynamic_capacity = 0; 360 | v.m_dynamic_data = nullptr; 361 | v.m_begin = v.m_end = v.static_begin_ptr(); 362 | v.m_capacity = StaticCapacity; 363 | 364 | return *this; 365 | } 366 | 367 | void assign(size_type count, const T& value) 368 | { 369 | clear(); 370 | assign_impl(count, value); 371 | } 372 | 373 | template ())> 374 | void assign(InputIterator first, InputIterator last) 375 | { 376 | clear(); 377 | assign_impl(first, last); 378 | } 379 | 380 | void assign(std::initializer_list ilist) 381 | { 382 | clear(); 383 | assign_impl(ilist); 384 | } 385 | 386 | allocator_type get_allocator() const 387 | { 388 | return get_alloc(); 389 | } 390 | 391 | const_reference at(size_type i) const 392 | { 393 | _CHOBO_SMALL_VECTOR_BOUNDS_CHECK(i); 394 | return *(m_begin + i); 395 | } 396 | 397 | reference at(size_type i) 398 | { 399 | _CHOBO_SMALL_VECTOR_BOUNDS_CHECK(i); 400 | return *(m_begin + i); 401 | } 402 | 403 | const_reference operator[](size_type i) const 404 | { 405 | return at(i); 406 | } 407 | 408 | reference operator[](size_type i) 409 | { 410 | return at(i); 411 | } 412 | 413 | const_reference front() const 414 | { 415 | return at(0); 416 | } 417 | 418 | reference front() 419 | { 420 | return at(0); 421 | } 422 | 423 | const_reference back() const 424 | { 425 | return *(m_end - 1); 426 | } 427 | 428 | reference back() 429 | { 430 | return *(m_end - 1); 431 | } 432 | 433 | const_pointer data() const noexcept 434 | { 435 | return m_begin; 436 | } 437 | 438 | pointer data() noexcept 439 | { 440 | return m_begin; 441 | } 442 | 443 | // iterators 444 | iterator begin() noexcept 445 | { 446 | return m_begin; 447 | } 448 | 449 | const_iterator begin() const noexcept 450 | { 451 | return m_begin; 452 | } 453 | 454 | const_iterator cbegin() const noexcept 455 | { 456 | return m_begin; 457 | } 458 | 459 | iterator end() noexcept 460 | { 461 | return m_end; 462 | } 463 | 464 | const_iterator end() const noexcept 465 | { 466 | return m_end; 467 | } 468 | 469 | const_iterator cend() const noexcept 470 | { 471 | return m_end; 472 | } 473 | 474 | reverse_iterator rbegin() noexcept 475 | { 476 | return reverse_iterator(end()); 477 | } 478 | 479 | const_reverse_iterator rbegin() const noexcept 480 | { 481 | return const_reverse_iterator(end()); 482 | } 483 | 484 | const_reverse_iterator crbegin() const noexcept 485 | { 486 | return const_reverse_iterator(end()); 487 | } 488 | 489 | reverse_iterator rend() noexcept 490 | { 491 | return reverse_iterator(begin()); 492 | } 493 | 494 | const_reverse_iterator rend() const noexcept 495 | { 496 | return const_reverse_iterator(begin()); 497 | } 498 | 499 | const_reverse_iterator crend() const noexcept 500 | { 501 | return const_reverse_iterator(begin()); 502 | } 503 | 504 | // capacity 505 | bool empty() const noexcept 506 | { 507 | return m_begin == m_end; 508 | } 509 | 510 | size_t size() const noexcept 511 | { 512 | return m_end - m_begin; 513 | } 514 | 515 | size_t max_size() const noexcept 516 | { 517 | return atraits::max_size(); 518 | } 519 | 520 | void reserve(size_type new_cap) 521 | { 522 | if (new_cap <= m_capacity) return; 523 | 524 | auto new_buf = choose_data(new_cap); 525 | 526 | assert(new_buf != m_begin); // should've been handled by new_cap <= m_capacity 527 | assert(new_buf != static_begin_ptr()); // we should never reserve into static memory 528 | 529 | const auto s = size(); 530 | if(s < RevertToStaticSize) 531 | { 532 | // we've allocated enough memory for the dynamic buffer but don't move there until we have to 533 | return; 534 | } 535 | 536 | // now we need to transfer the existing elements into the new buffer 537 | for (size_type i = 0; i < s; ++i) 538 | { 539 | atraits::construct(get_alloc(), new_buf + i, std::move(*(m_begin + i))); 540 | } 541 | 542 | // free old elements 543 | for (size_type i = 0; i < s; ++i) 544 | { 545 | atraits::destroy(get_alloc(), m_begin + i); 546 | } 547 | 548 | if (m_begin != static_begin_ptr()) 549 | { 550 | // we've moved from dyn to dyn memory, so deallocate the old one 551 | atraits::deallocate(get_alloc(), m_begin, m_capacity); 552 | } 553 | 554 | m_begin = new_buf; 555 | m_end = new_buf + s; 556 | m_capacity = m_dynamic_capacity; 557 | } 558 | 559 | size_t capacity() const noexcept 560 | { 561 | return m_capacity; 562 | } 563 | 564 | void shrink_to_fit() 565 | { 566 | const auto s = size(); 567 | 568 | if (s == m_capacity) return; 569 | if (m_begin == static_begin_ptr()) return; 570 | 571 | auto old_end = m_end; 572 | 573 | if (s < StaticCapacity) 574 | { 575 | // revert to static capacity 576 | m_begin = m_end = static_begin_ptr(); 577 | m_capacity = StaticCapacity; 578 | } 579 | else 580 | { 581 | // alloc new smaller buffer 582 | m_begin = m_end = atraits::allocate(get_alloc(), s); 583 | m_capacity = s; 584 | } 585 | 586 | for (auto p = m_dynamic_data; p != old_end; ++p) 587 | { 588 | atraits::construct(get_alloc(), m_end, std::move(*p)); 589 | ++m_end; 590 | atraits::destroy(get_alloc(), p); 591 | } 592 | 593 | atraits::deallocate(get_alloc(), m_dynamic_data, m_dynamic_capacity); 594 | m_dynamic_data = nullptr; 595 | m_dynamic_capacity = 0; 596 | } 597 | 598 | void revert_to_static() 599 | { 600 | const auto s = size(); 601 | if (m_begin == static_begin_ptr()) return; //we're already there 602 | if (s > StaticCapacity) return; // nothing we can do 603 | 604 | // revert to static capacity 605 | auto old_end = m_end; 606 | m_begin = m_end = static_begin_ptr(); 607 | m_capacity = StaticCapacity; 608 | for (auto p = m_dynamic_data; p != old_end; ++p) 609 | { 610 | atraits::construct(get_alloc(), m_end, std::move(*p)); 611 | ++m_end; 612 | atraits::destroy(get_alloc(), p); 613 | } 614 | } 615 | 616 | // modifiers 617 | void clear() noexcept 618 | { 619 | for (auto p = m_begin; p != m_end; ++p) 620 | { 621 | atraits::destroy(get_alloc(), p); 622 | } 623 | 624 | if (RevertToStaticSize > 0) 625 | { 626 | m_begin = m_end = static_begin_ptr(); 627 | m_capacity = StaticCapacity; 628 | } 629 | else 630 | { 631 | m_end = m_begin; 632 | } 633 | } 634 | 635 | iterator insert(const_iterator position, const value_type& val) 636 | { 637 | auto pos = grow_at(position, 1); 638 | atraits::construct(get_alloc(), pos, val); 639 | return pos; 640 | } 641 | 642 | iterator insert(const_iterator position, value_type&& val) 643 | { 644 | auto pos = grow_at(position, 1); 645 | atraits::construct(get_alloc(), pos, std::move(val)); 646 | return pos; 647 | } 648 | 649 | iterator insert(const_iterator position, size_type count, const value_type& val) 650 | { 651 | auto pos = grow_at(position, count); 652 | for (size_type i = 0; i < count; ++i) 653 | { 654 | atraits::construct(get_alloc(), pos + i, val); 655 | } 656 | return pos; 657 | } 658 | 659 | template ())> 660 | iterator insert(const_iterator position, InputIterator first, InputIterator last) 661 | { 662 | auto pos = grow_at(position, last - first); 663 | size_type i = 0; 664 | auto np = pos; 665 | for (auto p = first; p != last; ++p, ++np) 666 | { 667 | atraits::construct(get_alloc(), np, *p); 668 | } 669 | return pos; 670 | } 671 | 672 | iterator insert(const_iterator position, std::initializer_list ilist) 673 | { 674 | auto pos = grow_at(position, ilist.size()); 675 | size_type i = 0; 676 | for (auto& elem : ilist) 677 | { 678 | atraits::construct(get_alloc(), pos + i, elem); 679 | ++i; 680 | } 681 | return pos; 682 | } 683 | 684 | template 685 | iterator emplace(const_iterator position, Args&&... args) 686 | { 687 | auto pos = grow_at(position, 1); 688 | atraits::construct(get_alloc(), pos, std::forward(args)...); 689 | return pos; 690 | } 691 | 692 | iterator erase(const_iterator position) 693 | { 694 | return shrink_at(position, 1); 695 | } 696 | 697 | iterator erase(const_iterator first, const_iterator last) 698 | { 699 | _CHOBO_SMALL_VECTOR_OUT_OF_RANGE_IF(first > last); 700 | return shrink_at(first, last - first); 701 | } 702 | 703 | void push_back(const_reference val) 704 | { 705 | auto pos = grow_at(m_end, 1); 706 | atraits::construct(get_alloc(), pos, val); 707 | } 708 | 709 | void push_back(T&& val) 710 | { 711 | auto pos = grow_at(m_end, 1); 712 | atraits::construct(get_alloc(), pos, std::move(val)); 713 | } 714 | 715 | template 716 | reference emplace_back(Args&&... args) 717 | { 718 | auto pos = grow_at(m_end, 1); 719 | atraits::construct(get_alloc(), pos, std::forward(args)...); 720 | return *pos; 721 | } 722 | 723 | void pop_back() 724 | { 725 | shrink_at(m_end - 1, 1); 726 | } 727 | 728 | void resize(size_type n, const value_type& v) 729 | { 730 | auto new_buf = choose_data(n); 731 | 732 | if (new_buf == m_begin) 733 | { 734 | // no special transfers needed 735 | 736 | auto new_end = m_begin + n; 737 | 738 | while (m_end > new_end) 739 | { 740 | atraits::destroy(get_alloc(), --m_end); 741 | } 742 | 743 | while (new_end > m_end) 744 | { 745 | atraits::construct(get_alloc(), m_end++, v); 746 | } 747 | } 748 | else 749 | { 750 | // we need to transfer the elements into the new buffer 751 | 752 | const auto s = size(); 753 | const auto num_transfer = n < s ? n : s; 754 | 755 | for (size_type i = 0; i < num_transfer; ++i) 756 | { 757 | atraits::construct(get_alloc(), new_buf + i, std::move(*(m_begin + i))); 758 | } 759 | 760 | // free obsoletes 761 | for (size_type i = 0; i < s; ++i) 762 | { 763 | atraits::destroy(get_alloc(), m_begin + i); 764 | } 765 | 766 | // construct new elements 767 | for (size_type i = num_transfer; i < n; ++i) 768 | { 769 | atraits::construct(get_alloc(), new_buf + i, v); 770 | } 771 | 772 | if (m_begin != static_begin_ptr()) 773 | { 774 | // we've moved from dyn to dyn memory, so deallocate the old one 775 | atraits::deallocate(get_alloc(), m_begin, m_capacity); 776 | } 777 | 778 | if (new_buf == static_begin_ptr()) 779 | { 780 | m_capacity = StaticCapacity; 781 | } 782 | else 783 | { 784 | m_capacity = m_dynamic_capacity; 785 | } 786 | 787 | m_begin = new_buf; 788 | m_end = new_buf + n; 789 | } 790 | } 791 | 792 | void resize(size_type n) 793 | { 794 | auto new_buf = choose_data(n); 795 | 796 | if (new_buf == m_begin) 797 | { 798 | // no special transfers needed 799 | 800 | auto new_end = m_begin + n; 801 | 802 | while (m_end > new_end) 803 | { 804 | atraits::destroy(get_alloc(), --m_end); 805 | } 806 | 807 | while (new_end > m_end) 808 | { 809 | atraits::construct(get_alloc(), m_end++); 810 | } 811 | } 812 | else 813 | { 814 | // we need to transfer the elements into the new buffer 815 | 816 | const auto s = size(); 817 | const auto num_transfer = n < s ? n : s; 818 | 819 | for (size_type i = 0; i < num_transfer; ++i) 820 | { 821 | atraits::construct(get_alloc(), new_buf + i, std::move(*(m_begin + i))); 822 | } 823 | 824 | // free obsoletes 825 | for (size_type i = 0; i < n; ++i) 826 | { 827 | atraits::destroy(get_alloc(), m_begin + i); 828 | } 829 | 830 | // construct new elements 831 | for (size_type i = num_transfer; i < s; ++i) 832 | { 833 | atraits::construct(get_alloc(), new_buf + i); 834 | } 835 | 836 | if (m_begin != static_begin_ptr()) 837 | { 838 | // we've moved from dyn to dyn memory, so deallocate the old one 839 | atraits::deallocate(get_alloc(), m_begin, m_capacity); 840 | } 841 | 842 | if (new_buf == static_begin_ptr()) 843 | { 844 | m_capacity = StaticCapacity; 845 | } 846 | else 847 | { 848 | m_capacity = m_dynamic_capacity; 849 | } 850 | 851 | m_begin = new_buf; 852 | m_end = new_buf + n; 853 | } 854 | } 855 | 856 | private: 857 | T* static_begin_ptr() 858 | { 859 | return reinterpret_cast(m_static_data + 0); 860 | } 861 | 862 | // increase the size by splicing the elements in such a way that 863 | // a hole of uninitialized elements is left at position, with size num 864 | // returns the (potentially new) address of the hole 865 | T* grow_at(const T* cp, size_t num) 866 | { 867 | auto position = const_cast(cp); 868 | 869 | _CHOBO_SMALL_VECTOR_OUT_OF_RANGE_IF(position < m_begin || position > m_end); 870 | 871 | const auto s = size(); 872 | auto new_buf = choose_data(s + num); 873 | 874 | if (new_buf == m_begin) 875 | { 876 | // no special transfers needed 877 | 878 | m_end = m_begin + s + num; 879 | 880 | for (auto p = m_end - num - 1; p >= position; --p) 881 | { 882 | atraits::construct(get_alloc(), p + num, std::move(*p)); 883 | atraits::destroy(get_alloc(), p); 884 | } 885 | 886 | return position; 887 | } 888 | else 889 | { 890 | // we need to transfer the elements into the new buffer 891 | 892 | position = new_buf + (position - m_begin); 893 | 894 | auto p = m_begin; 895 | auto np = new_buf; 896 | 897 | for (; np != position; ++p, ++np) 898 | { 899 | atraits::construct(get_alloc(), np, std::move(*p)); 900 | } 901 | 902 | np += num; 903 | for (; p != m_end; ++p, ++np) 904 | { 905 | atraits::construct(get_alloc(), np, std::move(*p)); 906 | } 907 | 908 | // destroy old 909 | for (p = m_begin; p != m_end; ++p) 910 | { 911 | atraits::destroy(get_alloc(), p); 912 | } 913 | 914 | if (m_begin != static_begin_ptr()) 915 | { 916 | // we've moved from dyn to dyn memory, so deallocate the old one 917 | atraits::deallocate(get_alloc(), m_begin, m_capacity); 918 | } 919 | 920 | m_capacity = m_dynamic_capacity; 921 | 922 | m_begin = new_buf; 923 | m_end = new_buf + s + num; 924 | 925 | return position; 926 | } 927 | } 928 | 929 | T* shrink_at(const T* cp, size_t num) 930 | { 931 | auto position = const_cast(cp); 932 | 933 | _CHOBO_SMALL_VECTOR_OUT_OF_RANGE_IF(position < m_begin || position > m_end || position + num > m_end); 934 | 935 | const auto s = size(); 936 | if (s - num == 0) 937 | { 938 | clear(); 939 | return m_end; 940 | } 941 | 942 | auto new_buf = choose_data(s - num); 943 | 944 | if (new_buf == m_begin) 945 | { 946 | // no special transfers needed 947 | 948 | for (auto p = position, np = position + num; np != m_end; ++p, ++np) 949 | { 950 | atraits::destroy(get_alloc(), p); 951 | atraits::construct(get_alloc(), p, std::move(*np)); 952 | } 953 | 954 | for (auto p = m_end - num; p != m_end; ++p) 955 | { 956 | atraits::destroy(get_alloc(), p); 957 | } 958 | 959 | m_end -= num; 960 | } 961 | else 962 | { 963 | // we need to transfer the elements into the new buffer 964 | 965 | assert(new_buf == static_begin_ptr()); // since we're shrinking that's the only way to have a new buffer 966 | 967 | m_capacity = StaticCapacity; 968 | 969 | auto p = m_begin, np = new_buf; 970 | for (; p != position; ++p, ++np) 971 | { 972 | atraits::construct(get_alloc(), np, std::move(*p)); 973 | atraits::destroy(get_alloc(), p); 974 | } 975 | 976 | for (; p != position + num; ++p) 977 | { 978 | atraits::destroy(get_alloc(), p); 979 | } 980 | 981 | for (; np != new_buf + s - num; ++p, ++np) 982 | { 983 | atraits::construct(get_alloc(), np, std::move(*p)); 984 | atraits::destroy(get_alloc(), p); 985 | } 986 | 987 | position = new_buf + (position - m_begin); 988 | m_begin = new_buf; 989 | m_end = np; 990 | } 991 | 992 | return ++position; 993 | } 994 | 995 | void assign_impl(size_type count, const T& value) 996 | { 997 | assert(m_begin); 998 | assert(m_begin == m_end); 999 | 1000 | m_begin = m_end = choose_data(count); 1001 | for (size_type i = 0; i < count; ++i) 1002 | { 1003 | atraits::construct(get_alloc(), m_end, value); 1004 | ++m_end; 1005 | } 1006 | 1007 | update_capacity(); 1008 | } 1009 | 1010 | template 1011 | void assign_impl(InputIterator first, InputIterator last) 1012 | { 1013 | assert(m_begin); 1014 | assert(m_begin == m_end); 1015 | 1016 | m_begin = m_end = choose_data(last - first); 1017 | for (auto p = first; p != last; ++p) 1018 | { 1019 | atraits::construct(get_alloc(), m_end, *p); 1020 | ++m_end; 1021 | } 1022 | 1023 | update_capacity(); 1024 | } 1025 | 1026 | void assign_impl(std::initializer_list ilist) 1027 | { 1028 | assert(m_begin); 1029 | assert(m_begin == m_end); 1030 | 1031 | m_begin = m_end = choose_data(ilist.size()); 1032 | for (auto& elem : ilist) 1033 | { 1034 | atraits::construct(get_alloc(), m_end, elem); 1035 | ++m_end; 1036 | } 1037 | 1038 | update_capacity(); 1039 | } 1040 | 1041 | void update_capacity() 1042 | { 1043 | if (m_begin == static_begin_ptr()) 1044 | { 1045 | m_capacity = StaticCapacity; 1046 | } 1047 | else 1048 | { 1049 | m_capacity = m_dynamic_capacity; 1050 | } 1051 | } 1052 | 1053 | T* choose_data(size_t desired_capacity) 1054 | { 1055 | if (m_begin == m_dynamic_data) 1056 | { 1057 | // we're at the dyn buffer, so see if it needs resize or revert to static 1058 | 1059 | if (desired_capacity > m_dynamic_capacity) 1060 | { 1061 | while (m_dynamic_capacity < desired_capacity) 1062 | { 1063 | // grow by roughly 1.5 1064 | m_dynamic_capacity *= 3; 1065 | ++m_dynamic_capacity; 1066 | m_dynamic_capacity /= 2; 1067 | } 1068 | 1069 | m_dynamic_data = atraits::allocate(get_alloc(), m_dynamic_capacity); 1070 | return m_dynamic_data; 1071 | } 1072 | else if (desired_capacity < RevertToStaticSize) 1073 | { 1074 | // we're reverting to the static buffer 1075 | return static_begin_ptr(); 1076 | } 1077 | else 1078 | { 1079 | // if the capacity and we don't revert to static, just do nothing 1080 | return m_dynamic_data; 1081 | } 1082 | } 1083 | else 1084 | { 1085 | assert(m_begin == static_begin_ptr()); // corrupt begin ptr? 1086 | 1087 | if (desired_capacity > StaticCapacity) 1088 | { 1089 | // we must move to dyn memory 1090 | 1091 | // see if we have enough 1092 | if (desired_capacity > m_dynamic_capacity) 1093 | { 1094 | // we need to allocate more 1095 | // we don't have anything to destroy, so we can also deallocate the buffer 1096 | if (m_dynamic_data) 1097 | { 1098 | atraits::deallocate(get_alloc(), m_dynamic_data, m_dynamic_capacity); 1099 | } 1100 | 1101 | m_dynamic_capacity = desired_capacity; 1102 | m_dynamic_data = atraits::allocate(get_alloc(), m_dynamic_capacity); 1103 | } 1104 | 1105 | return m_dynamic_data; 1106 | } 1107 | else 1108 | { 1109 | // we have enough capacity as it is 1110 | return static_begin_ptr(); 1111 | } 1112 | } 1113 | } 1114 | 1115 | allocator_type& get_alloc() { return static_cast(*this); } 1116 | const allocator_type& get_alloc() const { return static_cast(*this); } 1117 | 1118 | pointer m_begin; 1119 | pointer m_end; 1120 | 1121 | size_t m_capacity; 1122 | typename std::aligned_storage::value>::type m_static_data[StaticCapacity]; 1123 | 1124 | size_t m_dynamic_capacity; 1125 | pointer m_dynamic_data; 1126 | }; 1127 | 1128 | template 1129 | bool operator==(const small_vector& a, 1130 | const small_vector& b) 1131 | { 1132 | if (a.size() != b.size()) 1133 | { 1134 | return false; 1135 | } 1136 | 1137 | for (size_t i = 0; i < a.size(); ++i) 1138 | { 1139 | if (a[i] != b[i]) 1140 | return false; 1141 | } 1142 | 1143 | return true; 1144 | } 1145 | 1146 | template 1147 | bool operator!=(const small_vector& a, 1148 | const small_vector& b) 1149 | { 1150 | if (a.size() != b.size()) 1151 | { 1152 | return true; 1153 | } 1154 | 1155 | for (size_t i = 0; i < a.size(); ++i) 1156 | { 1157 | if (a[i] != b[i]) 1158 | return true; 1159 | } 1160 | 1161 | return false; 1162 | } 1163 | 1164 | } 1165 | 1166 | 1167 | #if defined(CHOBO_SMALL_VECTOR_TEST_WITH_DOCTEST) 1168 | 1169 | #include 1170 | #include 1171 | 1172 | #if !defined(CHOBO_TEST_COUNTING_ALLOCATOR) 1173 | #define CHOBO_TEST_COUNTING_ALLOCATOR 1 1174 | 1175 | size_t allocations = 0; 1176 | size_t deallocations = 0; 1177 | size_t allocated_bytes = 0; 1178 | size_t deallocated_bytes = 0; 1179 | size_t constructions = 0; 1180 | size_t destructions = 0; 1181 | 1182 | template 1183 | class counting_allocator : public std::allocator 1184 | { 1185 | }; 1186 | 1187 | namespace std 1188 | { 1189 | 1190 | template 1191 | class allocator_traits> /* hacky */ : public allocator_traits> 1192 | { 1193 | public: 1194 | typedef std::allocator_traits> super; 1195 | typedef counting_allocator Alloc; 1196 | 1197 | static T* allocate(Alloc& a, size_t n, typename std::allocator_traits::const_pointer hint = 0) 1198 | { 1199 | ++allocations; 1200 | allocated_bytes += n * sizeof(T); 1201 | return super::allocate(a, n, hint); 1202 | } 1203 | 1204 | static void deallocate(Alloc& a, T* p, size_t n) 1205 | { 1206 | ++deallocations; 1207 | deallocated_bytes += n * sizeof(T); 1208 | return super::deallocate(a, p, n); 1209 | } 1210 | 1211 | template< class U, class... Args > 1212 | static void construct(Alloc& a, U* p, Args&&... args) 1213 | { 1214 | ++constructions; 1215 | return super::construct(a, p, std::forward(args)...); 1216 | } 1217 | 1218 | template< class U > 1219 | static void destroy(Alloc& a, U* p) 1220 | { 1221 | ++destructions; 1222 | return super::destroy(a, p); 1223 | } 1224 | 1225 | static Alloc select_on_container_copy_construction(const Alloc& a) 1226 | { 1227 | return a; 1228 | } 1229 | }; 1230 | 1231 | } 1232 | 1233 | #endif 1234 | 1235 | TEST_CASE("[small_vector] static") 1236 | { 1237 | using namespace chobo; 1238 | using namespace std; 1239 | 1240 | static_assert(sizeof(small_vector) - sizeof(small_vector) == sizeof(void*) * 7, "small_vector needs to have a static buffer"); 1241 | { 1242 | small_vector> ivec; 1243 | CHECK(ivec.size() == 0); 1244 | CHECK(ivec.capacity() == 10); 1245 | CHECK(ivec.begin() == ivec.end()); 1246 | CHECK(ivec.cbegin() == ivec.cend()); 1247 | CHECK(ivec.empty()); 1248 | 1249 | auto d = ivec.data(); 1250 | ivec.reserve(9); 1251 | CHECK(ivec.capacity() == 10); 1252 | CHECK(d == ivec.data()); 1253 | 1254 | ivec.resize(2, 8); 1255 | CHECK(ivec.size() == 2); 1256 | CHECK(ivec.front() == 8); 1257 | CHECK(ivec.back() == 8); 1258 | CHECK(d == ivec.data()); 1259 | 1260 | ivec.clear(); 1261 | CHECK(ivec.size() == 0); 1262 | CHECK(ivec.capacity() == 10); 1263 | CHECK(ivec.begin() == ivec.end()); 1264 | CHECK(ivec.cbegin() == ivec.cend()); 1265 | CHECK(ivec.empty()); 1266 | CHECK(d == ivec.data()); 1267 | 1268 | ivec.push_back(5); 1269 | CHECK(ivec.size() == 1); 1270 | CHECK(ivec[0] == 5); 1271 | auto it = ivec.begin(); 1272 | CHECK(it == ivec.data()); 1273 | CHECK(it == ivec.cbegin()); 1274 | CHECK(*it == 5); 1275 | ++it; 1276 | CHECK(it == ivec.end()); 1277 | CHECK(it == ivec.cend()); 1278 | 1279 | auto& back = ivec.emplace_back(3); 1280 | CHECK(ivec.size() == 2); 1281 | auto rit = ivec.rbegin(); 1282 | CHECK(*rit == 3); 1283 | ++rit; 1284 | *rit = 12; 1285 | ++rit; 1286 | CHECK(rit == ivec.rend()); 1287 | CHECK(rit == ivec.crend()); 1288 | CHECK(ivec.front() == 12); 1289 | CHECK(ivec.back() == 3); 1290 | CHECK(back == 3); 1291 | CHECK(&back == &ivec.back()); 1292 | 1293 | ivec.insert(ivec.begin(), 53); 1294 | ivec.insert(ivec.begin() + 2, 90); 1295 | ivec.insert(ivec.begin() + 4, 17); 1296 | ivec.insert(ivec.end(), 6); 1297 | ivec.insert(ivec.begin(), { 1, 2 }); 1298 | 1299 | int ints[] = { 1, 2, 53, 12, 90, 3, 17, 6 }; 1300 | CHECK(ivec.size() == 8); 1301 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 1302 | 1303 | ivec.shrink_to_fit(); 1304 | CHECK(ivec.size() == 8); 1305 | CHECK(ivec.capacity() == 10); 1306 | CHECK(d == ivec.data()); 1307 | 1308 | ivec.revert_to_static(); 1309 | CHECK(ivec.size() == 8); 1310 | CHECK(ivec.capacity() == 10); 1311 | CHECK(d == ivec.data()); 1312 | 1313 | ivec.pop_back(); 1314 | CHECK(ivec.size() == 7); 1315 | CHECK(memcmp(ivec.data(), ints, sizeof(ints) - sizeof(int)) == 0); 1316 | 1317 | ivec.resize(8); 1318 | CHECK(ivec.size() == 8); 1319 | ints[7] = 0; 1320 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 1321 | 1322 | const small_vector> ivec2 = { 1, 2, 3, 4 }; 1323 | CHECK(ivec2.size() == 4); 1324 | CHECK(*ivec2.begin() == 1); 1325 | CHECK(ivec2[1] == 2); 1326 | CHECK(ivec2.at(2) == 3); 1327 | CHECK(*ivec2.rbegin() == 4); 1328 | 1329 | ivec.erase(ivec.begin()); 1330 | CHECK(ivec.size() == 7); 1331 | CHECK(ivec.front() == 2); 1332 | CHECK(memcmp(ivec.data(), ints + 1, ivec.size() * sizeof(int)) == 0); 1333 | 1334 | ivec.erase(ivec.begin() + 2, ivec.begin() + 4); 1335 | CHECK(ivec.size() == 5); 1336 | CHECK(ivec[3] == 17); 1337 | 1338 | small_vector> svec; 1339 | svec.assign({ "as", "df" }); 1340 | CHECK(svec.size() == 2); 1341 | string s1 = "the quick brown fox jumped over the lazy dog 1234567890"; 1342 | auto& rs = svec.emplace_back(s1); 1343 | CHECK(svec.back() == s1); 1344 | CHECK(rs == s1); 1345 | CHECK(&rs == &svec.back()); 1346 | 1347 | auto svec1 = svec; 1348 | CHECK(svec1 == svec); 1349 | 1350 | const void* cstr = svec.back().c_str(); 1351 | auto svec2 = std::move(svec); 1352 | CHECK(svec2.size() == 3); 1353 | CHECK(svec2.back() == s1); 1354 | 1355 | CHECK(svec.empty()); 1356 | CHECK(svec2.back().c_str() == cstr); 1357 | 1358 | svec = std::move(svec2); 1359 | CHECK(svec2.empty()); 1360 | CHECK(svec.back().c_str() == cstr); 1361 | 1362 | svec2 = svec; 1363 | CHECK(svec2.back() == s1); 1364 | CHECK(svec.back() == s1); 1365 | CHECK(svec == svec2); 1366 | 1367 | svec.insert(svec.begin(), s1); 1368 | CHECK(svec.size() == 4); 1369 | CHECK(svec.back().c_str() == cstr); 1370 | CHECK(svec.front() == svec.back()); 1371 | 1372 | cstr = s1.c_str(); 1373 | svec.emplace(svec.begin() + 2, std::move(s1)); 1374 | CHECK(svec.size() == 5); 1375 | CHECK(svec.front() == svec[2]); 1376 | CHECK(svec[2].c_str() == cstr); 1377 | 1378 | svec.clear(); 1379 | CHECK(svec.empty()); 1380 | svec2.clear(); 1381 | CHECK(svec2.empty()); 1382 | CHECK(svec == svec2); 1383 | 1384 | svec.resize(svec.capacity()); 1385 | CHECK(svec.size() == svec.capacity()); 1386 | 1387 | for (auto& s : svec) 1388 | { 1389 | CHECK(s.empty()); 1390 | } 1391 | 1392 | s1 = "asdf"; 1393 | small_vector> cvec(s1.begin(), s1.end()); 1394 | CHECK(cvec.size() == 4); 1395 | CHECK(cvec.front() == 'a'); 1396 | CHECK(cvec.back() == 'f'); 1397 | 1398 | cvec.clear(); 1399 | CHECK(cvec.size() == 0); 1400 | CHECK(cvec.empty()); 1401 | 1402 | s1 = "baz"; 1403 | cvec.assign(s1.begin(), s1.end()); 1404 | CHECK(cvec.size() == 3); 1405 | CHECK(cvec.front() == 'b'); 1406 | CHECK(cvec.back() == 'z'); 1407 | 1408 | // 0 is implicitly castable to nullptr_t which can be an iterator in our case 1409 | small_vector nullptr_test(2, 0); 1410 | CHECK(nullptr_test.size() == 2); 1411 | CHECK(nullptr_test.front() == 0); 1412 | CHECK(nullptr_test.back() == 0); 1413 | 1414 | nullptr_test.assign(3, 0); 1415 | CHECK(nullptr_test.size() == 3); 1416 | CHECK(nullptr_test.front() == 0); 1417 | CHECK(nullptr_test.back() == 0); 1418 | 1419 | nullptr_test.insert(nullptr_test.begin(), 1, 0); 1420 | CHECK(nullptr_test.size() == 4); 1421 | CHECK(nullptr_test.front() == 0); 1422 | } 1423 | 1424 | CHECK(allocations == 0); 1425 | CHECK(deallocations == 0); 1426 | CHECK(allocated_bytes == 0); 1427 | CHECK(deallocated_bytes == 0); 1428 | CHECK(constructions == destructions); 1429 | 1430 | constructions = destructions = 0; 1431 | } 1432 | 1433 | 1434 | TEST_CASE("[small_vector] dynamic") 1435 | { 1436 | using namespace chobo; 1437 | using namespace std; 1438 | { 1439 | small_vector> ivec; 1440 | CHECK(ivec.size() == 0); 1441 | CHECK(ivec.capacity() == 1); 1442 | CHECK(ivec.begin() == ivec.end()); 1443 | CHECK(ivec.cbegin() == ivec.cend()); 1444 | CHECK(ivec.empty()); 1445 | 1446 | auto d = ivec.data(); 1447 | ivec.reserve(2); 1448 | CHECK(ivec.capacity() == 2); 1449 | CHECK(d != ivec.data()); 1450 | CHECK(allocations == 1); 1451 | 1452 | ivec.resize(3, 8); 1453 | CHECK(ivec.capacity() == 3); 1454 | CHECK(ivec.size() == 3); 1455 | CHECK(ivec.front() == 8); 1456 | CHECK(ivec.back() == 8); 1457 | CHECK(d != ivec.data()); 1458 | CHECK(allocations == 2); 1459 | 1460 | ivec.clear(); 1461 | CHECK(ivec.size() == 0); 1462 | CHECK(ivec.capacity() == 3); 1463 | CHECK(d != ivec.data()); 1464 | CHECK(ivec.begin() == ivec.end()); 1465 | CHECK(ivec.cbegin() == ivec.cend()); 1466 | CHECK(ivec.empty()); 1467 | 1468 | ivec.push_back(5); 1469 | CHECK(ivec.size() == 1); 1470 | CHECK(ivec[0] == 5); 1471 | auto it = ivec.begin(); 1472 | CHECK(it == ivec.data()); 1473 | CHECK(it == ivec.cbegin()); 1474 | CHECK(*it == 5); 1475 | ++it; 1476 | CHECK(it == ivec.end()); 1477 | CHECK(it == ivec.cend()); 1478 | 1479 | auto& back = ivec.emplace_back(3); 1480 | CHECK(ivec.size() == 2); 1481 | auto rit = ivec.rbegin(); 1482 | CHECK(*rit == 3); 1483 | ++rit; 1484 | *rit = 12; 1485 | ++rit; 1486 | CHECK(rit == ivec.rend()); 1487 | CHECK(rit == ivec.crend()); 1488 | CHECK(ivec.front() == 12); 1489 | CHECK(ivec.back() == 3); 1490 | CHECK(back == 3); 1491 | CHECK(&back == &ivec.back()); 1492 | 1493 | ivec.insert(ivec.begin(), 53); 1494 | CHECK(ivec.capacity() == 3); 1495 | 1496 | ivec.insert(ivec.begin() + 2, 90); 1497 | ivec.insert(ivec.begin() + 4, 17); 1498 | ivec.insert(ivec.end(), 6); 1499 | ivec.insert(ivec.begin(), { 1, 2 }); 1500 | 1501 | int ints[] = { 1, 2, 53, 12, 90, 3, 17, 6 }; 1502 | CHECK(ivec.capacity() >= 8); 1503 | CHECK(ivec.size() == 8); 1504 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 1505 | 1506 | ivec.pop_back(); 1507 | CHECK(ivec.size() == 7); 1508 | CHECK(memcmp(ivec.data(), ints, sizeof(ints) - sizeof(int)) == 0); 1509 | 1510 | ivec.resize(8); 1511 | CHECK(ivec.size() == 8); 1512 | ints[7] = 0; 1513 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 1514 | 1515 | const small_vector> ivec2 = { 1, 2, 3, 4 }; 1516 | CHECK(ivec2.size() == 4); 1517 | CHECK(*ivec2.begin() == 1); 1518 | CHECK(ivec2[1] == 2); 1519 | CHECK(ivec2.at(2) == 3); 1520 | CHECK(*ivec2.rbegin() == 4); 1521 | 1522 | ivec.erase(ivec.begin()); 1523 | CHECK(ivec.size() == 7); 1524 | CHECK(ivec.front() == 2); 1525 | CHECK(memcmp(ivec.data(), ints + 1, ivec.size() * sizeof(int)) == 0); 1526 | 1527 | ivec.erase(ivec.begin() + 2, ivec.begin() + 4); 1528 | CHECK(ivec.size() == 5); 1529 | CHECK(ivec[3] == 17); 1530 | 1531 | small_vector> svec; 1532 | svec.assign({ "as", "df" }); 1533 | CHECK(svec.size() == 2); 1534 | string s1 = "the quick brown fox jumped over the lazy dog 1234567890"; 1535 | auto& rs = svec.emplace_back(s1); 1536 | CHECK(svec.back() == s1); 1537 | CHECK(rs == s1); 1538 | CHECK(&rs == &svec.back()); 1539 | 1540 | auto svec1 = svec; 1541 | CHECK(svec1 == svec); 1542 | 1543 | const void* cstr = svec.back().c_str(); 1544 | auto svec2 = std::move(svec); 1545 | CHECK(svec2.size() == 3); 1546 | CHECK(svec2.back() == s1); 1547 | 1548 | CHECK(svec.empty()); 1549 | CHECK(svec2.back().c_str() == cstr); 1550 | 1551 | svec = std::move(svec2); 1552 | CHECK(svec2.empty()); 1553 | CHECK(svec.back().c_str() == cstr); 1554 | 1555 | svec2 = svec; 1556 | CHECK(svec2.back() == s1); 1557 | CHECK(svec.back() == s1); 1558 | CHECK(svec == svec2); 1559 | 1560 | svec.insert(svec.begin(), s1); 1561 | CHECK(svec.size() == 4); 1562 | CHECK(svec.back().c_str() == cstr); 1563 | CHECK(svec.front() == svec.back()); 1564 | 1565 | cstr = s1.c_str(); 1566 | svec.emplace(svec.begin() + 2, std::move(s1)); 1567 | CHECK(svec.size() == 5); 1568 | CHECK(svec.front() == svec[2]); 1569 | CHECK(svec[2].c_str() == cstr); 1570 | 1571 | svec.clear(); 1572 | CHECK(svec.empty()); 1573 | svec2.clear(); 1574 | CHECK(svec2.empty()); 1575 | CHECK(svec == svec2); 1576 | 1577 | svec.resize(svec.capacity()); 1578 | CHECK(svec.size() == svec.capacity()); 1579 | 1580 | for (auto& s : svec) 1581 | { 1582 | CHECK(s.empty()); 1583 | } 1584 | 1585 | s1 = "asdf"; 1586 | small_vector> cvec(s1.begin(), s1.end()); 1587 | CHECK(cvec.size() == 4); 1588 | CHECK(cvec.front() == 'a'); 1589 | CHECK(cvec.back() == 'f'); 1590 | 1591 | cvec.clear(); 1592 | CHECK(cvec.size() == 0); 1593 | CHECK(cvec.empty()); 1594 | 1595 | s1 = "baz"; 1596 | cvec.assign(s1.begin(), s1.end()); 1597 | CHECK(cvec.size() == 3); 1598 | CHECK(cvec.front() == 'b'); 1599 | CHECK(cvec.back() == 'z'); 1600 | } 1601 | 1602 | CHECK(allocations == deallocations); 1603 | CHECK(allocated_bytes == deallocated_bytes); 1604 | CHECK(constructions == destructions); 1605 | 1606 | allocations = deallocations = allocated_bytes = deallocated_bytes = constructions = destructions = 0; 1607 | } 1608 | 1609 | TEST_CASE("[small_vector] static-dynamic") 1610 | { 1611 | using namespace chobo; 1612 | using namespace std; 1613 | 1614 | { 1615 | small_vector> ivec; 1616 | auto d = ivec.data(); 1617 | ivec.reserve(20); 1618 | CHECK(ivec.data() == d); 1619 | 1620 | ivec.push_back(1); 1621 | ivec.push_back(2); 1622 | ivec.push_back(3); 1623 | 1624 | CHECK(ivec.data() == d); 1625 | 1626 | ivec.insert(ivec.end(), 3u, 8); 1627 | 1628 | CHECK(ivec.size() == 6); 1629 | CHECK(ivec.capacity() == 20); 1630 | 1631 | auto dd = ivec.data(); 1632 | 1633 | ivec.erase(ivec.begin(), ivec.begin() + 6); 1634 | CHECK(ivec.data() == d); 1635 | CHECK(ivec.empty()); 1636 | 1637 | ivec.resize(19, 11); 1638 | CHECK(ivec.size() == 19); 1639 | CHECK(ivec.capacity() == 20); 1640 | CHECK(ivec.data() == dd); 1641 | 1642 | ivec.resize(4); 1643 | CHECK(ivec.size() == 4); 1644 | CHECK(ivec.capacity() == 20); 1645 | CHECK(ivec.data() == dd); 1646 | 1647 | ivec.revert_to_static(); 1648 | CHECK(ivec.size() == 4); 1649 | CHECK(ivec.capacity() == 5); 1650 | CHECK(ivec.data() == d); 1651 | 1652 | ivec.reserve(10); 1653 | CHECK(ivec.size() == 4); 1654 | CHECK(ivec.capacity() == 20); 1655 | CHECK(ivec.data() == dd); 1656 | 1657 | ivec.shrink_to_fit(); 1658 | CHECK(ivec.size() == 4); 1659 | CHECK(ivec.capacity() == 5); 1660 | CHECK(ivec.data() == d); 1661 | 1662 | ivec.reserve(10); 1663 | CHECK(ivec.size() == 4); 1664 | CHECK(ivec.capacity() == 10); 1665 | CHECK(ivec.data() != d); 1666 | 1667 | dd = ivec.data(); 1668 | ivec.insert(ivec.begin() + 3, 5u, 88); 1669 | CHECK(ivec.size() == 9); 1670 | CHECK(ivec.capacity() == 10); 1671 | CHECK(ivec.data() == dd); 1672 | CHECK(ivec[2] == 11); 1673 | CHECK(ivec[7] == 88); 1674 | CHECK(ivec[8] == 11); 1675 | 1676 | small_vector> ivec2(ivec.begin(), ivec.end()); 1677 | CHECK(ivec2.size() == 9); 1678 | CHECK(ivec2.size() == 9); 1679 | CHECK(ivec2.capacity() == 9); 1680 | CHECK(ivec2[2] == 11); 1681 | CHECK(ivec2[7] == 88); 1682 | CHECK(ivec2[8] == 11); 1683 | 1684 | ivec.erase(ivec.begin() + 1, ivec.end() - 2); 1685 | CHECK(ivec.size() == 3); 1686 | ivec.erase(ivec.end() - 1); 1687 | CHECK(ivec.size() == 2); 1688 | CHECK(ivec.capacity() == 5); 1689 | CHECK(ivec.data() == d); 1690 | 1691 | ivec2.erase(ivec2.begin() + 1, ivec2.end() - 2); 1692 | CHECK(ivec2.size() == 3); 1693 | CHECK(ivec2.capacity() == 3); 1694 | } 1695 | 1696 | CHECK(allocations == deallocations); 1697 | CHECK(allocated_bytes == deallocated_bytes); 1698 | CHECK(constructions == destructions); 1699 | 1700 | allocations = deallocations = allocated_bytes = deallocated_bytes = constructions = destructions = 0; 1701 | } 1702 | 1703 | #if !defined(__EMSCRIPTEN__) || !defined(NDEBUG) // emscripten allows exceptions with -O0 1704 | TEST_CASE("[small_vector] out of range") 1705 | { 1706 | using namespace chobo; 1707 | small_vector ivec; 1708 | ivec.resize(4); 1709 | CHECK(ivec.capacity() == 5); 1710 | 1711 | CHECK_THROWS_AS(ivec.insert(ivec.begin() - 1, 1), std::out_of_range); 1712 | CHECK(ivec.size() == 4); 1713 | CHECK_THROWS_AS(ivec.insert(ivec.end() + 1, 1), std::out_of_range); 1714 | CHECK(ivec.size() == 4); 1715 | CHECK_THROWS_AS(ivec.erase(ivec.begin() - 1), std::out_of_range); 1716 | CHECK(ivec.size() == 4); 1717 | CHECK_THROWS_AS(ivec.erase(ivec.end() + 1), std::out_of_range); 1718 | CHECK(ivec.size() == 4); 1719 | CHECK_THROWS_AS(ivec.erase(ivec.begin() - 1, ivec.begin() + 1), std::out_of_range); 1720 | CHECK(ivec.size() == 4); 1721 | CHECK_THROWS_AS(ivec.erase(ivec.begin() + 2, ivec.end() + 1), std::out_of_range); 1722 | CHECK(ivec.size() == 4); 1723 | CHECK_THROWS_AS(ivec.erase(ivec.end() + 1, ivec.end() + 3), std::out_of_range); 1724 | CHECK(ivec.size() == 4); 1725 | CHECK_THROWS_AS(ivec.erase(ivec.end() - 1, ivec.begin() + 1), std::out_of_range); 1726 | CHECK(ivec.size() == 4); 1727 | 1728 | } 1729 | #endif 1730 | 1731 | 1732 | #endif 1733 | -------------------------------------------------------------------------------- /include/chobo/static_vector.hpp: -------------------------------------------------------------------------------- 1 | // chobo-static-vector v1.03 2 | // 3 | // std::vector-like class with a fixed capacity 4 | // 5 | // MIT License: 6 | // Copyright(c) 2016-2019 Chobolabs Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files(the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and / or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions : 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | // 28 | // VERSION HISTORY 29 | // 30 | // 1.03 (2019-11-04) Proper noexcept specifiers on move ctor and assignment 31 | // 1.02 (2017-02-05) Added swap to make it a better drop-in replacement of std::vector 32 | // 1.01 (2016-09-27) Qualified operator new. Fixed self usurp on assignment 33 | // 1.00 (2016-09-23) First public release 34 | // 35 | // 36 | // DOCUMENTATION 37 | // 38 | // Simply include this file wherever you need. 39 | // It defines the class chobo::static_vector, which is almost a drop-in 40 | // replacement of std::vector, but has a fixed capacity as a template argument. 41 | // It gives you the benefits of using std::array (cache-locality) with the 42 | // flexibility of std::vector - dynamic size. 43 | // However the size may never exceed its initially set capacity. 44 | // 45 | // Example: 46 | // 47 | // chobo::static_vector myvec; // a static vector of size 0 and capacity 4 48 | // myvec.resize(2); // vector is {0,0} 49 | // myvec[1] = 11; // vector is {0,11} 50 | // myvec.push_back(7); // vector is {0,11,7} 51 | // myvec.insert(myvec.begin() + 1, 3); // vector is {0,3,11,7} 52 | // myvec.push_back(5); // runtime error 53 | // myvec.erase(myvec.begin()); // vector is {3,11,7} 54 | // myvec.resize(5); // runtime error 55 | // 56 | // Configuration 57 | // 58 | // The library has two configuration options. They can be set as #define-s 59 | // before including the header file, but it is recommended to change the code 60 | // of the library itself with the values you want, especially if you include 61 | // the library in many compilation units (as opposed to, say, a precompiled 62 | // header or a central header). 63 | // 64 | // Config out of range error handling 65 | // 66 | // An out of range error is a runtime error which is triggered when the vector 67 | // needs to be resized with a size higher than its capacity. 68 | // For example: chobo::static_vector v(101); 69 | // 70 | // This is set by defining CHOBO_STATIC_VECTOR_ERROR_HANDLING to one of the 71 | // following values: 72 | // * CHOBO_STATIC_VECTOR_ERROR_HANDLING_NONE - no error handling. Crashes WILL 73 | // ensue if the error is triggered. 74 | // * CHOBO_STATIC_VECTOR_ERROR_HANDLING_THROW - std::out_of_range is thrown. 75 | // * CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT - asserions are triggered. 76 | // * CHOBO_STATIC_VECTOR_ERROR_HANDLING_RESCUE - the error is ignored but 77 | // sanity is (somewhat) preserved. Functions which trigger the error will 78 | // just bail without changing the vector. 79 | // * CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT_AND_THROW - combines assert and 80 | // throw to catch errors more easily in debug mode 81 | // * CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT_AND_RESCUE - combines assert and 82 | // rescue to catch errors in debug mode and silently bail in release mode. 83 | // 84 | // To set this setting by editing the file change the line: 85 | // ``` 86 | // # define CHOBO_STATIC_VECTOR_ERROR_HANDLING CHOBO_STATIC_VECTOR_ERROR_HANDLING_THROW 87 | // ``` 88 | // to the default setting of your choice 89 | // 90 | // Config bounds checks: 91 | // 92 | // By default bounds checks are made in debug mode (via an asser) when accessing 93 | // elements (with `at` or `[]`). Iterators are not checked (yet...) 94 | // 95 | // To disable them, you can define CHOBO_STATIC_VECTOR_NO_DEBUG_BOUNDS_CHECK 96 | // before including the header. 97 | // 98 | // 99 | // MISC 100 | // 101 | // * There is an unused (and unusable) allocator class defined inside 102 | // static_vector. It's point is to be a sham for templates which refer to 103 | // container::allocator. It also allows it to work with chobo::flat_map 104 | // 105 | // TESTS 106 | // 107 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 108 | // To run them, define CHOBO_STATIC_VECTOR_TEST_WITH_DOCTEST before including 109 | // the header in a file which has doctest.h already included. 110 | // 111 | #pragma once 112 | 113 | #include 114 | #include 115 | #include 116 | 117 | #define CHOBO_STATIC_VECTOR_ERROR_HANDLING_NONE 0 118 | #define CHOBO_STATIC_VECTOR_ERROR_HANDLING_THROW 1 119 | #define CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT 2 120 | #define CHOBO_STATIC_VECTOR_ERROR_HANDLING_RESCUE 3 121 | #define CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT_AND_THROW 4 122 | #define CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT_AND_RESCUE 5 123 | 124 | #if !defined(CHOBO_STATIC_VECTOR_ERROR_HANDLING) 125 | # define CHOBO_STATIC_VECTOR_ERROR_HANDLING CHOBO_STATIC_VECTOR_ERROR_HANDLING_THROW 126 | #endif 127 | 128 | #if CHOBO_STATIC_VECTOR_ERROR_HANDLING == CHOBO_STATIC_VECTOR_ERROR_HANDLING_NONE 129 | # define _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) 130 | #elif CHOBO_STATIC_VECTOR_ERROR_HANDLING == CHOBO_STATIC_VECTOR_ERROR_HANDLING_THROW 131 | # include 132 | # define _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) if (cond) throw std::out_of_range("chobo::static_vector out of range") 133 | #elif CHOBO_STATIC_VECTOR_ERROR_HANDLING == CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT 134 | # include 135 | # define _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) assert(!(cond) && "chobo::static_vector out of range") 136 | #elif CHOBO_STATIC_VECTOR_ERROR_HANDLING == CHOBO_STATIC_VECTOR_ERROR_HANDLING_RESCUE 137 | # define _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) if (cond) return rescue_return 138 | #elif CHOBO_STATIC_VECTOR_ERROR_HANDLING == CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT_AND_THROW 139 | # include 140 | # include 141 | # define _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) \ 142 | do { if (cond) { assert(false && "chobo::static_vector out of range"); throw std::out_of_range("chobo::static_vector out of range"); } } while(false) 143 | #elif CHOBO_STATIC_VECTOR_ERROR_HANDLING == CHOBO_STATIC_VECTOR_ERROR_HANDLING_ASSERT_AND_RESCUE 144 | # include 145 | # define _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(cond, rescue_return) \ 146 | do { if (cond) { assert(false && "chobo::static_vector out of range"); return rescue_return; } } while(false) 147 | #else 148 | #error "Unknown CHOBO_STATIC_VECTOR_ERRROR_HANDLING" 149 | #endif 150 | 151 | 152 | #if defined(CHOBO_STATIC_VECTOR_NO_DEBUG_BOUNDS_CHECK) 153 | # define _CHOBO_STATIC_VECTOR_BOUNDS_CHECK(i) 154 | #else 155 | # include 156 | # define _CHOBO_STATIC_VECTOR_BOUNDS_CHECK(i) assert((i) < this->size()) 157 | #endif 158 | 159 | namespace chobo 160 | { 161 | 162 | template 163 | struct static_vector 164 | { 165 | struct fake_allocator 166 | { 167 | typedef T value_type; 168 | typedef T* pointer; 169 | typedef const T* const_pointer; 170 | }; 171 | 172 | public: 173 | using value_type = T; 174 | using size_type = size_t; 175 | using difference_type = ptrdiff_t; 176 | using reference = T&; 177 | using const_reference = const T&; 178 | using pointer = T*; 179 | using const_pointer = const T*; 180 | using iterator = T*; 181 | using const_iterator = const T*; 182 | using reverse_iterator = std::reverse_iterator; 183 | using const_reverse_iterator = std::reverse_iterator; 184 | using allocator_type = fake_allocator; 185 | 186 | static_vector() = default; 187 | 188 | explicit static_vector(size_t count) 189 | { 190 | resize(count); 191 | } 192 | 193 | static_vector(size_t count, const T& value) 194 | { 195 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(count > Capacity, ); 196 | 197 | for (size_t i = 0; i < count; ++i) 198 | { 199 | push_back(value); 200 | } 201 | } 202 | 203 | static_vector(std::initializer_list l) 204 | { 205 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(l.size() > Capacity, ); 206 | 207 | for (auto&& i : l) 208 | { 209 | push_back(i); 210 | } 211 | } 212 | 213 | static_vector(const static_vector& v) 214 | { 215 | for (const auto& i : v) 216 | { 217 | push_back(i); 218 | } 219 | } 220 | 221 | template 222 | static_vector(const static_vector& v) 223 | { 224 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(v.size() > Capacity, ); 225 | 226 | for (const auto& i : v) 227 | { 228 | push_back(i); 229 | } 230 | } 231 | 232 | static_vector(static_vector&& v) noexcept(std::is_nothrow_move_constructible::value) 233 | { 234 | for (auto i = v.begin(); i != v.end(); ++i) 235 | { 236 | emplace_back(std::move(*i)); 237 | } 238 | v.clear(); 239 | } 240 | 241 | ~static_vector() 242 | { 243 | clear(); 244 | } 245 | 246 | static_vector& operator=(const static_vector& v) 247 | { 248 | if (this == &v) 249 | { 250 | // prevent self usurp 251 | return *this; 252 | } 253 | 254 | clear(); 255 | for (auto& elem : v) 256 | { 257 | push_back(elem); 258 | } 259 | 260 | return *this; 261 | } 262 | 263 | static_vector& operator=(static_vector&& v) noexcept(std::is_nothrow_move_assignable::value) 264 | { 265 | clear(); 266 | for (auto i = v.begin(); i!=v.end(); ++i) 267 | { 268 | emplace_back(std::move(*i)); 269 | } 270 | 271 | v.clear(); 272 | return *this; 273 | } 274 | 275 | const_reference at(size_type i) const 276 | { 277 | _CHOBO_STATIC_VECTOR_BOUNDS_CHECK(i); 278 | return *reinterpret_cast(m_data + i); 279 | } 280 | 281 | reference at(size_type i) 282 | { 283 | _CHOBO_STATIC_VECTOR_BOUNDS_CHECK(i); 284 | return *reinterpret_cast(m_data + i); 285 | } 286 | 287 | const_reference operator[](size_type i) const 288 | { 289 | return at(i); 290 | } 291 | 292 | reference operator[](size_type i) 293 | { 294 | return at(i); 295 | } 296 | 297 | const_reference front() const 298 | { 299 | return at(0); 300 | } 301 | 302 | reference front() 303 | { 304 | return at(0); 305 | } 306 | 307 | const_reference back() const 308 | { 309 | return at(m_size - 1); 310 | } 311 | 312 | reference back() 313 | { 314 | return at(m_size - 1); 315 | } 316 | 317 | const_pointer data() const noexcept 318 | { 319 | return reinterpret_cast(m_data); 320 | } 321 | 322 | pointer data() noexcept 323 | { 324 | return reinterpret_cast(m_data); 325 | } 326 | 327 | // iterators 328 | iterator begin() noexcept 329 | { 330 | return data(); 331 | } 332 | 333 | const_iterator begin() const noexcept 334 | { 335 | return data(); 336 | } 337 | 338 | const_iterator cbegin() const noexcept 339 | { 340 | return data(); 341 | } 342 | 343 | iterator end() noexcept 344 | { 345 | return data() + m_size; 346 | } 347 | 348 | const_iterator end() const noexcept 349 | { 350 | return data() + m_size; 351 | } 352 | 353 | const_iterator cend() const noexcept 354 | { 355 | return data() + m_size; 356 | } 357 | 358 | reverse_iterator rbegin() noexcept 359 | { 360 | return reverse_iterator(end()); 361 | } 362 | 363 | const_reverse_iterator rbegin() const noexcept 364 | { 365 | return const_reverse_iterator(end()); 366 | } 367 | 368 | const_reverse_iterator crbegin() const noexcept 369 | { 370 | return const_reverse_iterator(end()); 371 | } 372 | 373 | reverse_iterator rend() noexcept 374 | { 375 | return reverse_iterator(begin()); 376 | } 377 | 378 | const_reverse_iterator rend() const noexcept 379 | { 380 | return const_reverse_iterator(begin()); 381 | } 382 | 383 | const_reverse_iterator crend() const noexcept 384 | { 385 | return const_reverse_iterator(begin()); 386 | } 387 | 388 | // capacity 389 | bool empty() const noexcept 390 | { 391 | return m_size == 0; 392 | } 393 | 394 | size_t size() const noexcept 395 | { 396 | return m_size; 397 | } 398 | 399 | size_t max_size() const noexcept 400 | { 401 | return Capacity; 402 | } 403 | 404 | size_t capacity() const noexcept 405 | { 406 | return Capacity; 407 | } 408 | 409 | // modifiers 410 | void pop_back() 411 | { 412 | reinterpret_cast(m_data + m_size - 1)->~T(); 413 | --m_size; 414 | } 415 | 416 | void clear() noexcept 417 | { 418 | while (!empty()) 419 | { 420 | pop_back(); 421 | } 422 | } 423 | 424 | void push_back(const_reference v) 425 | { 426 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(size() >= Capacity, ); 427 | 428 | ::new(m_data + m_size) T(v); 429 | ++m_size; 430 | } 431 | 432 | void push_back(T&& v) 433 | { 434 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(size() >= Capacity, ); 435 | 436 | ::new(m_data + m_size) T(std::move(v)); 437 | ++m_size; 438 | } 439 | 440 | template 441 | void emplace_back(Args&&... args) 442 | { 443 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(size() >= Capacity, ); 444 | 445 | ::new(m_data + m_size) T(std::forward(args)...); 446 | ++m_size; 447 | } 448 | 449 | iterator insert(iterator position, const value_type& val) 450 | { 451 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(size() >= Capacity, position); 452 | 453 | if (position == end()) 454 | { 455 | emplace_back(val); 456 | return position; 457 | } 458 | 459 | emplace_back(std::move(back())); 460 | 461 | for (auto i = end() - 2; i != position; --i) 462 | { 463 | *i = std::move(*(i - 1)); 464 | } 465 | 466 | *position = val; 467 | 468 | return position; 469 | } 470 | 471 | template 472 | iterator emplace(iterator position, Args&&... args) 473 | { 474 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(size() >= Capacity, position); 475 | 476 | if (position == end()) 477 | { 478 | emplace_back(std::forward(args)...); 479 | return position; 480 | } 481 | 482 | emplace_back(std::move(back())); 483 | 484 | for (auto i = end() - 2; i != position; --i) 485 | { 486 | *i = std::move(*(i - 1)); 487 | } 488 | 489 | position->~T(); 490 | ::new (position) T(std::forward(args)...); 491 | 492 | return position; 493 | } 494 | 495 | iterator erase(iterator position) 496 | { 497 | auto dist = position - begin(); 498 | position->~T(); 499 | 500 | for (auto i = position + 1; i != end(); ++i) 501 | { 502 | *(i - 1) = std::move(*i); 503 | } 504 | 505 | resize(size() - 1); 506 | return begin() + dist; 507 | } 508 | 509 | void resize(size_type n) 510 | { 511 | _CHOBO_STATIC_VECTOR_OUT_OF_RANGE_IF(n > Capacity, ); 512 | 513 | while (m_size > n) 514 | { 515 | pop_back(); 516 | } 517 | 518 | while (n > m_size) 519 | { 520 | emplace_back(); 521 | } 522 | } 523 | 524 | void swap(static_vector& v) 525 | { 526 | static_vector* longer; 527 | static_vector* shorter; 528 | 529 | if (v.m_size > m_size) 530 | { 531 | longer = &v; 532 | shorter = this; 533 | } 534 | else 535 | { 536 | longer = this; 537 | shorter = &v; 538 | } 539 | 540 | for (size_t i = 0; i < shorter->size(); ++i) 541 | { 542 | std::swap(shorter->at(i), longer->at(i)); 543 | } 544 | 545 | auto short_size = shorter->m_size; 546 | 547 | for (size_t i = shorter->size(); i < longer->size(); ++i) 548 | { 549 | shorter->emplace_back(std::move(longer->at(i))); 550 | longer->at(i).~T(); 551 | } 552 | 553 | longer->m_size = short_size; 554 | } 555 | 556 | private: 557 | typename std::aligned_storage::value>::type m_data[Capacity]; 558 | size_t m_size = 0; 559 | }; 560 | 561 | template 562 | bool operator==(const static_vector& a, const static_vector& b) 563 | { 564 | if (a.size() != b.size()) 565 | { 566 | return false; 567 | } 568 | 569 | for (size_t i = 0; i < a.size(); ++i) 570 | { 571 | if (a[i] != b[i]) 572 | return false; 573 | } 574 | 575 | return true; 576 | } 577 | 578 | template 579 | bool operator!=(const static_vector& a, const static_vector& b) 580 | { 581 | if (a.size() != b.size()) 582 | { 583 | return true; 584 | } 585 | 586 | for (size_t i = 0; i < a.size(); ++i) 587 | { 588 | if (a[i] != b[i]) 589 | return true; 590 | } 591 | 592 | return false; 593 | } 594 | 595 | } 596 | 597 | 598 | #if defined(CHOBO_STATIC_VECTOR_TEST_WITH_DOCTEST) 599 | 600 | #include 601 | 602 | TEST_CASE("[static_vector] test") 603 | { 604 | using namespace chobo; 605 | using namespace std; 606 | 607 | static_vector ivec; 608 | 609 | static_assert(sizeof(ivec) == sizeof(int) * 10 + sizeof(size_t), "static_vector must have size of N*t + size_t"); 610 | 611 | CHECK(ivec.size() == 0); 612 | CHECK(ivec.capacity() == 10); 613 | CHECK(ivec.max_size() == 10); 614 | CHECK(ivec.begin() == ivec.end()); 615 | CHECK(ivec.cbegin() == ivec.cend()); 616 | CHECK(ivec.empty()); 617 | 618 | ivec.push_back(5); 619 | CHECK(ivec.size() == 1); 620 | CHECK(ivec[0] == 5); 621 | auto it = ivec.begin(); 622 | CHECK(it == ivec.data()); 623 | CHECK(it == ivec.cbegin()); 624 | CHECK(*it == 5); 625 | ++it; 626 | CHECK(it == ivec.end()); 627 | CHECK(it == ivec.cend()); 628 | 629 | ivec.emplace_back(3); 630 | CHECK(ivec.size() == 2); 631 | auto rit = ivec.rbegin(); 632 | CHECK(*rit == 3); 633 | ++rit; 634 | *rit = 12; 635 | ++rit; 636 | CHECK(rit == ivec.rend()); 637 | CHECK(rit == ivec.crend()); 638 | CHECK(ivec.front() == 12); 639 | CHECK(ivec.back() == 3); 640 | 641 | ivec.insert(ivec.begin(), 53); 642 | ivec.insert(ivec.begin() + 2, 90); 643 | ivec.insert(ivec.begin() + 4, 17); 644 | ivec.insert(ivec.end(), 6); 645 | 646 | int ints[] = { 53, 12, 90, 3, 17, 6 }; 647 | CHECK(ivec.size() == 6); 648 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 649 | 650 | ivec.pop_back(); 651 | CHECK(ivec.size() == 5); 652 | CHECK(memcmp(ivec.data(), ints, sizeof(ints) - sizeof(int)) == 0); 653 | 654 | ivec.resize(6); 655 | CHECK(ivec.size() == 6); 656 | ints[5] = 0; 657 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 658 | 659 | const static_vector ivec2 = { 1, 2, 3, 4 }; 660 | CHECK(ivec2.size() == 4); 661 | CHECK(*ivec2.begin() == 1); 662 | CHECK(ivec2[1] == 2); 663 | CHECK(ivec2.at(2) == 3); 664 | CHECK(*ivec2.rbegin() == 4); 665 | 666 | ivec.erase(ivec.begin()); 667 | CHECK(ivec.size() == 5); 668 | CHECK(ivec.front() == 12); 669 | CHECK(memcmp(ivec.data(), ints + 1, ivec.size() * sizeof(int)) == 0); 670 | 671 | ivec.erase(ivec.begin() + 2); 672 | CHECK(ivec.size() == 4); 673 | CHECK(ivec[2] == 17); 674 | 675 | static_vector svec = { "as", "df" }; 676 | CHECK(svec.size() == 2); 677 | string s1 = "the quick brown fox jumped over the lazy dog 1234567890"; 678 | svec.emplace_back(s1); 679 | CHECK(svec.back() == s1); 680 | 681 | auto svec1 = svec; 682 | static_assert(sizeof(svec1) == sizeof(string) * 11 + sizeof(size_t), "static_vector must have size of N*t + size_t"); 683 | CHECK(svec1 == svec); 684 | 685 | const void* cstr = svec.back().c_str(); 686 | auto svec2 = std::move(svec); 687 | CHECK(svec2.size() == 3); 688 | CHECK(svec2.back() == s1); 689 | 690 | CHECK(svec.empty()); 691 | CHECK(svec2.back().c_str() == cstr); 692 | 693 | svec = std::move(svec2); 694 | CHECK(svec2.empty()); 695 | CHECK(svec.back().c_str() == cstr); 696 | 697 | svec2 = svec; 698 | CHECK(svec2.back() == s1); 699 | CHECK(svec.back() == s1); 700 | CHECK(svec == svec2); 701 | 702 | svec.insert(svec.begin(), s1); 703 | CHECK(svec.size() == 4); 704 | CHECK(svec.back().c_str() == cstr); 705 | CHECK(svec.front() == svec.back()); 706 | 707 | cstr = s1.c_str(); 708 | svec.emplace(svec.begin() + 2, std::move(s1)); 709 | CHECK(svec.size() == 5); 710 | CHECK(svec.front() == svec[2]); 711 | CHECK(svec[2].c_str() == cstr); 712 | 713 | svec.clear(); 714 | CHECK(svec.empty()); 715 | svec2.clear(); 716 | CHECK(svec2.empty()); 717 | CHECK(svec == svec2); 718 | 719 | svec.resize(svec.capacity()); 720 | CHECK(svec.size() == svec.capacity()); 721 | 722 | for (auto& s : svec) 723 | { 724 | CHECK(s.empty()); 725 | } 726 | 727 | #if !defined(__EMSCRIPTEN__) || !defined(NDEBUG) // emscripten allows exceptions with -O0 728 | CHECK_THROWS_AS(svec.push_back("asd"), std::out_of_range); 729 | CHECK(svec.size() == svec.capacity()); 730 | CHECK_THROWS_AS(svec.resize(55), std::out_of_range); 731 | CHECK(svec.size() == svec.capacity()); 732 | CHECK_THROWS_AS(svec.insert(svec.begin(), "55"), std::out_of_range); 733 | CHECK(svec.size() == svec.capacity()); 734 | CHECK_THROWS_AS(svec.emplace(svec.begin(), "55"), std::out_of_range); 735 | CHECK(svec.size() == svec.capacity()); 736 | #endif 737 | 738 | // self usurp 739 | svec = svec; 740 | CHECK(svec.size() == svec.capacity()); 741 | 742 | // swap 743 | svec = { "1", "2", "3" }; 744 | svec2 = { "4", "5", "6", "7" }; 745 | 746 | svec.swap(svec2); 747 | 748 | CHECK(svec.size() == 4); 749 | CHECK(svec2.size() == 3); 750 | CHECK(svec2.front() == "1"); 751 | CHECK(svec.back() == "7"); 752 | 753 | svec = { "a", "b", "c" }; 754 | svec2.swap(svec); 755 | 756 | CHECK(svec2.size() == svec.size()); 757 | CHECK(svec2.back() == "c"); 758 | CHECK(svec.front() == "1"); 759 | } 760 | 761 | #endif 762 | 763 | -------------------------------------------------------------------------------- /include/chobo/vector.hpp: -------------------------------------------------------------------------------- 1 | // chobo-vector v1.02 2 | // 3 | // An std::vector-like class with no debug bounds checks which is faster in 4 | // "Debug" mode 5 | // 6 | // MIT License: 7 | // Copyright(c) 2017-2018 Chobolabs Inc. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files(the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and / or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions : 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | // 29 | // VERSION HISTORY 30 | // 31 | // 1.02 (2018-11-29) Removed references to deprecated std::allocator members 32 | // 1.01 (2017-04-02) Fixed compilation error on (count, value) constructor and 33 | // assign, and insert methods when count or value is 0 34 | // 1.00 (2017-03-10) First public release 35 | // 36 | // 37 | // DOCUMENTATION 38 | // 39 | // Simply include this file wherever you need. 40 | // It defines the class chobo::vector, which is a drop-in replacement of 41 | // std::vector. Unlike some standard library releases, most notably 42 | // Dincumware's, it has no debug iterator checks, thus having a much, much 43 | // better performance in "Debug" mode. 44 | // 45 | // TESTS 46 | // 47 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 48 | // To run them, define CHOBO_VECTOR_TEST_WITH_DOCTEST before including 49 | // the header in a file which has doctest.h already included. 50 | // 51 | #pragma once 52 | 53 | #include 54 | #include 55 | #include 56 | 57 | namespace chobo 58 | { 59 | 60 | template> 61 | struct vector 62 | { 63 | using atraits = std::allocator_traits; 64 | public: 65 | using allocator_type = Alloc; 66 | using value_type = typename atraits::value_type; 67 | using size_type = typename atraits::size_type; 68 | using difference_type = typename atraits::difference_type; 69 | using reference = T & ; 70 | using const_reference = const T&; 71 | using pointer = typename atraits::pointer; 72 | using const_pointer = typename atraits::const_pointer; 73 | using iterator = pointer; 74 | using const_iterator = const_pointer; 75 | using reverse_iterator = std::reverse_iterator; 76 | using const_reverse_iterator = std::reverse_iterator; 77 | 78 | vector() 79 | : vector(Alloc()) 80 | {} 81 | 82 | vector(const Alloc& alloc) 83 | : m_capacity(0) 84 | , m_alloc(alloc) 85 | { 86 | m_begin = m_end = nullptr; 87 | } 88 | 89 | explicit vector(size_t count, const Alloc& alloc = Alloc()) 90 | : vector(alloc) 91 | { 92 | resize(count); 93 | } 94 | 95 | explicit vector(size_t count, const T& value, const Alloc& alloc = Alloc()) 96 | : vector(alloc) 97 | { 98 | assign_impl(count, value); 99 | } 100 | 101 | template ())> 102 | vector(InputIterator first, InputIterator last, const Alloc& alloc = Alloc()) 103 | : vector(alloc) 104 | { 105 | assign_impl(first, last); 106 | } 107 | 108 | vector(std::initializer_list l, const Alloc& alloc = Alloc()) 109 | : vector(alloc) 110 | { 111 | assign_impl(l); 112 | } 113 | 114 | vector(const vector& v) 115 | : vector(v, atraits::select_on_container_copy_construction(v.get_allocator())) 116 | {} 117 | 118 | vector(const vector& v, const Alloc& alloc) 119 | : vector(alloc) 120 | { 121 | assign_impl(v.begin(), v.end()); 122 | } 123 | 124 | vector(vector&& v) 125 | : m_capacity(v.m_capacity) 126 | , m_alloc(std::move(v.m_alloc)) 127 | , m_begin(v.m_begin) 128 | , m_end(v.m_end) 129 | { 130 | v.m_begin = v.m_end = nullptr; 131 | v.m_capacity = 0; 132 | } 133 | 134 | ~vector() 135 | { 136 | clear(); 137 | 138 | if (m_begin) 139 | { 140 | atraits::deallocate(m_alloc, m_begin, m_capacity); 141 | } 142 | } 143 | 144 | vector& operator=(const vector& v) 145 | { 146 | if (this == &v) 147 | { 148 | // prevent self usurp 149 | return *this; 150 | } 151 | 152 | clear(); 153 | 154 | assign_impl(v.begin(), v.end()); 155 | 156 | return *this; 157 | } 158 | 159 | vector& operator=(vector&& v) 160 | { 161 | clear(); 162 | 163 | m_alloc = std::move(v.m_alloc); 164 | m_capacity = v.m_capacity; 165 | m_begin = v.m_begin; 166 | m_end = v.m_end; 167 | 168 | v.m_begin = v.m_end = nullptr; 169 | v.m_capacity = 0; 170 | 171 | return *this; 172 | } 173 | 174 | void assign(size_type count, const T& value) 175 | { 176 | clear(); 177 | assign_impl(count, value); 178 | } 179 | 180 | template ())> 181 | void assign(InputIterator first, InputIterator last) 182 | { 183 | clear(); 184 | assign_impl(first, last); 185 | } 186 | 187 | void assign(std::initializer_list ilist) 188 | { 189 | clear(); 190 | assign_impl(ilist); 191 | } 192 | 193 | allocator_type get_allocator() const 194 | { 195 | return m_alloc; 196 | } 197 | 198 | const_reference at(size_type i) const 199 | { 200 | return *(m_begin + i); 201 | } 202 | 203 | reference at(size_type i) 204 | { 205 | return *(m_begin + i); 206 | } 207 | 208 | const_reference operator[](size_type i) const 209 | { 210 | return at(i); 211 | } 212 | 213 | reference operator[](size_type i) 214 | { 215 | return at(i); 216 | } 217 | 218 | const_reference front() const 219 | { 220 | return at(0); 221 | } 222 | 223 | reference front() 224 | { 225 | return at(0); 226 | } 227 | 228 | const_reference back() const 229 | { 230 | return *(m_end - 1); 231 | } 232 | 233 | reference back() 234 | { 235 | return *(m_end - 1); 236 | } 237 | 238 | const_pointer data() const noexcept 239 | { 240 | return m_begin; 241 | } 242 | 243 | pointer data() noexcept 244 | { 245 | return m_begin; 246 | } 247 | 248 | // iterators 249 | iterator begin() noexcept 250 | { 251 | return m_begin; 252 | } 253 | 254 | const_iterator begin() const noexcept 255 | { 256 | return m_begin; 257 | } 258 | 259 | const_iterator cbegin() const noexcept 260 | { 261 | return m_begin; 262 | } 263 | 264 | iterator end() noexcept 265 | { 266 | return m_end; 267 | } 268 | 269 | const_iterator end() const noexcept 270 | { 271 | return m_end; 272 | } 273 | 274 | const_iterator cend() const noexcept 275 | { 276 | return m_end; 277 | } 278 | 279 | reverse_iterator rbegin() noexcept 280 | { 281 | return reverse_iterator(end()); 282 | } 283 | 284 | const_reverse_iterator rbegin() const noexcept 285 | { 286 | return const_reverse_iterator(end()); 287 | } 288 | 289 | const_reverse_iterator crbegin() const noexcept 290 | { 291 | return const_reverse_iterator(end()); 292 | } 293 | 294 | reverse_iterator rend() noexcept 295 | { 296 | return reverse_iterator(begin()); 297 | } 298 | 299 | const_reverse_iterator rend() const noexcept 300 | { 301 | return const_reverse_iterator(begin()); 302 | } 303 | 304 | const_reverse_iterator crend() const noexcept 305 | { 306 | return const_reverse_iterator(begin()); 307 | } 308 | 309 | // capacity 310 | bool empty() const noexcept 311 | { 312 | return m_begin == m_end; 313 | } 314 | 315 | size_t size() const noexcept 316 | { 317 | return m_end - m_begin; 318 | } 319 | 320 | size_t max_size() const noexcept 321 | { 322 | return m_alloc.max_size(); 323 | } 324 | 325 | 326 | void reserve(size_t desired_capacity) 327 | { 328 | if (desired_capacity <= m_capacity) 329 | { 330 | return; 331 | } 332 | 333 | size_type new_cap = find_capacity(desired_capacity); 334 | 335 | auto new_buf = atraits::allocate(m_alloc, new_cap); 336 | const auto s = size(); 337 | 338 | // now we need to transfer the existing elements into the new buffer 339 | for (size_type i = 0; i < s; ++i) 340 | { 341 | atraits::construct(m_alloc, new_buf + i, std::move(*(m_begin + i))); 342 | } 343 | 344 | if (m_begin) 345 | { 346 | // free old elements 347 | for (size_type i = 0; i < s; ++i) 348 | { 349 | atraits::destroy(m_alloc, m_begin + i); 350 | } 351 | 352 | atraits::deallocate(m_alloc, m_begin, m_capacity); 353 | } 354 | 355 | m_begin = new_buf; 356 | m_end = new_buf + s; 357 | m_capacity = new_cap; 358 | } 359 | 360 | size_t capacity() const noexcept 361 | { 362 | return m_capacity; 363 | } 364 | 365 | void shrink_to_fit() 366 | { 367 | const auto s = size(); 368 | 369 | if (s == m_capacity) return; 370 | 371 | if (s == 0) 372 | { 373 | atraits::deallocate(m_alloc, m_begin, m_capacity); 374 | m_capacity = 0; 375 | m_begin = m_end = nullptr; 376 | return; 377 | } 378 | 379 | auto new_buf = atraits::allocate(m_alloc, s); 380 | 381 | // now we need to transfer the existing elements into the new buffer 382 | for (size_type i = 0; i < s; ++i) 383 | { 384 | atraits::construct(m_alloc, new_buf + i, std::move(*(m_begin + i))); 385 | } 386 | 387 | // free old elements 388 | for (size_type i = 0; i < s; ++i) 389 | { 390 | atraits::destroy(m_alloc, m_begin + i); 391 | } 392 | 393 | atraits::deallocate(m_alloc, m_begin, m_capacity); 394 | 395 | m_begin = new_buf; 396 | m_end = new_buf + s; 397 | m_capacity = s; 398 | } 399 | 400 | // modifiers 401 | void clear() noexcept 402 | { 403 | for (auto p = m_begin; p != m_end; ++p) 404 | { 405 | atraits::destroy(m_alloc, p); 406 | } 407 | 408 | m_end = m_begin; 409 | } 410 | 411 | iterator insert(const_iterator position, const value_type& val) 412 | { 413 | auto pos = grow_at(position, 1); 414 | atraits::construct(m_alloc, pos, val); 415 | return pos; 416 | } 417 | 418 | iterator insert(const_iterator position, value_type&& val) 419 | { 420 | auto pos = grow_at(position, 1); 421 | atraits::construct(m_alloc, pos, std::move(val)); 422 | return pos; 423 | } 424 | 425 | iterator insert(const_iterator position, size_type count, const value_type& val) 426 | { 427 | auto pos = grow_at(position, count); 428 | for (size_type i = 0; i < count; ++i) 429 | { 430 | atraits::construct(m_alloc, pos + i, val); 431 | } 432 | return pos; 433 | } 434 | 435 | template ())> 436 | iterator insert(const_iterator position, InputIterator first, InputIterator last) 437 | { 438 | auto pos = grow_at(position, last - first); 439 | size_type i = 0; 440 | auto np = pos; 441 | for (auto p = first; p != last; ++p, ++np) 442 | { 443 | atraits::construct(m_alloc, np, *p); 444 | } 445 | return pos; 446 | } 447 | 448 | iterator insert(const_iterator position, std::initializer_list ilist) 449 | { 450 | auto pos = grow_at(position, ilist.size()); 451 | size_type i = 0; 452 | for (auto& elem : ilist) 453 | { 454 | atraits::construct(m_alloc, pos + i, elem); 455 | ++i; 456 | } 457 | return pos; 458 | } 459 | 460 | template 461 | iterator emplace(const_iterator position, Args&&... args) 462 | { 463 | auto pos = grow_at(position, 1); 464 | atraits::construct(m_alloc, pos, std::forward(args)...); 465 | return pos; 466 | } 467 | 468 | iterator erase(const_iterator position) 469 | { 470 | return shrink_at(position, 1); 471 | } 472 | 473 | iterator erase(const_iterator first, const_iterator last) 474 | { 475 | return shrink_at(first, last - first); 476 | } 477 | 478 | void push_back(const_reference val) 479 | { 480 | auto pos = grow_at(m_end, 1); 481 | atraits::construct(m_alloc, pos, val); 482 | } 483 | 484 | void push_back(T&& val) 485 | { 486 | auto pos = grow_at(m_end, 1); 487 | atraits::construct(m_alloc, pos, std::move(val)); 488 | } 489 | 490 | template 491 | void emplace_back(Args&&... args) 492 | { 493 | auto pos = grow_at(m_end, 1); 494 | atraits::construct(m_alloc, pos, std::forward(args)...); 495 | } 496 | 497 | void pop_back() 498 | { 499 | shrink_at(m_end - 1, 1); 500 | } 501 | 502 | void resize(size_type n, const value_type& v) 503 | { 504 | if (n <= m_capacity) 505 | { 506 | const auto new_end = m_begin + n; 507 | 508 | while (m_end > new_end) 509 | { 510 | atraits::destroy(m_alloc, --m_end); 511 | } 512 | 513 | while (new_end > m_end) 514 | { 515 | atraits::construct(m_alloc, m_end++, v); 516 | } 517 | } 518 | else 519 | { 520 | reserve(n); 521 | 522 | const auto new_end = m_begin + n; 523 | 524 | while (new_end > m_end) 525 | { 526 | atraits::construct(m_alloc, m_end++, v); 527 | } 528 | } 529 | } 530 | 531 | void resize(size_type n) 532 | { 533 | if (n <= m_capacity) 534 | { 535 | const auto new_end = m_begin + n; 536 | 537 | while (m_end > new_end) 538 | { 539 | atraits::destroy(m_alloc, --m_end); 540 | } 541 | 542 | while (new_end > m_end) 543 | { 544 | atraits::construct(m_alloc, m_end++); 545 | } 546 | } 547 | else 548 | { 549 | reserve(n); 550 | 551 | const auto new_end = m_begin + n; 552 | 553 | while (new_end > m_end) 554 | { 555 | atraits::construct(m_alloc, m_end++); 556 | } 557 | } 558 | } 559 | 560 | private: 561 | 562 | size_type find_capacity(size_type desired_capacity) const 563 | { 564 | if (m_capacity == 0) 565 | { 566 | return desired_capacity; 567 | } 568 | else 569 | { 570 | auto new_cap = m_capacity; 571 | 572 | while (new_cap < desired_capacity) 573 | { 574 | // grow by roughly 1.5 575 | new_cap *= 3; 576 | ++new_cap; 577 | new_cap /= 2; 578 | } 579 | 580 | return new_cap; 581 | } 582 | } 583 | 584 | // increase the size by splicing the elements in such a way that 585 | // a hole of uninitialized elements is left at position, with size num 586 | // returns the (potentially new) address of the hole 587 | T* grow_at(const T* cp, size_t num) 588 | { 589 | auto position = const_cast(cp); 590 | 591 | const auto s = size(); 592 | 593 | if (s + num <= m_capacity) 594 | { 595 | m_end = m_begin + s + num; 596 | 597 | for (auto p = m_end - num - 1; p >= position; --p) 598 | { 599 | atraits::construct(m_alloc, p + num, std::move(*p)); 600 | atraits::destroy(m_alloc, p); 601 | } 602 | 603 | return position; 604 | } 605 | else 606 | { 607 | // we need to transfer the elements into the a buffer 608 | size_type new_cap = find_capacity(s + num); 609 | 610 | auto new_buf = atraits::allocate(m_alloc, new_cap); 611 | 612 | position = new_buf + (position - m_begin); 613 | 614 | auto p = m_begin; 615 | auto np = new_buf; 616 | 617 | for (; np != position; ++p, ++np) 618 | { 619 | atraits::construct(m_alloc, np, std::move(*p)); 620 | } 621 | 622 | np += num; 623 | for (; p != m_end; ++p, ++np) 624 | { 625 | atraits::construct(m_alloc, np, std::move(*p)); 626 | } 627 | 628 | // destroy old 629 | if (m_begin) 630 | { 631 | for (p = m_begin; p != m_end; ++p) 632 | { 633 | atraits::destroy(m_alloc, p); 634 | } 635 | 636 | atraits::deallocate(m_alloc, m_begin, m_capacity); 637 | } 638 | 639 | m_capacity = new_cap; 640 | m_begin = new_buf; 641 | m_end = new_buf + s + num; 642 | 643 | return position; 644 | } 645 | } 646 | 647 | T* shrink_at(const T* cp, size_t num) 648 | { 649 | auto position = const_cast(cp); 650 | 651 | const auto s = size(); 652 | if (s - num == 0) 653 | { 654 | clear(); 655 | return m_end; 656 | } 657 | 658 | for (auto p = position, np = position + num; np != m_end; ++p, ++np) 659 | { 660 | atraits::destroy(m_alloc, p); 661 | atraits::construct(m_alloc, p, std::move(*np)); 662 | } 663 | 664 | for (auto p = m_end - num; p != m_end; ++p) 665 | { 666 | atraits::destroy(m_alloc, p); 667 | } 668 | 669 | m_end -= num; 670 | 671 | return ++position; 672 | } 673 | 674 | // grows buffer only on empty vectors 675 | void safe_grow(size_t capacity) 676 | { 677 | if (capacity <= m_capacity) 678 | return; 679 | 680 | if (m_begin) 681 | { 682 | atraits::deallocate(m_alloc, m_begin, m_capacity); 683 | } 684 | 685 | m_capacity = find_capacity(capacity); 686 | m_begin = m_end = atraits::allocate(m_alloc, m_capacity); 687 | } 688 | 689 | void assign_impl(size_type count, const T& value) 690 | { 691 | safe_grow(count); 692 | 693 | for (size_type i = 0; i < count; ++i) 694 | { 695 | atraits::construct(m_alloc, m_end, value); 696 | ++m_end; 697 | } 698 | } 699 | 700 | template 701 | void assign_impl(InputIterator first, InputIterator last) 702 | { 703 | auto isize = last - first; 704 | safe_grow(isize); 705 | 706 | for (auto p = first; p != last; ++p) 707 | { 708 | atraits::construct(m_alloc, m_end, *p); 709 | ++m_end; 710 | } 711 | } 712 | 713 | void assign_impl(std::initializer_list ilist) 714 | { 715 | safe_grow(ilist.size()); 716 | 717 | for (auto& elem : ilist) 718 | { 719 | atraits::construct(m_alloc, m_end, elem); 720 | ++m_end; 721 | } 722 | } 723 | 724 | pointer m_begin; 725 | pointer m_end; 726 | 727 | size_t m_capacity; 728 | 729 | Alloc m_alloc; 730 | }; 731 | 732 | template 733 | bool operator==(const vector& a, const vector& b) 734 | { 735 | if (a.size() != b.size()) 736 | { 737 | return false; 738 | } 739 | 740 | for (size_t i = 0; i < a.size(); ++i) 741 | { 742 | if (a[i] != b[i]) 743 | return false; 744 | } 745 | 746 | return true; 747 | } 748 | 749 | template 750 | bool operator!=(const vector& a, const vector& b) 751 | { 752 | if (a.size() != b.size()) 753 | { 754 | return true; 755 | } 756 | 757 | for (size_t i = 0; i < a.size(); ++i) 758 | { 759 | if (a[i] != b[i]) 760 | return true; 761 | } 762 | 763 | return false; 764 | } 765 | 766 | } 767 | 768 | 769 | #if defined(CHOBO_VECTOR_TEST_WITH_DOCTEST) 770 | 771 | #include 772 | #include 773 | 774 | #if !defined(CHOBO_TEST_COUNTING_ALLOCATOR) 775 | #define CHOBO_TEST_COUNTING_ALLOCATOR 1 776 | 777 | size_t allocations = 0; 778 | size_t deallocations = 0; 779 | size_t allocated_bytes = 0; 780 | size_t deallocated_bytes = 0; 781 | size_t constructions = 0; 782 | size_t destructions = 0; 783 | 784 | template 785 | class counting_allocator : public std::allocator 786 | { 787 | }; 788 | 789 | namespace std 790 | { 791 | 792 | template 793 | class allocator_traits> /* hacky */ : public allocator_traits> 794 | { 795 | public: 796 | typedef std::allocator_traits> super; 797 | typedef counting_allocator Alloc; 798 | 799 | static T* allocate(Alloc& a, size_t n, typename std::allocator_traits::const_pointer hint = 0) 800 | { 801 | ++allocations; 802 | allocated_bytes += n * sizeof(T); 803 | return super::allocate(a, n, hint); 804 | } 805 | 806 | static void deallocate(Alloc& a, T* p, size_t n) 807 | { 808 | ++deallocations; 809 | deallocated_bytes += n * sizeof(T); 810 | return super::deallocate(a, p, n); 811 | } 812 | 813 | template< class U, class... Args > 814 | static void construct(Alloc& a, U* p, Args&&... args) 815 | { 816 | ++constructions; 817 | return super::construct(a, p, std::forward(args)...); 818 | } 819 | 820 | template< class U > 821 | static void destroy(Alloc& a, U* p) 822 | { 823 | ++destructions; 824 | return super::destroy(a, p); 825 | } 826 | 827 | static Alloc select_on_container_copy_construction(const Alloc& a) 828 | { 829 | return a; 830 | } 831 | }; 832 | 833 | } 834 | 835 | #endif 836 | 837 | TEST_CASE("[vector] test") 838 | { 839 | using namespace chobo; 840 | { 841 | vector> ivec; 842 | CHECK(ivec.size() == 0); 843 | CHECK(ivec.capacity() == 0); 844 | CHECK(!ivec.begin()); 845 | CHECK(ivec.begin() == ivec.end()); 846 | CHECK(!ivec.cbegin()); 847 | CHECK(ivec.cbegin() == ivec.cend()); 848 | CHECK(ivec.empty()); 849 | 850 | auto d = ivec.data(); 851 | ivec.reserve(2); 852 | CHECK(ivec.capacity() == 2); 853 | CHECK(d != ivec.data()); 854 | CHECK(allocations == 1); 855 | 856 | ivec.resize(3, 8); 857 | CHECK(ivec.capacity() == 3); 858 | CHECK(ivec.size() == 3); 859 | CHECK(ivec.front() == 8); 860 | CHECK(ivec.back() == 8); 861 | CHECK(d != ivec.data()); 862 | CHECK(allocations == 2); 863 | 864 | ivec.clear(); 865 | CHECK(ivec.size() == 0); 866 | CHECK(ivec.capacity() == 3); 867 | CHECK(d != ivec.data()); 868 | CHECK(ivec.begin() == ivec.end()); 869 | CHECK(ivec.cbegin() == ivec.cend()); 870 | CHECK(ivec.empty()); 871 | 872 | ivec.shrink_to_fit(); 873 | CHECK(ivec.size() == 0); 874 | CHECK(ivec.capacity() == 0); 875 | CHECK(!ivec.begin()); 876 | CHECK(ivec.begin() == ivec.end()); 877 | CHECK(ivec.empty()); 878 | 879 | ivec.push_back(5); 880 | CHECK(ivec.size() == 1); 881 | CHECK(ivec[0] == 5); 882 | auto it = ivec.begin(); 883 | CHECK(it == ivec.data()); 884 | CHECK(it == ivec.cbegin()); 885 | CHECK(*it == 5); 886 | ++it; 887 | CHECK(it == ivec.end()); 888 | CHECK(it == ivec.cend()); 889 | 890 | ivec.emplace_back(3); 891 | CHECK(ivec.size() == 2); 892 | auto rit = ivec.rbegin(); 893 | CHECK(*rit == 3); 894 | ++rit; 895 | *rit = 12; 896 | ++rit; 897 | CHECK(rit == ivec.rend()); 898 | CHECK(rit == ivec.crend()); 899 | CHECK(ivec.front() == 12); 900 | CHECK(ivec.back() == 3); 901 | 902 | ivec.insert(ivec.begin(), 53); 903 | CHECK(ivec.capacity() == 3); 904 | 905 | ivec.insert(ivec.begin() + 2, 90); 906 | ivec.insert(ivec.begin() + 4, 17); 907 | ivec.insert(ivec.end(), 6); 908 | ivec.insert(ivec.begin(), { 1, 2 }); 909 | 910 | int ints[] = { 1, 2, 53, 12, 90, 3, 17, 6 }; 911 | CHECK(ivec.capacity() >= 8); 912 | CHECK(ivec.size() == 8); 913 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 914 | 915 | ivec.pop_back(); 916 | CHECK(ivec.size() == 7); 917 | CHECK(memcmp(ivec.data(), ints, sizeof(ints) - sizeof(int)) == 0); 918 | 919 | CHECK(ivec.capacity() > 7); 920 | ivec.shrink_to_fit(); 921 | CHECK(ivec.capacity() == 7); 922 | 923 | ivec.resize(8); 924 | CHECK(ivec.size() == 8); 925 | ints[7] = 0; 926 | CHECK(memcmp(ivec.data(), ints, sizeof(ints)) == 0); 927 | 928 | const vector> ivec2 = { 1, 2, 3, 4 }; 929 | CHECK(ivec2.size() == 4); 930 | CHECK(*ivec2.begin() == 1); 931 | CHECK(ivec2[1] == 2); 932 | CHECK(ivec2.at(2) == 3); 933 | CHECK(*ivec2.rbegin() == 4); 934 | 935 | ivec.erase(ivec.begin()); 936 | CHECK(ivec.size() == 7); 937 | CHECK(ivec.front() == 2); 938 | CHECK(memcmp(ivec.data(), ints + 1, ivec.size() * sizeof(int)) == 0); 939 | 940 | ivec.erase(ivec.begin() + 2, ivec.begin() + 4); 941 | CHECK(ivec.size() == 5); 942 | CHECK(ivec[3] == 17); 943 | 944 | vector> svec; 945 | svec.assign({ "as", "df" }); 946 | CHECK(svec.size() == 2); 947 | std::string s1 = "the quick brown fox jumped over the lazy dog 1234567890"; 948 | svec.emplace_back(s1); 949 | CHECK(svec.back() == s1); 950 | 951 | auto svec1 = svec; 952 | CHECK(svec1 == svec); 953 | 954 | const void* cstr = svec.back().c_str(); 955 | auto svec2 = std::move(svec); 956 | CHECK(svec2.size() == 3); 957 | CHECK(svec2.back() == s1); 958 | 959 | CHECK(svec.empty()); 960 | CHECK(svec2.back().c_str() == cstr); 961 | 962 | svec = std::move(svec2); 963 | CHECK(svec2.empty()); 964 | CHECK(svec.back().c_str() == cstr); 965 | 966 | svec2 = svec; 967 | CHECK(svec2.back() == s1); 968 | CHECK(svec.back() == s1); 969 | CHECK(svec == svec2); 970 | 971 | svec.insert(svec.begin(), s1); 972 | CHECK(svec.size() == 4); 973 | CHECK(svec.back().c_str() == cstr); 974 | CHECK(svec.front() == svec.back()); 975 | 976 | cstr = s1.c_str(); 977 | svec.emplace(svec.begin() + 2, std::move(s1)); 978 | CHECK(svec.size() == 5); 979 | CHECK(svec.front() == svec[2]); 980 | CHECK(svec[2].c_str() == cstr); 981 | 982 | svec.clear(); 983 | CHECK(svec.empty()); 984 | svec2.clear(); 985 | CHECK(svec2.empty()); 986 | CHECK(svec == svec2); 987 | 988 | svec.resize(svec.capacity()); 989 | CHECK(svec.size() == svec.capacity()); 990 | 991 | for (auto& s : svec) 992 | { 993 | CHECK(s.empty()); 994 | } 995 | 996 | s1 = "asdf"; 997 | vector> cvec(s1.begin(), s1.end()); 998 | CHECK(cvec.size() == 4); 999 | CHECK(cvec.front() == 'a'); 1000 | CHECK(cvec.back() == 'f'); 1001 | 1002 | cvec.clear(); 1003 | CHECK(cvec.size() == 0); 1004 | CHECK(cvec.empty()); 1005 | 1006 | s1 = "baz"; 1007 | cvec.assign(s1.begin(), s1.end()); 1008 | CHECK(cvec.size() == 3); 1009 | CHECK(cvec.front() == 'b'); 1010 | CHECK(cvec.back() == 'z'); 1011 | 1012 | // 0 is implicitly castable to nullptr_t which can be an iterator in our case 1013 | vector nullptr_test(2, 0); 1014 | CHECK(nullptr_test.size() == 2); 1015 | CHECK(nullptr_test.front() == 0); 1016 | CHECK(nullptr_test.back() == 0); 1017 | 1018 | nullptr_test.assign(3, 0); 1019 | CHECK(nullptr_test.size() == 3); 1020 | CHECK(nullptr_test.front() == 0); 1021 | CHECK(nullptr_test.back() == 0); 1022 | 1023 | nullptr_test.insert(nullptr_test.begin(), 1, 0); 1024 | CHECK(nullptr_test.size() == 4); 1025 | CHECK(nullptr_test.front() == 0); 1026 | } 1027 | 1028 | CHECK(allocations == deallocations); 1029 | CHECK(allocated_bytes == deallocated_bytes); 1030 | CHECK(constructions == destructions); 1031 | 1032 | allocations = deallocations = allocated_bytes = deallocated_bytes = constructions = destructions = 0; 1033 | } 1034 | 1035 | #endif 1036 | -------------------------------------------------------------------------------- /include/chobo/vector_ptr.hpp: -------------------------------------------------------------------------------- 1 | // chobo-vector-ptr v1.03 2 | // 3 | // A non-owning pointer to std::vector which can be used in generic code 4 | // 5 | // MIT License: 6 | // Copyright(c) 2016-2018 Chobolabs Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files(the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and / or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions : 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | // 28 | // VERSION HISTORY 29 | // 30 | // 1.03 (2018-11-29) Removed references to deprecated std::allocator members 31 | // 1.02 (2017-06-20) Explicit operator bool 32 | // 1.01 (2017-04-02) Fixed compilation error on assign and insert with count 33 | // and value when count or value is 0 34 | // 1.00 (2016-09-23) First public release 35 | // 36 | // 37 | // DOCUMENTATION 38 | // 39 | // Simply include this file wherever you need. 40 | // This is a non-owning pointer class to a std::vector. You can attach it to a 41 | // given vector and make changes to it or just refer to it. Its purpose is to 42 | // be used in generic code which requires a vector. 43 | // 44 | // It is the responsibility of the user to make sure the pointer is attached 45 | // to a valid vector before calling any vector-specific methods or operators. 46 | // Failing to do so will result in null-pointer-dereference crashes. 47 | // 48 | // The library includes two classes, for pointing to const and non-const 49 | // vectors: vector_ptr, and const_vector_ptr. To automatically generate 50 | // the appropriate pointer, use `make_vector_ptr(your_vector)`. 51 | // 52 | // Example: 53 | // 54 | // vector myvec; 55 | // auto vp = make_vector_ptr(myvec); 56 | // vp.resize(4); // myvec has 4 elements now 57 | // vp[0] = 5; // myvec.front() is now 5 58 | // 59 | // Reference: 60 | // 61 | // vector_ptr has all std::vector methods and operators 62 | // const_vector_ptr has all std::vector const methods and operators 63 | // 64 | // Besides those, both classes define a ptr-like interface: 65 | // 66 | // void reset(vector* vec = nullptr) - reset the internal pointer 67 | // operator bool() const - returns whether the internal pointer is valid 68 | // vector* get() noexcept { return m_vector; } - return the internal pointer 69 | // 70 | // 71 | // TESTS 72 | // 73 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 74 | // To run them, define CHOBO_VECTOR_PTR_TEST_WITH_DOCTEST before including 75 | // the header in a file which has doctest.h already included. 76 | // 77 | #pragma once 78 | 79 | #include 80 | 81 | namespace chobo 82 | { 83 | 84 | template > 85 | class vector_ptr 86 | { 87 | public: 88 | typedef std::vector vector; 89 | 90 | typedef T value_type; 91 | typedef Alloc allocator_type; 92 | typedef typename vector::size_type size_type; 93 | typedef typename vector::difference_type difference_type; 94 | typedef value_type& reference; 95 | typedef const value_type& const_reference; 96 | typedef typename std::allocator_traits::pointer pointer; 97 | typedef typename std::allocator_traits::pointer const_pointer; 98 | typedef typename vector::iterator iterator; 99 | typedef typename vector::const_iterator const_iterator; 100 | typedef typename std::reverse_iterator reverse_iterator; 101 | typedef typename std::reverse_iterator const_reverse_iterator; 102 | 103 | vector_ptr() = default; 104 | 105 | explicit vector_ptr(vector& vec) 106 | : m_vector(&vec) 107 | {} 108 | 109 | vector_ptr(const vector_ptr& other) 110 | : m_vector(other.m_vector) 111 | {} 112 | 113 | vector_ptr(vector_ptr&& other) 114 | : m_vector(other.m_vector) 115 | { 116 | other.m_vector = nullptr; 117 | } 118 | 119 | vector_ptr& operator=(const vector_ptr& other) 120 | { 121 | m_vector = other.m_vector; 122 | return *this; 123 | } 124 | 125 | vector_ptr& operator=(vector_ptr&& other) 126 | { 127 | m_vector = other.m_vector; 128 | other.m_vector = nullptr; 129 | return *this; 130 | } 131 | 132 | void reset(vector* vec = nullptr) 133 | { 134 | m_vector = vec; 135 | } 136 | 137 | explicit operator bool() const 138 | { 139 | return !!m_vector; 140 | } 141 | 142 | vector* get() noexcept { return m_vector; } 143 | const vector* get() const noexcept { return m_vector; } 144 | 145 | iterator begin() noexcept { return m_vector->begin(); } 146 | const_iterator begin() const noexcept { return m_vector->begin(); } 147 | iterator end() noexcept { return m_vector->end(); } 148 | const_iterator end() const noexcept { return m_vector->end(); } 149 | 150 | reverse_iterator rbegin() noexcept { return m_vector->rbegin(); } 151 | const_reverse_iterator rbegin() const noexcept { return m_vector->rbegin(); } 152 | reverse_iterator rend() noexcept { return m_vector->rend(); } 153 | const_reverse_iterator rend() const noexcept { return m_vector->rend(); } 154 | 155 | const_iterator cbegin() const noexcept { return m_vector->cbegin(); } 156 | const_iterator cend() const noexcept { return m_vector->cend(); } 157 | const_reverse_iterator crbegin() const noexcept { return m_vector->crbegin(); } 158 | const_reverse_iterator crend() const noexcept { return m_vector->crend(); } 159 | 160 | size_type size() const noexcept { return m_vector->size(); } 161 | size_type max_size() const noexcept { return m_vector->max_size(); } 162 | void resize(size_type n) { m_vector->resize(n); } 163 | void resize(size_type n, const value_type& val) { m_vector->resize(n, val); } 164 | size_type capacity() const noexcept { return m_vector->capacity(); } 165 | bool empty() const noexcept { return m_vector->empty(); } 166 | void reserve(size_type n) { m_vector->reserve(n); } 167 | void shrink_to_fit() { m_vector->shrink_to_fit(); } 168 | 169 | reference operator[] (size_type n) { return m_vector->operator[](n); } 170 | const_reference operator[] (size_type n) const { return m_vector->operator[](n); } 171 | reference at(size_type n) { return m_vector->at(n); } 172 | const_reference at(size_type n) const { return m_vector->at(n); } 173 | reference front() { return m_vector->front(); } 174 | const_reference front() const { return m_vector->front(); } 175 | reference back() { return m_vector->back(); } 176 | const_reference back() const { return m_vector->back(); } 177 | value_type* data() noexcept { return m_vector->data(); } 178 | const value_type* data() const noexcept { return m_vector->data(); } 179 | 180 | template ())> 181 | void assign(InputIterator first, InputIterator last) { m_vector->assign(first, last); } 182 | void assign(size_type n, const value_type& val) { m_vector->assign(n, val); } 183 | void assign(std::initializer_list il) { m_vector->asign(il); } 184 | 185 | void push_back(const value_type& val) { m_vector->push_back(val); } 186 | void push_back(value_type&& val) { m_vector->push_back(std::forward(val)); } 187 | 188 | void pop_back() { m_vector->pop_back(); } 189 | 190 | iterator insert(const_iterator position, const value_type& val) { return m_vector->insert(position, val); } 191 | iterator insert(const_iterator position, size_type n, const value_type& val) { return m_vector->insert(position, n, val); } 192 | template ())> 193 | iterator insert(const_iterator position, InputIterator first, InputIterator last) { return m_vector->insert(position, first, last); } 194 | iterator insert(const_iterator position, value_type&& val) { return m_vector->insert(position, std::forward(val)); } 195 | iterator insert(const_iterator position, std::initializer_list il) { return m_vector->insert(il); } 196 | 197 | iterator erase(const_iterator position) { return m_vector->erase(position); } 198 | iterator erase(const_iterator first, const_iterator last) { return m_vector->erase(first, last); } 199 | 200 | void swap(vector_ptr& other) { m_vector->swap(other.m_vector); } 201 | void swap(vector& other) { m_vector->swap(other); } 202 | 203 | void clear() noexcept { m_vector->clear(); } 204 | 205 | template 206 | iterator emplace(const_iterator position, Args&&... args) { return m_vector->emplace(position, std::forward(args)...); } 207 | template 208 | void emplace_back(Args&&... args) { m_vector->emplace_back(std::forward(args)...); } 209 | 210 | allocator_type get_allocator() const noexcept { return m_vector->get_allocator(); } 211 | 212 | private: 213 | vector* m_vector = nullptr; 214 | }; 215 | 216 | template 217 | bool operator== (const vector_ptr& lhs, const vector_ptr& rhs) 218 | { 219 | return lhs.m_vector == rhs.m_vector; 220 | } 221 | template 222 | bool operator!= (const vector_ptr& lhs, const vector_ptr& rhs) 223 | { 224 | return lhs.m_vector != rhs.m_vector; 225 | } 226 | template 227 | bool operator< (const vector_ptr& lhs, const vector_ptr& rhs) 228 | { 229 | return lhs.m_vector < rhs.m_vector; 230 | } 231 | template 232 | bool operator<= (const vector_ptr& lhs, const vector_ptr& rhs) 233 | { 234 | return lhs.m_vector <= rhs.m_vector; 235 | } 236 | template 237 | bool operator>(const vector_ptr& lhs, const vector_ptr& rhs) 238 | { 239 | return lhs.m_vector > rhs.m_vector; 240 | } 241 | template 242 | bool operator>= (const vector_ptr& lhs, const vector_ptr& rhs) 243 | { 244 | return lhs.m_vector >= rhs.m_vector; 245 | } 246 | 247 | /////////////////////////////////////////////////////////////////////////////// 248 | 249 | 250 | template > 251 | class const_vector_ptr 252 | { 253 | public: 254 | typedef std::vector vector; 255 | 256 | typedef T value_type; 257 | typedef Alloc allocator_type; 258 | typedef typename vector::size_type size_type; 259 | typedef typename vector::difference_type difference_type; 260 | typedef value_type& reference; 261 | typedef const value_type& const_reference; 262 | typedef typename std::allocator_traits::pointer pointer; 263 | typedef typename std::allocator_traits::pointer const_pointer; 264 | typedef typename vector::iterator iterator; 265 | typedef typename vector::const_iterator const_iterator; 266 | typedef typename std::reverse_iterator reverse_iterator; 267 | typedef typename std::reverse_iterator const_reverse_iterator; 268 | 269 | const_vector_ptr() = default; 270 | 271 | explicit const_vector_ptr(const vector& vec) 272 | : m_vector(&vec) 273 | {} 274 | 275 | const_vector_ptr(const const_vector_ptr& other) 276 | : m_vector(other.m_vector) 277 | {} 278 | 279 | const_vector_ptr(const_vector_ptr&& other) 280 | : m_vector(other.m_vector) 281 | { 282 | other.m_vector = nullptr; 283 | } 284 | 285 | const_vector_ptr& operator=(const const_vector_ptr& other) 286 | { 287 | m_vector = other.m_vector; 288 | return *this; 289 | } 290 | 291 | const_vector_ptr& operator=(const_vector_ptr&& other) 292 | { 293 | m_vector = other.m_vector; 294 | other.m_vector = nullptr; 295 | return *this; 296 | } 297 | 298 | void reset(const vector* vec = nullptr) 299 | { 300 | m_vector = vec; 301 | } 302 | 303 | explicit operator bool() const 304 | { 305 | return !!m_vector; 306 | } 307 | 308 | const vector* get() const noexcept { return m_vector; } 309 | 310 | const_iterator begin() const noexcept { return m_vector->begin(); } 311 | const_iterator end() const noexcept { return m_vector->end(); } 312 | 313 | const_reverse_iterator rbegin() const noexcept { return m_vector->rbegin(); } 314 | const_reverse_iterator rend() const noexcept { return m_vector->rend(); } 315 | 316 | const_iterator cbegin() const noexcept { return m_vector->cbegin(); } 317 | const_iterator cend() const noexcept { return m_vector->cend(); } 318 | const_reverse_iterator crbegin() const noexcept { return m_vector->crbegin(); } 319 | const_reverse_iterator crend() const noexcept { return m_vector->crend(); } 320 | 321 | size_type size() const noexcept { return m_vector->size(); } 322 | size_type max_size() const noexcept { return m_vector->max_size(); } 323 | size_type capacity() const noexcept { return m_vector->capacity(); } 324 | bool empty() const noexcept { return m_vector->empty(); } 325 | 326 | const_reference operator[] (size_type n) const { return m_vector->operator[](n); } 327 | const_reference at(size_type n) const { return m_vector->at(n); } 328 | const_reference front() const { return m_vector->front(); } 329 | const_reference back() const { return m_vector->back(); } 330 | const value_type* data() const noexcept { return m_vector->data(); } 331 | 332 | allocator_type get_allocator() const noexcept { return m_vector->get_allocator(); } 333 | 334 | private: 335 | const vector* m_vector = nullptr; 336 | }; 337 | 338 | template 339 | bool operator== (const const_vector_ptr& lhs, const const_vector_ptr& rhs) 340 | { 341 | return lhs.m_vector == rhs.m_vector; 342 | } 343 | template 344 | bool operator!= (const const_vector_ptr& lhs, const const_vector_ptr& rhs) 345 | { 346 | return lhs.m_vector != rhs.m_vector; 347 | } 348 | template 349 | bool operator< (const const_vector_ptr& lhs, const const_vector_ptr& rhs) 350 | { 351 | return lhs.m_vector < rhs.m_vector; 352 | } 353 | template 354 | bool operator<= (const const_vector_ptr& lhs, const const_vector_ptr& rhs) 355 | { 356 | return lhs.m_vector <= rhs.m_vector; 357 | } 358 | template 359 | bool operator>(const const_vector_ptr& lhs, const const_vector_ptr& rhs) 360 | { 361 | return lhs.m_vector > rhs.m_vector; 362 | } 363 | template 364 | bool operator>= (const const_vector_ptr& lhs, const const_vector_ptr& rhs) 365 | { 366 | return lhs.m_vector >= rhs.m_vector; 367 | } 368 | 369 | /////////////////////////////////////////////////////////////////////////////// 370 | 371 | template 372 | vector_ptr make_vector_ptr(std::vector& vec) 373 | { 374 | return vector_ptr(vec); 375 | } 376 | 377 | template 378 | const_vector_ptr make_vector_ptr(const std::vector& vec) 379 | { 380 | return const_vector_ptr(vec); 381 | } 382 | 383 | } 384 | 385 | #if defined(CHOBO_VECTOR_PTR_TEST_WITH_DOCTEST) 386 | 387 | #include 388 | 389 | TEST_CASE("[vector_ptr] test") 390 | { 391 | using namespace chobo; 392 | 393 | std::vector vec; 394 | 395 | vector_ptr p1; 396 | CHECK(!p1); 397 | 398 | auto p2 = make_vector_ptr(vec); 399 | static_assert(std::is_same, decltype(p2)>::value, "p2 must be vector_ptr"); 400 | CHECK(p2); 401 | CHECK(p2.size() == 0); 402 | 403 | p1.reset(&vec); 404 | CHECK(p1); 405 | 406 | p1.resize(3); 407 | CHECK(vec.size() == 3); 408 | CHECK(p2.size() == 3); 409 | 410 | for (auto i : p2) 411 | { 412 | CHECK(i == 0); 413 | } 414 | 415 | p2.reset(); 416 | CHECK(!p2); 417 | 418 | p2 = p1; 419 | CHECK(p2); 420 | 421 | p2[1] = 5; 422 | CHECK(p1.at(1) == 5); 423 | 424 | auto p3 = p1; 425 | CHECK(p3); 426 | CHECK(p3.size() == 3); 427 | CHECK(*(p3.begin() + 1) == 5); 428 | 429 | auto p4 = std::move(p1); 430 | CHECK(!p1); 431 | CHECK(p4); 432 | CHECK(p4.size() == 3); 433 | CHECK(*(p4.rbegin() + 1) == 5); 434 | 435 | CHECK(p2); 436 | p2 = p1; 437 | CHECK(!p2); 438 | 439 | p2 = std::move(p4); 440 | CHECK(!p4); 441 | CHECK(p2); 442 | CHECK(p2.size() == 3); 443 | 444 | p2.assign(3, 0); 445 | CHECK(vec.size() == 3); 446 | CHECK(vec.front() == 0); 447 | CHECK(vec.back() == 0); 448 | 449 | p2.insert(p2.begin(), 1, 0); 450 | CHECK(vec.size() == 4); 451 | CHECK(vec.front() == 0); 452 | } 453 | 454 | TEST_CASE("[const_vector_ptr] test") 455 | { 456 | using namespace chobo; 457 | 458 | std::vector ncvec; 459 | ncvec.resize(3); 460 | 461 | const auto& vec = ncvec; 462 | 463 | const_vector_ptr p1; 464 | CHECK(!p1); 465 | 466 | auto p2 = make_vector_ptr(vec); 467 | static_assert(std::is_same, decltype(p2)>::value, "p2 must be const_vector_ptr"); 468 | CHECK(p2); 469 | CHECK(p2.size() == 3); 470 | 471 | p1.reset(&vec); 472 | CHECK(p1); 473 | 474 | CHECK(p1.size() == 3); 475 | 476 | for (auto i : p2) 477 | { 478 | CHECK(i == 0); 479 | } 480 | 481 | p2.reset(); 482 | CHECK(!p2); 483 | 484 | p2 = p1; 485 | CHECK(p2); 486 | 487 | ncvec[1] = 5; 488 | CHECK(p1.at(1) == 5); 489 | 490 | auto p3 = p1; 491 | CHECK(p3); 492 | CHECK(p3.size() == 3); 493 | CHECK(*(p3.begin() + 1) == 5); 494 | 495 | auto p4 = std::move(p1); 496 | CHECK(!p1); 497 | CHECK(p4); 498 | CHECK(p4.size() == 3); 499 | CHECK(*(p4.rbegin() + 1) == 5); 500 | 501 | CHECK(p2); 502 | p2 = p1; 503 | CHECK(!p2); 504 | 505 | p2 = std::move(p4); 506 | CHECK(!p4); 507 | CHECK(p2); 508 | CHECK(p2.size() == 3); 509 | } 510 | 511 | #endif 512 | -------------------------------------------------------------------------------- /include/chobo/vector_view.hpp: -------------------------------------------------------------------------------- 1 | // chobo-vector-view v1.01 2 | // 3 | // A view of a std::vector which makes it look as a vector of another type 4 | // 5 | // MIT License: 6 | // Copyright(c) 2016 Chobolabs Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files(the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and / or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions : 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | // 28 | // VERSION HISTORY 29 | // 30 | // 1.01 (2016-09-27) Added checks for unsupported resizes when 31 | // sizeof(view type) is less than half of sizeof(vec type) 32 | // 1.00 (2016-09-23) First public release 33 | // 34 | // 35 | // DOCUMENTATION 36 | // 37 | // Simply include this file wherever you need. 38 | // A vector view is a class which attaches to an existing std::vector 39 | // and provides a view to its data as an alternative type, along 40 | // with a working std::vector-like interface. 41 | // 42 | // THIS IS DANGEROUS, so you must know the risks ot doing so before 43 | // using this library. 44 | // 45 | // The library includes two classes, for viewing const and non-const 46 | // vectors: vector_view, and const_vector_view. To automatically generate 47 | // the appropriate pointer, use `make_vector_view(your_vector)`. 48 | // 49 | // Example: 50 | // 51 | // vector geometry; 52 | // ... // fill geometry with data 53 | // auto float_view = make_vector_view(geometry); 54 | // float_view[5] = 8; // equivalent to geometry[2].y = 8; 55 | // 56 | // auto view4d = make_vector_view(geometry); 57 | // 58 | // // add two elements to original array {1,2} and {3,4} 59 | // view4d.push_back(make_vector4D(1, 2, 3, 4)); 60 | // 61 | // Reference: 62 | // 63 | // vector_view has the most common std::vector methods and operators 64 | // const_vector_view has the most common std::vector const methods and operators 65 | // 66 | // besides that both have a method vector& vec() for getting the underlying 67 | // std::vector 68 | // 69 | // 70 | // Configuration 71 | // 72 | // chobo::vector_view has two configurable settings: 73 | // 74 | // Config bounds checks: 75 | // 76 | // By default bounds checks are made in debug mode (via an assert) when accessing 77 | // elements (with `at` or `[]`). Iterators are not checked (yet...) 78 | // 79 | // To disable them, you can define CHOBO_VECTOR_VIEW_NO_DEBUG_BOUNDS_CHECK 80 | // before including the header. 81 | // 82 | // Config POD check: 83 | // 84 | // By default the library checks that both the source and the target type of 85 | // the view are PODs. This is a good idea but sometimes a type is almost a POD 86 | // (say constructors to geometry vectors) and is still fit for a view 87 | // 88 | // To disable the POD checks, define CHOBO_VECTOR_VIEW_NO_POD_CHECK 89 | // before including the header. 90 | // 91 | // Using vector_view with non-POD types 92 | // 93 | // If you end up using the class with a non-pod type, you should take into 94 | // account that when changing the size of the vector via its view ONLY the 95 | // constructors and destructors of the source type (the one in the std::vector) 96 | // will be called. The target type's constructors and destructors will NEVER be 97 | // called. All assignments happen with its assignment operator (oprator=). 98 | // 99 | // 100 | // TESTS 101 | // 102 | // The tests are included in the header file and use doctest (https://github.com/onqtam/doctest). 103 | // To run them, define CHOBO_VECTOR_VIEW_TEST_WITH_DOCTEST before including 104 | // the header in a file which has doctest.h already included. 105 | // 106 | #pragma once 107 | 108 | #include 109 | 110 | #if defined(CHOBO_VECTOR_VIEW_NO_DEBUG_BOUNDS_CHECK) 111 | # define _CHOBO_VECTOR_VIEW_BOUNDS_CHECK(i) 112 | #else 113 | # include 114 | # define _CHOBO_VECTOR_VIEW_BOUNDS_CHECK(i) assert((i) < this->size()) 115 | #endif 116 | 117 | #if defined(CHOBO_VECTOR_VIEW_NO_POD_CHECK) 118 | #define _CHOBO_VECTOR_VIEW_POD_CHECK(T) 119 | #else 120 | #include 121 | #define _CHOBO_VECTOR_VIEW_POD_CHECK(T) static_assert(std::is_pod::value, #T " must be a pod"); 122 | #endif 123 | 124 | namespace chobo 125 | { 126 | 127 | template > 128 | class vector_view 129 | { 130 | _CHOBO_VECTOR_VIEW_POD_CHECK(T) 131 | _CHOBO_VECTOR_VIEW_POD_CHECK(U) 132 | public: 133 | typedef std::vector vector; 134 | 135 | typedef U value_type; 136 | typedef T vec_value_type; 137 | typedef Alloc allocator_type; 138 | typedef typename vector::size_type size_type; 139 | typedef typename vector::difference_type difference_type; 140 | typedef U& reference; 141 | typedef const U& const_reference; 142 | typedef U* pointer; 143 | typedef const U* const_pointer; 144 | typedef pointer iterator; 145 | typedef const_pointer const_iterator; 146 | typedef std::reverse_iterator reverse_iterator; 147 | typedef std::reverse_iterator const_reverse_iterator; 148 | 149 | explicit vector_view(vector& vec) 150 | : m_vector(vec) 151 | {} 152 | 153 | vector_view(const vector_view& other) = delete; 154 | vector_view& operator=(const vector_view& other) = delete; 155 | 156 | vector_view(vector_view&& other) 157 | : m_vector(other.m_vector) 158 | {} // intentionally don't inavlidate the other view 159 | 160 | vector_view& operator=(vector_view&& other) 161 | { 162 | m_vector = std::move(other.m_vector); 163 | } 164 | 165 | vector& vec() 166 | { 167 | return m_vector; 168 | } 169 | 170 | const vector& vec() const 171 | { 172 | return m_vector; 173 | } 174 | 175 | template 176 | vector_view& operator=(const std::vector& other) 177 | { 178 | size_type n = other.size(); 179 | resize(n); 180 | for (size_type i = 0; i < n; ++i) 181 | { 182 | this->at(i) = other[i]; 183 | } 184 | } 185 | 186 | iterator begin() noexcept 187 | { 188 | return reinterpret_cast(m_vector.data()); 189 | } 190 | 191 | const_iterator begin() const noexcept 192 | { 193 | return reinterpret_cast(m_vector.data()); 194 | } 195 | 196 | const_iterator cbegin() const noexcept 197 | { 198 | return begin(); 199 | } 200 | 201 | iterator end() noexcept 202 | { 203 | return begin() + size(); 204 | } 205 | 206 | const_iterator end() const noexcept 207 | { 208 | return begin() + size(); 209 | } 210 | 211 | const_iterator cend() const noexcept 212 | { 213 | return begin() + size(); 214 | } 215 | 216 | reverse_iterator rbegin() noexcept 217 | { 218 | return reverse_iterator(end()); 219 | } 220 | 221 | const_reverse_iterator rbegin() const noexcept 222 | { 223 | return const_reverse_iterator(end()); 224 | } 225 | 226 | const_reverse_iterator crbegin() const noexcept 227 | { 228 | return const_reverse_iterator(end()); 229 | } 230 | 231 | reverse_iterator rend() noexcept 232 | { 233 | return reverse_iterator(begin()); 234 | } 235 | 236 | const_reverse_iterator rend() const noexcept 237 | { 238 | return const_reverse_iterator(begin()); 239 | } 240 | 241 | const_reverse_iterator crend() const noexcept 242 | { 243 | return const_reverse_iterator(begin()); 244 | } 245 | 246 | size_type size() const noexcept 247 | { 248 | return (m_vector.size() * sizeof(vec_value_type)) / sizeof(value_type); 249 | } 250 | 251 | size_type capacity() const noexcept 252 | { 253 | return (m_vector.capacity() * sizeof(vec_value_type)) / sizeof(value_type); 254 | } 255 | 256 | size_type max_size() const noexcept 257 | { 258 | return (m_vector.max_size() * sizeof(vec_value_type)) / sizeof(value_type); 259 | } 260 | 261 | void resize(size_type n) 262 | { 263 | size_type s = (n * sizeof(value_type) + sizeof(vec_value_type) - 1) / sizeof(vec_value_type); 264 | m_vector.resize(s); 265 | 266 | #if !defined CHOBO_VECTOR_VIEW_NO_RESIZE_CHECK 267 | // Is this assert fails, here's what has happened: 268 | // The size of the type of the view is smaller than the half of the size of the vector. 269 | // Since the vector's number of elements must be a whole number, we cannot resize it to 270 | // hold cerrtain numbers of the type of the view (ie not multiples of the sizeof(vec_type)/sizeof(view_type)) 271 | // 272 | // In theory this could be supported if we add a custom size variable for the view class 273 | // However, besides the increase in complexity, this will cause us to lose a cruicial 274 | // feature - having changes to the vector from outside be automatically reflected on the view 275 | // because it's only a view, and has no state of its own. 276 | // Adding such size will be a state for this class. 277 | // Perhaps if there is need such a feature could be implemented but in a class 278 | // with another name, where it's clear that persisting it would be hurtful. 279 | // 280 | // So to avoid potential bugs this assertion, as well as the static assertions in 281 | // push_back and pop_back have been added. 282 | assert(size() == n && "unsupported resize"); 283 | #endif 284 | } 285 | 286 | void resize(size_type n, const value_type& val) 287 | { 288 | size_type prev_size = size(); 289 | resize(n); 290 | for (iterator i = begin() + prev_size; i != end(); ++i) 291 | { 292 | *i = val; 293 | } 294 | } 295 | 296 | bool empty() const noexcept 297 | { 298 | return m_vector.size() * sizeof(vec_value_type) < sizeof(value_type); 299 | } 300 | 301 | void reserve(size_type n) 302 | { 303 | n = (n * sizeof(value_type) + sizeof(vec_value_type) - 1) / sizeof(vec_value_type); 304 | m_vector.reserve(n); 305 | } 306 | 307 | void shrink_to_fit() 308 | { 309 | m_vector.shrink_to_fit(); 310 | } 311 | 312 | const_reference at(size_type i) const 313 | { 314 | _CHOBO_VECTOR_VIEW_BOUNDS_CHECK(i); 315 | return *(begin() + i); 316 | } 317 | 318 | reference at(size_type i) 319 | { 320 | _CHOBO_VECTOR_VIEW_BOUNDS_CHECK(i); 321 | return *(begin() + i); 322 | } 323 | 324 | const_reference operator[](size_type i) const 325 | { 326 | return at(i); 327 | } 328 | 329 | reference operator[](size_type i) 330 | { 331 | return at(i); 332 | } 333 | 334 | const_reference front() const 335 | { 336 | return at(0); 337 | } 338 | 339 | reference front() 340 | { 341 | return at(0); 342 | } 343 | 344 | const_reference back() const 345 | { 346 | return *(end() - 1); 347 | } 348 | 349 | reference back() 350 | { 351 | return *(end() - 1); 352 | } 353 | 354 | const_pointer data() const noexcept 355 | { 356 | return begin(); 357 | } 358 | 359 | pointer data() noexcept 360 | { 361 | return begin(); 362 | } 363 | 364 | void push_back(const value_type& val) 365 | { 366 | // see comment in resize for an explanation 367 | static_assert(sizeof(value_type) > sizeof(vec_value_type) / 2, 368 | "vector_view::push_back is not supported for value_type with size smaller than half of the viewed type"); 369 | resize(size() + 1, val); 370 | } 371 | 372 | void push_back(value_type&& val) 373 | { 374 | // see comment in resize for an explanation 375 | static_assert(sizeof(value_type) > sizeof(vec_value_type) / 2, 376 | "vector_view::push_back is not supported for value_type with size smaller than half of the viewed type"); 377 | resize(size() + 1); 378 | back() = std::move(val); 379 | } 380 | 381 | void pop_back() 382 | { 383 | // see comment in resize for an explanation 384 | static_assert(sizeof(value_type) > sizeof(vec_value_type) / 2, 385 | "vector_view::pop_back is not supported for value_type with size smaller than half of the viewed type"); 386 | resize(size() - 1); 387 | } 388 | 389 | void clear() noexcept 390 | { 391 | m_vector.clear(); 392 | } 393 | 394 | private: 395 | vector& m_vector; 396 | }; 397 | 398 | /////////////////////////////////////////////////////////////////////////////// 399 | 400 | template > 401 | class const_vector_view 402 | { 403 | _CHOBO_VECTOR_VIEW_POD_CHECK(T) 404 | _CHOBO_VECTOR_VIEW_POD_CHECK(U) 405 | public: 406 | typedef std::vector vector; 407 | 408 | typedef U value_type; 409 | typedef T vec_value_type; 410 | typedef Alloc allocator_type; 411 | typedef typename vector::size_type size_type; 412 | typedef typename vector::difference_type difference_type; 413 | typedef U& reference; 414 | typedef const U& const_reference; 415 | typedef U* pointer; 416 | typedef const U* const_pointer; 417 | typedef pointer iterator; 418 | typedef const_pointer const_iterator; 419 | typedef std::reverse_iterator reverse_iterator; 420 | typedef std::reverse_iterator const_reverse_iterator; 421 | 422 | explicit const_vector_view(const vector& vec) 423 | : m_vector(vec) 424 | {} 425 | 426 | const_vector_view(const const_vector_view& other) = delete; 427 | const_vector_view& operator=(const const_vector_view& other) = delete; 428 | 429 | const_vector_view(const_vector_view&& other) 430 | : m_vector(other.m_vector) 431 | {} // intentionally don't inavlidate the other view 432 | 433 | const_vector_view& operator=(const_vector_view&& other) = delete; 434 | 435 | const vector& vec() const 436 | { 437 | return m_vector; 438 | } 439 | 440 | const_iterator begin() const noexcept 441 | { 442 | return reinterpret_cast(m_vector.data()); 443 | } 444 | 445 | const_iterator cbegin() const noexcept 446 | { 447 | return begin(); 448 | } 449 | 450 | const_iterator end() const noexcept 451 | { 452 | return begin() + size(); 453 | } 454 | 455 | const_iterator cend() const noexcept 456 | { 457 | return begin() + size(); 458 | } 459 | 460 | const_reverse_iterator rbegin() const noexcept 461 | { 462 | return const_reverse_iterator(end()); 463 | } 464 | 465 | const_reverse_iterator crbegin() const noexcept 466 | { 467 | return const_reverse_iterator(end()); 468 | } 469 | 470 | const_reverse_iterator rend() const noexcept 471 | { 472 | return const_reverse_iterator(begin()); 473 | } 474 | 475 | const_reverse_iterator crend() const noexcept 476 | { 477 | return const_reverse_iterator(begin()); 478 | } 479 | 480 | size_type size() const noexcept 481 | { 482 | return (m_vector.size() * sizeof(vec_value_type)) / sizeof(value_type); 483 | } 484 | 485 | size_type capacity() const noexcept 486 | { 487 | return (m_vector.capacity() * sizeof(vec_value_type)) / sizeof(value_type); 488 | } 489 | 490 | size_type max_size() const noexcept 491 | { 492 | return (m_vector.max_size() * sizeof(vec_value_type)) / sizeof(value_type); 493 | } 494 | 495 | bool empty() const noexcept 496 | { 497 | return m_vector.size() * sizeof(vec_value_type) < sizeof(value_type); 498 | } 499 | 500 | const_reference at(size_type i) const 501 | { 502 | _CHOBO_VECTOR_VIEW_BOUNDS_CHECK(i); 503 | return *(begin() + i); 504 | } 505 | 506 | const_reference operator[](size_type i) const 507 | { 508 | return at(i); 509 | } 510 | 511 | const_reference front() const 512 | { 513 | return at(0); 514 | } 515 | 516 | const_reference back() const 517 | { 518 | return *(end() - 1); 519 | } 520 | 521 | const_pointer data() const noexcept 522 | { 523 | return begin(); 524 | } 525 | 526 | private: 527 | const vector& m_vector; 528 | }; 529 | 530 | /////////////////////////////////////////////////////////////////////////////// 531 | 532 | template 533 | vector_view make_vector_view(std::vector& vec) 534 | { 535 | return vector_view(vec); 536 | } 537 | 538 | template 539 | const_vector_view make_vector_view(const std::vector& vec) 540 | { 541 | return const_vector_view(vec); 542 | } 543 | 544 | } 545 | 546 | 547 | #if defined(CHOBO_VECTOR_VIEW_TEST_WITH_DOCTEST) 548 | 549 | namespace chobo_vector_view_test 550 | { 551 | 552 | struct vector3D 553 | { 554 | int x, y, z; 555 | }; 556 | 557 | struct vector4D 558 | { 559 | int x, y, z, w; 560 | }; 561 | 562 | struct chobo_vector3D 563 | { 564 | int a, b, c; 565 | }; 566 | 567 | } 568 | 569 | TEST_CASE("[vector_view] test") 570 | { 571 | using namespace chobo; 572 | using namespace chobo_vector_view_test; 573 | 574 | std::vector vec; 575 | auto v3dview = make_vector_view(vec); 576 | 577 | CHECK(v3dview.empty()); 578 | CHECK(v3dview.size() == 0); 579 | vec.push_back(5); 580 | CHECK(v3dview.empty()); 581 | CHECK(v3dview.size() == 0); 582 | vec.push_back(10); 583 | CHECK(v3dview.empty()); 584 | CHECK(v3dview.size() == 0); 585 | vec.push_back(15); 586 | CHECK(!v3dview.empty()); 587 | CHECK(v3dview.size() == 1); 588 | 589 | CHECK(v3dview.front().x == 5); 590 | CHECK(v3dview.front().y == 10); 591 | CHECK(v3dview.front().z == 15); 592 | 593 | CHECK(v3dview.back().x == 5); 594 | CHECK(v3dview.back().y == 10); 595 | CHECK(v3dview.back().z == 15); 596 | 597 | v3dview.resize(2); 598 | CHECK(vec.size() == 6); 599 | v3dview[1].x = 2; 600 | v3dview.at(1).y = 4; 601 | (v3dview.begin() + 1)->z = 6; 602 | CHECK(vec[3] == 2); 603 | CHECK(vec[4] == 4); 604 | CHECK(vec[5] == 6); 605 | CHECK((v3dview.rend() - 2)->z == 6); 606 | 607 | vector3D foo = { 1,3,5 }; 608 | v3dview.resize(4, foo); 609 | CHECK(vec.size() == 12); 610 | 611 | CHECK(vec.back() == 5); 612 | CHECK(vec[6] == 1); 613 | CHECK(v3dview.rbegin()->y == 3); 614 | 615 | v3dview.resize(3); 616 | CHECK(vec.size() == 9); 617 | CHECK(v3dview.crbegin()->z == 5); 618 | 619 | v3dview.push_back(foo); 620 | CHECK(vec.size() == 12); 621 | CHECK(vec[9] == 1); 622 | CHECK(vec[10] == 3); 623 | CHECK(vec[11] == 5); 624 | 625 | v3dview.pop_back(); 626 | CHECK(vec.size() == 9); 627 | 628 | v3dview.clear(); 629 | CHECK(vec.empty()); 630 | 631 | vec.resize(5); 632 | CHECK(v3dview.size() == 1); 633 | 634 | v3dview.resize(2); 635 | CHECK(vec.size() == 6); 636 | 637 | void* data = vec.data(); 638 | CHECK(v3dview.data() == data); 639 | 640 | // same 641 | std::vector vec3d; 642 | auto v3dview_chobo = make_vector_view(vec3d); 643 | vec3d.resize(5); 644 | CHECK(v3dview_chobo.size() == 5); 645 | 646 | vec3d[2].a = 7; 647 | vec3d[2].b = 8; 648 | vec3d[2].c = 9; 649 | CHECK(v3dview_chobo[2].x == 7); 650 | CHECK(v3dview_chobo.at(2).y == 8); 651 | CHECK((v3dview_chobo.begin() + 2)->z == 9); 652 | 653 | // unequal 654 | std::vector vec4d; 655 | auto v3dview4d = make_vector_view(vec4d); 656 | 657 | vec4d.resize(1); 658 | CHECK(v3dview4d.size() == 1); 659 | 660 | v3dview4d.resize(2); 661 | CHECK(v3dview4d.size() == 2); 662 | CHECK(vec4d.size() == 2); 663 | 664 | // smaller 665 | auto iview = make_vector_view(vec4d); 666 | CHECK(iview.size() == 8); 667 | iview.resize(12); 668 | CHECK(vec4d.size() == 3); 669 | } 670 | 671 | TEST_CASE("[const_vector_view] test") 672 | { 673 | using namespace chobo; 674 | using namespace chobo_vector_view_test; 675 | 676 | std::vector vvec; 677 | const auto& vec = vvec; 678 | auto v3dview = make_vector_view(vec); 679 | 680 | CHECK(v3dview.empty()); 681 | CHECK(v3dview.size() == 0); 682 | vvec.push_back(5); 683 | CHECK(v3dview.empty()); 684 | CHECK(v3dview.size() == 0); 685 | vvec.push_back(10); 686 | CHECK(v3dview.empty()); 687 | CHECK(v3dview.size() == 0); 688 | vvec.push_back(15); 689 | CHECK(!v3dview.empty()); 690 | CHECK(v3dview.size() == 1); 691 | 692 | CHECK(v3dview.front().x == 5); 693 | CHECK(v3dview.front().y == 10); 694 | CHECK(v3dview.front().z == 15); 695 | 696 | CHECK(v3dview.back().x == 5); 697 | CHECK(v3dview.back().y == 10); 698 | CHECK(v3dview.back().z == 15); 699 | 700 | vvec.resize(6); 701 | CHECK(v3dview.size() == 2); 702 | vvec[3] = 2; 703 | vvec[4] = 4; 704 | vvec[5] = 6; 705 | CHECK(v3dview[1].x == 2); 706 | CHECK(v3dview.at(1).y == 4); 707 | CHECK((v3dview.begin() + 1)->z == 6); 708 | CHECK((v3dview.rend() - 2)->z == 6); 709 | 710 | vvec.resize(12); 711 | CHECK(v3dview.size() == 4); 712 | 713 | vvec[10] = 3; 714 | CHECK(v3dview.rbegin()->y == 3); 715 | 716 | vvec.resize(9); 717 | CHECK(v3dview.size() == 3); 718 | 719 | vvec.clear(); 720 | CHECK(v3dview.empty()); 721 | 722 | vvec.resize(5); 723 | CHECK(v3dview.size() == 1); 724 | 725 | const void* data = vec.data(); 726 | CHECK(v3dview.data() == data); 727 | 728 | // same 729 | std::vector vvec3d; 730 | const auto& vec3d = vvec3d; 731 | auto v3dview_chobo = make_vector_view(vec3d); 732 | vvec3d.resize(5); 733 | CHECK(v3dview_chobo.size() == 5); 734 | 735 | vvec3d[2].a = 7; 736 | vvec3d[2].b = 8; 737 | vvec3d[2].c = 9; 738 | CHECK(v3dview_chobo[2].x == 7); 739 | CHECK(v3dview_chobo.at(2).y == 8); 740 | CHECK((v3dview_chobo.begin() + 2)->z == 9); 741 | 742 | // unequal 743 | std::vector vvec4d; 744 | const auto& vec4d = vvec4d; 745 | auto v3dview4d = make_vector_view(vec4d); 746 | 747 | vvec4d.resize(1); 748 | CHECK(v3dview4d.size() == 1); 749 | 750 | vvec4d.resize(2); 751 | CHECK(v3dview4d.size() == 2); 752 | 753 | vvec4d.resize(3); 754 | CHECK(v3dview4d.size() == 4); 755 | 756 | // smaller 757 | auto iview = make_vector_view(vec4d); 758 | CHECK(iview.size() == 12); 759 | } 760 | 761 | #endif -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | gen/ 2 | build/ 3 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(chobo-shl-test) 4 | 5 | enable_testing() 6 | 7 | set(INC ${CMAKE_CURRENT_SOURCE_DIR}/../include) 8 | include_directories(${INC}) 9 | 10 | file(GLOB_RECURSE choboshl "${INC}/chobo/*.hpp") 11 | source_group("chobo" FILES ${choboshl}) 12 | file(GLOB doctest ${CMAKE_CURRENT_SOURCE_DIR}/doctest/*.h) 13 | source_group("third_party" FILES ${doctest}) 14 | 15 | if(NOT MSVC) 16 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 17 | endif() 18 | 19 | add_executable(chobo-shl-test 20 | ${choboshl} 21 | ${doctest} 22 | test.cpp 23 | ) 24 | 25 | add_test(chobo-shl-test chobo-shl-test) 26 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## Chobo SHL unit test executable 2 | 3 | * Using a copy of [doctest](https://github.com/onqtam/doctest) -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "doctest/doctest.h" 3 | 4 | #define CHOBO_OPTIONAL_TEST_WITH_DOCTEST 5 | #include "chobo/optional.hpp" 6 | 7 | #define CHOBO_STATIC_VECTOR_TEST_WITH_DOCTEST 8 | #include "chobo/static_vector.hpp" 9 | 10 | #define CHOBO_VECTOR_PTR_TEST_WITH_DOCTEST 11 | #include "chobo/vector_ptr.hpp" 12 | 13 | #define CHOBO_FLAT_MAP_TEST_WITH_DOCTEST 14 | #define CHOBO_FLAT_MAP_TEST_STATIC_VECTOR_WITH_DOCTEST 15 | #define CHOBO_FLAT_MAP_TEST_VECTOR_PTR_WITH_DOCTEST 16 | #include "chobo/flat_map.hpp" 17 | 18 | #define CHOBO_VECTOR_VIEW_TEST_WITH_DOCTEST 19 | #include "chobo/vector_view.hpp" 20 | 21 | #define CHOBO_SMALL_VECTOR_TEST_WITH_DOCTEST 22 | #include "chobo/small_vector.hpp" 23 | 24 | #define CHOBO_MEMORY_VIEW_TEST_WITH_DOCTEST 25 | #include "chobo/memory_view.hpp" 26 | 27 | #define CHOBO_VECTOR_TEST_WITH_DOCTEST 28 | #include "chobo/vector.hpp" 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tools/vs/chobo_static_vector.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{size = {m_size}}} 5 | 6 | "$T1" 7 | 8 | m_size 9 | ($T1*)(this) 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------