├── .gitignore ├── .travis.yml ├── Jamroot.jam ├── README.md ├── appveyor.yml ├── benchmark ├── benchmark_util.hpp ├── push_back.cpp └── run.sh ├── doc ├── 01-introduction.qbk ├── 02-design_rationale.qbk ├── 03-examples.qbk ├── 04-benchmarks.qbk ├── 05-build_and_test.qbk ├── 06-acknowledgements.qbk ├── Jamfile.v2 ├── index.idx └── index.qbk ├── example ├── doc_batch_deque.cpp ├── doc_devector.cpp └── doc_serialize.cpp ├── include └── boost │ └── double_ended │ ├── batch_deque.hpp │ ├── detail │ ├── algorithm.hpp │ ├── allocator.hpp │ ├── documentation.hpp │ ├── guards.hpp │ └── iterators.hpp │ ├── devector.hpp │ ├── serialize_batch_deque.hpp │ └── serialize_devector.hpp └── test ├── Jamfile.v2 ├── batch_deque_test.cpp ├── devector_test.cpp ├── explicit_inst_batch_deque_test.cpp ├── explicit_inst_devector_test.cpp ├── input_iterator.hpp ├── serialize_batch_deque_test.cpp ├── serialize_devector_test.cpp ├── test_elem.hpp └── test_util.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | doc/apidoc.xml 3 | doc/html/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright Benedek Thaler 2015-2016 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 4 | 5 | language: cpp 6 | sudo: false 7 | 8 | branches: 9 | only: 10 | - master 11 | 12 | env: 13 | matrix: 14 | # Workaround for: https://github.com/travis-ci/travis-ci/issues/4681 15 | - TRAVIS_EXTRA_JOB_WORKAROUND=true 16 | 17 | matrix: 18 | include: 19 | 20 | # GCC 4.8 21 | - env: CXX=g++-4.8 CXX_PATH=/usr/bin/g++-4.8 CXX_NAME=gcc VARIANT=sanitize ARCH=64 22 | os: linux 23 | addons: 24 | apt: 25 | sources: 26 | - ubuntu-toolchain-r-test 27 | packages: 28 | - g++-4.8 29 | 30 | # GCC 5.3 31 | - env: CXX=g++-5 CXX_PATH=/usr/bin/g++-5 CXX_NAME=gcc VARIANT=sanitize ARCH=64 32 | os: linux 33 | addons: 34 | apt: 35 | sources: 36 | - ubuntu-toolchain-r-test 37 | packages: 38 | - g++-5 39 | 40 | # GCC 5.3 -- 32 bit 41 | - env: CXX=g++-5 CXX_PATH=/usr/bin/g++-5 CXX_NAME=gcc VARIANT=sanitize ARCH=32 42 | os: linux 43 | addons: 44 | apt: 45 | sources: 46 | - ubuntu-toolchain-r-test 47 | packages: 48 | - g++-5 49 | - g++-5-multilib 50 | - linux-libc-dev:i386 51 | 52 | # Xcode 6.4 53 | - env: CXX=clang++ CXX_PATH=clang++ CXX_NAME=clang VARIANT=release ARCH=64 54 | os: osx 55 | osx_image: xcode6.4 56 | 57 | exclude: 58 | - env: TRAVIS_EXTRA_JOB_WORKAROUND=true 59 | 60 | cache: 61 | directories: 62 | - deps 63 | 64 | install: 65 | - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" 66 | - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} 67 | 68 | # Install Boost 69 | - | 70 | if [ ! -d boost ]; then 71 | BOOST_URL="http://sourceforge.net/projects/boost/files/boost/1.61.0/boost_1_61_0.tar.gz" 72 | mkdir boost && travis_retry wget --quiet -O - ${BOOST_URL} | tar --strip-components=1 -xz -C boost 73 | fi 74 | 75 | - export BOOST_ROOT=${DEPS_DIR}/boost 76 | - export BOOST_BUILD_PATH=${BOOST_ROOT} 77 | 78 | # Setup Boost.Build 79 | - | 80 | echo "using ${CXX_NAME} : : ${CXX_PATH} ;" > ${HOME}/user-config.jam 81 | cd ${BOOST_ROOT} 82 | ./bootstrap.sh --with-libraries=serialization # build b2 83 | ./b2 toolset=${CXX_NAME} address-model=${ARCH} -j4 # build libraries 84 | export PATH=${BOOST_ROOT}:${PATH} 85 | 86 | - cd ${TRAVIS_BUILD_DIR} 87 | 88 | script: 89 | - cd test 90 | - ${BOOST_ROOT}/b2 toolset=${CXX_NAME} variant=${VARIANT} address-model=${ARCH} architecture=x86 -j2 91 | 92 | -------------------------------------------------------------------------------- /Jamroot.jam: -------------------------------------------------------------------------------- 1 | # (C) Copyright Benedek Thaler 2015-2016. 2 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | # Search for a boost installation to use 6 | 7 | # Use BOOST_ROOT if present 8 | # To set this variable, invoke bjam like this: 9 | # 10 | # $ BOOST_ROOT=/path/to/custom/install bjam 11 | # 12 | # A more permanent solution is to add the following 13 | # to your ~/user-config.jam along with your compilers: 14 | # 15 | # path-constant BOOST_ROOT : /path/to/custom/install ; 16 | # 17 | # using gcc ; 18 | # using clang ; 19 | # 20 | 21 | local boost_root = $(BOOST_ROOT) ; 22 | 23 | if ! $(boost_root) 24 | { 25 | boost_root = [ modules.peek : BOOST_ROOT ] ; 26 | } 27 | 28 | if ! $(boost_root) 29 | { 30 | # BOOST_ROOT not set, let's see if the project 31 | # is in the boost tree (under libs/project/) 32 | 33 | if [ glob ../../boost/version.hpp ] 34 | { 35 | boost_root = ../../ ; 36 | } 37 | } 38 | 39 | if ! $(boost_root) 40 | { 41 | # Still no luck. Let's see if bjam gives us a path 42 | 43 | local dirs = [ modules.peek : BOOST_BUILD_PATH ] ; 44 | 45 | for local dir in $(dirs) 46 | { 47 | if [ glob $(dir)/boost/version.hpp ] 48 | { 49 | boost_root = $(dir) ; 50 | } 51 | } 52 | } 53 | 54 | if $(boost_root) 55 | { 56 | ECHO "Using boost install:" $(boost_root) ; 57 | path-constant BOOST_ROOT : $(boost_root) ; 58 | } 59 | else 60 | { 61 | ECHO "No custom boost install specified, will try to use system install" ; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DoubleEndend containers 2 | [![Build Status](https://travis-ci.org/erenon/double_ended.svg?branch=master)](https://travis-ci.org/erenon/double_ended) 3 | [![Build Status](https://ci.appveyor.com/api/projects/status/github/erenon/double_ended?svg=true&branch=master)](https://ci.appveyor.com/project/erenon/double-ended) 4 | 5 | This library provides two double ended containers, `devector` and `batch_deque` 6 | similar to the `vector` and `deque` in the C++ Standard Library, 7 | but with additional features geared towards high performance, and unsafe constructs, giving more 8 | control to the user. 9 | 10 | ## Overview 11 | 12 | `devector` is a hybrid of the standard vector and deque containers, as well as the `small_vector` of Boost.Container. 13 | It offers cheap (amortized constant time) insertion at both the front and back ends, 14 | while also providing the regular features of `std::vector`, in particular the contiguous underlying memory. 15 | In contrast to the standard vector, however, `devector` offers greater control over its internals. 16 | Like `small_vector`, it can store elements internally without dynamically allocating memory. 17 | 18 | `batch_deque` is very similar to the standard `deque`, with a slight twist. It lets you specify its _segment size_: 19 | The size of the contiguous memory segments it uses to store its elements. This provides a consistent layout across platforms, 20 | and lets you iterate over the segments with batch operations. 21 | 22 | Read more in the [documentation][0] 23 | 24 | ## Disclaimer 25 | 26 | **This is not an official Boost library** 27 | 28 | [0]: http://erenon.hu/double_ended/ 29 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Copyright Benedek Thaler 2015-2016 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 4 | 5 | os: 6 | - Visual Studio 2015 7 | 8 | build: 9 | verbosity: detailed 10 | 11 | configuration: 12 | - Debug 13 | 14 | branches: 15 | only: 16 | - master 17 | 18 | environment: 19 | BOOST_ROOT: C:\Libraries\boost_1_60_0 20 | BOOST_BUILD_PATH: C:\Libraries\boost_1_60_0 21 | 22 | install: 23 | - cd C:\Libraries\boost_1_60_0 24 | - call .\bootstrap.bat 25 | - call .\b2 --with-serialization 26 | - xcopy stage\lib\libboost_serialization-vc*-mt-gd-1_*.lib stage\lib\boost_serialization.lib* 27 | 28 | before_build: 29 | - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 30 | - dir C:\Libraries\boost_1_60_0 C:\Libraries\boost_1_60_0\stage\lib 31 | 32 | build_script: 33 | - cd C:\projects\double-ended\test 34 | - call C:\Libraries\boost_1_60_0\b2.exe toolset=msvc 35 | -------------------------------------------------------------------------------- /benchmark/benchmark_util.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_BENCHMARK_BENCHMARK_UTIL_HPP_HPP 12 | #define BOOST_DOUBLE_ENDED_BENCHMARK_BENCHMARK_UTIL_HPP_HPP 13 | 14 | #include 15 | #include 16 | #include // uint64_t 17 | #include // atomic_thread_fence 18 | #include 19 | #include // strerror 20 | 21 | #if defined __GNUC__ 22 | #include // __rdtsc 23 | #elif defined _MSC_VER 24 | #include // __rdtsc 25 | #endif 26 | 27 | #if defined _GNU_SOURCE 28 | #include 29 | #endif 30 | 31 | #include 32 | #include // access cli args 33 | 34 | inline void pin_thread() 35 | { 36 | #if defined _GNU_SOURCE 37 | pthread_t thread = pthread_self(); 38 | 39 | cpu_set_t cpuset; 40 | CPU_ZERO(&cpuset); 41 | CPU_SET(0, &cpuset); // pin to CPU0 42 | 43 | int ok = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); 44 | if (ok != 0) 45 | { 46 | std::cerr << "Failed to pin thread: " << strerror(errno) << "\n"; 47 | } 48 | #elif defined _MSC_VER 49 | auto ok = SetThreadAffinityMask(GetCurrentThread(), 1); 50 | if (! ok) 51 | { 52 | std::cerr << "Failed to pin thread: " << GetLastError() << "\n"; 53 | } 54 | #else 55 | std::cerr << "pin_thread() not implemented on this platform\n"; 56 | #endif 57 | } 58 | 59 | template 60 | void prefault(T* buffer, const std::size_t size) 61 | { 62 | const std::size_t page_size = 4 * 1024; 63 | char* cbuffer = reinterpret_cast(buffer); 64 | const std::size_t csize = size * sizeof(T); 65 | 66 | for (std::size_t i = 0; i < csize; i += page_size) 67 | { 68 | cbuffer[i] = 'X'; 69 | } 70 | } 71 | 72 | using clock_type = uint64_t; 73 | 74 | inline clock_type get_clock() 75 | { 76 | return __rdtsc(); 77 | } 78 | 79 | using sample_container = std::deque>; 80 | 81 | template 82 | std::vector measure( 83 | const SamplesPredicate& samples_ready, 84 | Callable1& init, 85 | Callable2& action, 86 | const Scale& scale 87 | ) 88 | { 89 | sample_container samples; 90 | 91 | while (! samples_ready(samples)) 92 | { 93 | samples.emplace_back(); 94 | auto& sample = samples.back(); 95 | sample.reserve(scale.size()); 96 | 97 | init(); 98 | 99 | std::atomic_thread_fence(std::memory_order_seq_cst); 100 | 101 | const clock_type base_clock = get_clock(); 102 | 103 | std::size_t action_idx = 0; 104 | for (std::size_t step : scale) 105 | { 106 | for (; action_idx < step; ++action_idx) 107 | { 108 | action(); 109 | } 110 | 111 | sample.push_back(get_clock() - base_clock); 112 | } 113 | } 114 | 115 | std::vector result(scale.size()); 116 | for (std::size_t clock = 0; clock < scale.size(); ++clock) 117 | { 118 | std::size_t sum = 0; 119 | for (auto&& sample : samples) 120 | { 121 | sum += sample[clock]; 122 | } 123 | result[clock] = double(sum) / samples.size(); 124 | } 125 | 126 | return result; 127 | } 128 | 129 | // SamplesPredicates 130 | 131 | struct n_samples 132 | { 133 | const std::size_t _n; 134 | n_samples(std::size_t n) : _n(n) {} 135 | bool operator()(sample_container& sc) const { return sc.size() >= _n; } 136 | }; 137 | 138 | // alternative: gather until std deviation is below a specified limit 139 | 140 | // Scales 141 | 142 | class exp_scale 143 | { 144 | class exp_scale_iterator : public boost::iterator_facade< 145 | exp_scale_iterator, 146 | const std::size_t, 147 | boost::forward_traversal_tag 148 | > 149 | { 150 | public: 151 | exp_scale_iterator() = default; 152 | exp_scale_iterator(std::size_t value, std::size_t mult, std::size_t remaining) 153 | :_value(value), 154 | _mult(mult), 155 | _remaining(remaining) 156 | {} 157 | 158 | private: 159 | friend class boost::iterator_core_access; 160 | 161 | const std::size_t& dereference() const { return _value; } 162 | bool equal(const exp_scale_iterator& rhs) const { return _remaining == rhs._remaining; } 163 | void increment() { _value *= _mult; --_remaining; } 164 | 165 | std::size_t _value; 166 | const std::size_t _mult = 0; 167 | std::size_t _remaining = 0; 168 | }; 169 | 170 | public: 171 | using iterator = exp_scale_iterator; 172 | 173 | exp_scale(std::size_t initial, std::size_t mult, std::size_t size) 174 | :_initial(initial), 175 | _mult(mult), 176 | _size(size) 177 | {} 178 | 179 | iterator begin() const { return iterator{_initial, _mult, _size}; } 180 | iterator end() const { return iterator{}; } 181 | std::size_t size() const { return _size; } 182 | 183 | std::size_t back() const 184 | { 185 | auto i = begin(); 186 | std::size_t last_val = 0; 187 | while (i != end()) { last_val = *i; ++i; } 188 | return last_val; 189 | } 190 | 191 | private: 192 | const std::size_t _initial; 193 | const std::size_t _mult; 194 | const std::size_t _size; 195 | }; 196 | 197 | std::string report_dir() 198 | { 199 | auto&& ms = boost::unit_test::framework::master_test_suite(); 200 | return (ms.argc < 2) ? "/tmp/" : std::string(ms.argv[1]) + '/'; 201 | } 202 | 203 | template 204 | void report( 205 | const std::string& report_name, 206 | const std::string& seria_name, 207 | const Scale& scale, 208 | const std::vector& seria 209 | ) 210 | { 211 | const std::string report_path = report_dir() + report_name + ".dat"; 212 | std::fstream out(report_path, std::ios_base::out); 213 | 214 | out << "X " << seria_name << "\n0 0\n"; 215 | auto y = seria.begin(); 216 | for (auto x = scale.begin(); x != scale.end(); ++x, ++y) 217 | { 218 | out << *x << " " << std::fixed << *y << "\n"; 219 | } 220 | 221 | if (out) 222 | { 223 | BOOST_TEST_MESSAGE("Report written to: ") << report_path; 224 | } 225 | else 226 | { 227 | BOOST_FAIL("Failed to write report: " << report_path); 228 | } 229 | } 230 | 231 | #endif // BOOST_DOUBLE_ENDED_BENCHMARK_BENCHMARK_UTIL_HPP_HPP 232 | -------------------------------------------------------------------------------- /benchmark/push_back.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | #include 13 | 14 | #define BOOST_TEST_MODULE benchmark 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "benchmark_util.hpp" 24 | 25 | namespace { 26 | 27 | struct config 28 | { 29 | config() 30 | :scale(100, 2, 14) 31 | { 32 | pin_thread(); 33 | } 34 | 35 | exp_scale scale; 36 | }; 37 | 38 | config g_config; 39 | 40 | template 41 | std::vector push_back_impl(const Scale& scale) 42 | { 43 | Vector v; 44 | 45 | auto init = [&]() 46 | { 47 | v.clear(); 48 | const std::size_t max_size = scale.back(); 49 | v.reserve(max_size); 50 | prefault(v.data(), max_size); 51 | }; 52 | 53 | auto action = [&v]() 54 | { 55 | v.push_back(1); 56 | }; 57 | 58 | auto result = measure( 59 | n_samples(100), 60 | init, 61 | action, 62 | scale 63 | ); 64 | 65 | return result; 66 | } 67 | 68 | template 69 | std::vector deque_push_back_impl(const Scale& scale) 70 | { 71 | Deque v; 72 | 73 | auto init = [&]() 74 | { 75 | v.clear(); 76 | }; 77 | 78 | auto action = [&v]() 79 | { 80 | v.push_back(1); 81 | }; 82 | 83 | auto result = measure( 84 | n_samples(25), 85 | init, 86 | action, 87 | scale 88 | ); 89 | 90 | return result; 91 | } 92 | 93 | } // namespace 94 | 95 | BOOST_AUTO_TEST_SUITE(push_back) 96 | 97 | BOOST_AUTO_TEST_CASE(vector) 98 | { 99 | auto&& scale = g_config.scale; 100 | auto result = push_back_impl>(scale); 101 | report("push_back_vector", "std::vector", scale, result); 102 | } 103 | 104 | BOOST_AUTO_TEST_CASE(devector) 105 | { 106 | auto&& scale = g_config.scale; 107 | auto result = push_back_impl>(scale); 108 | report("push_back_devector", "devector", scale, result); 109 | } 110 | 111 | BOOST_AUTO_TEST_CASE(bcvector) 112 | { 113 | auto&& scale = g_config.scale; 114 | auto result = push_back_impl>(scale); 115 | report("push_back_bcvector", "boost::container::vector", scale, result); 116 | } 117 | 118 | BOOST_AUTO_TEST_CASE(deque) 119 | { 120 | auto&& scale = g_config.scale; 121 | auto result = deque_push_back_impl>(scale); 122 | report("push_back_deque", "std::deque", scale, result); 123 | } 124 | 125 | BOOST_AUTO_TEST_CASE(batch_deque) 126 | { 127 | auto&& scale = g_config.scale; 128 | auto result = deque_push_back_impl>(scale); 129 | report("push_back_batch_deque", "batch_deque", scale, result); 130 | } 131 | 132 | BOOST_AUTO_TEST_CASE(bcdeque) 133 | { 134 | auto&& scale = g_config.scale; 135 | auto result = deque_push_back_impl>(scale); 136 | report("push_back_bcdeque", "boost::container::deque", scale, result); 137 | } 138 | 139 | BOOST_AUTO_TEST_SUITE_END() 140 | -------------------------------------------------------------------------------- /benchmark/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # (C) Copyright Benedek Thaler 2015-2016 4 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | if [ $# -lt 1 ]; then 8 | echo "Usage $0 " 9 | exit 1 10 | fi 11 | 12 | TMP_DIR=/tmp/double_ended_bench 13 | BINARY=$1 14 | 15 | mkdir -p $TMP_DIR 16 | 17 | echo "Don't forget to turn off CPU scaling on CPU0, and boot with isolcpus=1" 18 | echo "Run tests..." 19 | 20 | $BINARY -- $TMP_DIR 21 | 22 | function plot3 23 | { 24 | title=$1 25 | output=$TMP_DIR/$2.png 26 | src1=$TMP_DIR/$3.dat 27 | src2=$TMP_DIR/$4.dat 28 | src3=$TMP_DIR/$5.dat 29 | 30 | gnuplot \ 31 | -e "set title \"$title\"" \ 32 | -e "set term pngcairo" \ 33 | -e "set output \"$output\"" \ 34 | -e "set key left" \ 35 | -e "set key autotitle columnheader" \ 36 | -e "set autoscale xfix" \ 37 | -e "set format x \"%.0s%c\"" \ 38 | -e "set xlabel 'Container size'" \ 39 | -e "set ylabel 'Time Stamp Counter'" \ 40 | -e "plot \"$src1\" u 1:2 smooth csplines, \"$src2\" u 1:2 smooth csplines, \"$src3\" u 1:2 smooth csplines" && 41 | 42 | echo "Plot done: $output" 43 | } 44 | 45 | echo "Plot..." 46 | 47 | plot3 "Vector push_back() to preallocated" vector_push_back push_back_vector push_back_bcvector push_back_devector 48 | plot3 "Deque push_back()" deque_push_back push_back_deque push_back_bcdeque push_back_batch_deque 49 | -------------------------------------------------------------------------------- /doc/01-introduction.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / (C) Copyright Benedek Thaler 2015-2016 3 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | /] 6 | 7 | [section Introduction] 8 | 9 | The library provides double ended containers, similar to the ones in the C++ Standard Library, 10 | but with additional features geared towards high performance, and unsafe constructs, giving more 11 | control to the user. 12 | 13 | The library requires a C++11 compatible compiler. 14 | The library is tested and proved to work with GCC 4.8 and above, Clang 3.5 and above, and with Visual Studio 2015. 15 | 16 | [important This is not an official Boost library] 17 | 18 | [endsect] 19 | -------------------------------------------------------------------------------- /doc/02-design_rationale.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / (C) Copyright Benedek Thaler 2015-2016 3 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | /] 6 | 7 | [section Design Rationale] 8 | 9 | [section:devector ['devector]] 10 | 11 | `devector` is a hybrid of the standard vector and deque containers, as well as the `small_vector` of Boost.Container. 12 | It offers cheap (amortized constant time) insertion at both the front and back ends, 13 | while also providing the regular features of `std::vector`, in particular the contiguous underlying memory. 14 | In contrast to the standard vector, however, `devector` offers greater control over its internals. 15 | Like `small_vector`, it can store elements internally without dynamically allocating memory. 16 | 17 | Through a policy template argument, it's possible to add small buffer optimization to `devector`: 18 | The specified number of elements will be stored inline with the internals, without requiring 19 | additional heap allocation. If the size of the `devector` exceeds the small buffer size, it 20 | automatically switches to an allocated storage. 21 | 22 | Furthermore, through another policy template argument, it's also possible to change how the `devector` grows 23 | its storage, providing explicit control over the reallocation strategy and consistent behavior across 24 | different platforms. 25 | 26 | To accommodate high performance requirements, `devector` offers (potentially) unsafe, 27 | less encapsulating methods: such methods can avoid unnecessary checks or initialization, 28 | but can trigger undefined behavior if their contract is broken. 29 | 30 | The `devector` class template intends to be a drop in replacement of `std::vector`. However, to keep its size slim 31 | (16 bytes on 32 bit, 24 bytes on a typical 64 bit architecture), its `size_type` is defined as 32 | `unsigned int` which reduces `max_size()` to `UINT_MAX`. 33 | 34 | In general, `devector` uses `move_if_noexcept` whenever necessary, offering strong exception guarantees. 35 | `devector` implements the NAD (Not A Defect) resolution of 36 | [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 LWG Issue 526 37 | (['Is it undefined if a function in the standard changes in parameters?])]. 38 | 39 | [endsect] 40 | 41 | [section:batch_deque ['batch_deque]] 42 | 43 | `batch_deque` is very similar to the standard `deque`, with a slight twist. It lets you specify its /segment size/: 44 | The size of the contiguous memory segments it uses to store its elements. This provides a consistent layout across platforms, 45 | and lets you iterate over the segments with batch operations. 46 | 47 | `batch_deque` makes a good candidate for a container of cached or reusable elements, because it provides reference 48 | stability while inserting at either end with amortized constant complexity. Reference stability also makes it 49 | possible to store the elements of an intrusive container. 50 | 51 | [endsect] 52 | 53 | [endsect] 54 | -------------------------------------------------------------------------------- /doc/03-examples.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / (C) Copyright Benedek Thaler 2015-2016 3 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | /] 6 | 7 | [section Examples] 8 | 9 | [import ../example/doc_devector.cpp] 10 | 11 | Below, the following includes and namespace alias are assumed: 12 | 13 | [doc_devector_preamble] 14 | 15 | [section:devector_usage devector usage] 16 | 17 | The `devector` class template, along the standard vector features, also offers a few extra. 18 | The small buffer optimization allows some elements to be stored next to the `devector` internals, 19 | without allocating memory on the heap. If the size of the `devector` exceeds the size of the small buffer, 20 | elements previously held in the small buffer are copied to a dynamically allocated buffer. The small buffer 21 | remains unused: The memory is always contiguous. The size of the small buffer can be set by the `SmallBufferPolicy` 22 | template argument. By default, it's set to 0, which means, no small buffer is used. 23 | 24 | [doc_devector_sbo] 25 | 26 | Here, while `push_back` calls make use of the small buffer, `push_front` results in an allocation: 27 | Push operations do not shift elements to avoid breaking the promise of `reserve`. 28 | 29 | In contrast with the standard vector, `devector` allows reserving construction time. This is especially 30 | important when both front and back pushes are expected, and the small buffer should be used. 31 | In the following example, only a single allocation is required, no reallocation happens: 32 | 33 | [doc_devector_ctr_reserve] 34 | 35 | The small buffer optimization makes the `devector` a good buffer candidate. 36 | It is safer and more flexible than a C style array or `std::array`, because it manages its size and memory, 37 | and it's just as performant: while using the small buffer, no dynamic memory allocation happens. 38 | 39 | The pace how standard vector grows its buffer is not fixed by the standard, it's an implementation detail. 40 | If you want a platform independent, custom behavior for `devector`, specify a `GrowthPolicy`: 41 | 42 | [doc_devector_growth_policy] 43 | 44 | With this policy, when an element is pushed to a `devector` without capacity, 45 | memory for 16 elements will be allocated (`initial_size`), and the size of this 46 | storage is doubled (`growth_factor`) each time it runs out. 47 | 48 | When `devector::shrink_to_fit` is called, the `should_shrink` method of the growth policy is called, and shrinking happens only if it returns `true`. 49 | In this example, the policy allows shrinking only if the contents of the `devector` can fit in the small buffer. 50 | 51 | Among the several use cases, a simple one is reversing an input range in one go 52 | while doing as few allocations as possible. In the following example, input lines are pushed 53 | to the front of the buffer, thus automatically forming a reverse order. 54 | 55 | [doc_devector_reverse_input] 56 | 57 | The `devector` is more trusting than the standard containers: 58 | it allows the programmer to use (potentially) unsafe methods. 59 | The most simple are the `unsafe_push_front` and `unsafe_push_back` methods. 60 | They are similar to the safe versions, but require the caller to make sure the required free capacity 61 | is present. The benefit: avoiding a check, thus sparing a few instructions. 62 | 63 | [doc_devector_unsafe_push] 64 | 65 | Another unsafe, but efficient construct is placing uninitialized elements into the sequence. 66 | In the example below, data is read from a socket and copied directly to the `devector` buffer: 67 | 68 | [doc_devector_unsafe_uninit] 69 | 70 | The constructor used here does not initialize the elements: it's done by `recv` later. 71 | After writing is done, the superfluous, still uninitialized elements are dropped from the back 72 | by `unsafe_uninitialized_resize_back`. This is particularly important for non-trivial value types: 73 | The `devector` does not know which elements are initialized, thus, for example, upon destruction, 74 | the destructor is called for every supposed element, even if one is not really there. 75 | 76 | When used with a non trivial type and small buffer optimization, this can be even more efficient 77 | (and more flexible) than using `std::array`, because no unnecessary initialization or assignment is made. 78 | Non trivial types can be be put into the right uninitialized slot by /placement new/. 79 | 80 | Most of the time, standard vector can be easily replaced by `devector`. If a custom allocator 81 | is needed, however, extra template arguments must be specified: 82 | 83 | [doc_devector_replace_vector] 84 | 85 | [endsect] 86 | 87 | [section:batch_deque_usage batch_deque usage] 88 | 89 | [import ../example/doc_batch_deque.cpp] 90 | 91 | The `batch_deque` class template, contrary to the standard deque, allows custom sized segments, configurable via a policy template. 92 | This gives greater control over the implementation and ensures portable behavior: 93 | 94 | [doc_batch_deque_sizing] 95 | 96 | Assuming the user more or less can guess the average required buffer size, the right segment size can be specified to minimize the number of memory allocations. 97 | 98 | Since the segment size is configured by the user, it makes sense to be able to do batch operations over the elements of each segment. 99 | In the example below, the deque is used as a flexible output buffer. 100 | The `write` function is called for each segment, instead of for each element, resulting in better performance. 101 | Scatter/gather I/O is possible in a similar way. 102 | 103 | [doc_batch_deque_segment_iterator] 104 | 105 | Reference stability is an important feature of the `batch_deque`. 106 | Inserting elements to the front or to the back of the sequence never invalidates any references. 107 | Moreover, a special insert operation is provided, `stable_insert`, which takes an iterator, as a hint, and inserts a sequence of elements (not necessary directly) before the element pointed by the hint, in a way that it doesn't invalidate references. 108 | If needed, default constructs elements to avoid a gap in the sequence. 109 | 110 | [doc_batch_deque_stable_insert] 111 | 112 | This feature can be useful, if reference stability and relative order is required, but the exact number of the elements is flexible, e.g: when the sequence stores cached objects. 113 | 114 | [endsect] 115 | 116 | [section:serialize_containers Serialize containers] 117 | 118 | The `double_ended` containers play well with the Boost.Serialization library. Any container can be 119 | serialized, as long as its value type can be serialized as well. 120 | 121 | [import ../example/doc_serialize.cpp] 122 | 123 | [doc_serialize_basics] 124 | 125 | [endsect] 126 | 127 | [endsect] 128 | -------------------------------------------------------------------------------- /doc/04-benchmarks.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / (C) Copyright Benedek Thaler 2015-2016 3 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | /] 6 | 7 | [section Benchmarks] 8 | 9 | Benchmarking a small piece of generic code such as this library is challenging because 10 | no benchmark can represent every actual use-case. What looks good on paper might 11 | perform poorly in practice, and vice-versa. Neither the comparison similar solutions is trivial. 12 | It is obvious, that `devector` excels if it can take advantage of its small buffer optimization. 13 | However, comparing a small buffer enabled `devector` to a standard vector is just measuring 14 | the performance of the memory allocator. 15 | 16 | It was tried to compare equivalent functionality of different containers, but the 17 | measurement gave surprising results. 18 | [footnote See [@http://lists.boost.org/Archives/boost/2016/02/227743.php What's wrong with this benchmark] mail thread] 19 | 20 | Instead of disclosing those unreliable performance numbers, the actual compiled code is presented here, 21 | to prove that the quality of the generated code matches the equivalent standard implementations. 22 | Below, a loop is shown, pushing back a constant value into a `std::vector`/`devector`. 23 | The code is compiled using GCC 5.2 with `-O2 -march=native` on a AMD FX 8320 processor. 24 | 25 | [table:push_back_code Assembly code of v.push_back(1) loop 26 | [[std::vector] [devector]] 27 | [ 28 | [ 29 | [pre 30 | ` 31 | 0: test %rax,%rax 32 | je <1> 33 | movl $0x1,(%rax) 34 | 1: add $0x4,%rax 35 | mov %rax,0x68(%rsp) 36 | inc %rbx 37 | cmp %rbp,%rbx 38 | jae 39 | mov 0x68(%rsp),%rax 40 | movl $0x1,0x3c(%rsp) 41 | cmp 0x70(%rsp),%rax 42 | jne <0> 43 | ` 44 | ] 45 | ] 46 | [ 47 | [pre 48 | ` 49 | 0: mov %ebx,%eax 50 | lea (%r14,%rax,4),%rax 51 | test %rax,%rax 52 | je <1> 53 | movl $0x1,(%rax) 54 | 1: inc %ebx 55 | inc %r13 56 | mov %ebx,0xb4(%rsp) 57 | cmp 0x8(%rsp),%r13 58 | jae 59 | cmp %ebx,%r12d 60 | jne <0> 61 | ` 62 | ] 63 | ] 64 | ] 65 | ] 66 | 67 | [endsect] 68 | -------------------------------------------------------------------------------- /doc/05-build_and_test.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / (C) Copyright Benedek Thaler 2015-2016 3 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | /] 6 | 7 | [section Build and Test] 8 | 9 | The library itself is header only, no separate build is required to use it, only 10 | the `include/` directory has to be added to the compiler include search path. 11 | 12 | [section Build the tests] 13 | 14 | Although it is not required to use the library, the tests can be build separately. 15 | A `Boost` tree is needed (version 1.59 or above), with at least `bjam` and `Boost.Serialization` library installed. 16 | However, it's not required to use the `Boost` installation provided by the operating system or distribution. 17 | To avoid conflicting with such installs, `BOOST_BUILD_PATH` can be explicitly specified: 18 | 19 | double_ended/test $ export BOOST_BUILD_PATH=/path/to/boost # contains bjam tool, boost and stage libraries 20 | double_ended/test $ $BOOST_BUILD_PATH/bjam 21 | 22 | The last command executed, the tests are compiled and executed. During compilation, no warning should be emitted 23 | and each test should pass, indicated by the `**passed**` string on the output. 24 | To change the used compiler, specify the toolchain switch: 25 | 26 | double_ended/test $ $BOOST_BUILD_PATH/bjam toolset=gcc # or clang or msvc 27 | 28 | To build the tests in release mode, use the `release` variant: 29 | 30 | double_ended/test $ $BOOST_BUILD_PATH/bjam variant=release 31 | 32 | On platforms where the [@https://github.com/google/sanitizers/wiki address sanitizer] is supported, 33 | the `sanitize` variant can be used to look for memory related errors: 34 | 35 | double_ended/test $ $BOOST_BUILD_PATH/bjam variant=sanitize 36 | 37 | A test coverage report can be generated when the GCC toolchain is in use: 38 | 39 | double_ended/test $ $BOOST_BUILD_PATH/bjam toolset=gcc variant=coverage 40 | double_ended/test $ $BOOST_BUILD_PATH/bjam toolset=gcc lcov 41 | 42 | [endsect] 43 | 44 | [section Build the documentation] 45 | 46 | To build the documentation (this one), run `bjam` in the `doc/` directory: 47 | 48 | double_ended/doc $ export BOOST_BUILD_PATH=/path/to/boost # contains bjam tool 49 | double_ended/doc $ $BOOST_BUILD_PATH/bjam 50 | 51 | The toolchain requires the quickbook, boostbook and doxygen tools to be 52 | [@http://www.boost.org/build/doc/html/bbv2/reference/tools.html#idp88891216 properly configured]. 53 | 54 | [endsect] 55 | 56 | [endsect] 57 | -------------------------------------------------------------------------------- /doc/06-acknowledgements.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / (C) Copyright Benedek Thaler 2015-2016 3 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | /] 6 | 7 | [section Acknowledgements] 8 | 9 | This library was initially founded by the Google Summer of Code 2015 program. 10 | I would like to thank Thorsten Ottosen for mentoring during and after the program. 11 | I would like to thank the Boost community for the valuable feedback. 12 | Design decisions were validated by looking at the excellent source of the 13 | [@https://github.com/gcc-mirror/gcc/tree/master/libstdc%2B%2B-v3/include libstdc++], 14 | [@http://llvm.org/svn/llvm-project/libcxx/trunk/include libc++] 15 | and [@https://github.com/boostorg/container/tree/develop/include/boost/container Boost.Container] 16 | libraries. 17 | 18 | [endsect] 19 | -------------------------------------------------------------------------------- /doc/Jamfile.v2: -------------------------------------------------------------------------------- 1 | # (C) Copyright Benedek Thaler 2015-2016. 2 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | using doxygen ; 6 | using auto-index ; 7 | 8 | import quickbook ; 9 | 10 | project boost/double_ended_doc 11 | : build-dir /var/tmp/double_ended/build/doc 12 | ; 13 | 14 | doxygen apidoc 15 | : [ glob ../include/boost/double_ended/*.hpp ] 16 | : 17 | "PREDEFINED= \\ 18 | \"BOOST_DOUBLE_ENDED_DOXYGEN_INVOKED\" \\ 19 | " 20 | INLINE_INHERITED_MEMB=YES 21 | MACRO_EXPANSION=YES 22 | INCLUDE_PATH="../include $(BOOST_ROOT)" 23 | EXAMPLE_PATH=../example 24 | SUBGROUPING=NO 25 | ; 26 | 27 | xml double_ended : index.qbk ; 28 | 29 | boostbook standalone 30 | : double_ended 31 | : 32 | apidoc 33 | boost.root=$(BOOST_ROOT) 34 | html.stylesheet=http://www.boost.org/doc/libs/1_55_0/doc/src/boostbook.css 35 | boost.graphics.root=http://www.boost.org/doc/libs/common/doc/src/images/ 36 | boost.header.root=../../include 37 | html 38 | 39 | # Auto-index 40 | on 41 | on 42 | pdf:off 43 | html:on 44 | index.idx 45 | ../ 46 | enable_index 47 | ; 48 | -------------------------------------------------------------------------------- /doc/index.idx: -------------------------------------------------------------------------------- 1 | !scan-path "include/boost/double_ended" .*hpp false 2 | -------------------------------------------------------------------------------- /doc/index.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / (C) Copyright Benedek Thaler 2015-2016 3 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | /] 6 | 7 | [library Boost.DoubleEnded 8 | [quickbook 1.6] 9 | [version 1] 10 | [/ [authors [Benedek Thaler]] ] 11 | [copyright 2015-2016 Benedek Thaler] 12 | [category container] 13 | [id double_ended] 14 | [dirname double_ended] 15 | [purpose 16 | Enhanced double ended containers with additional configuration and unsafe methods 17 | ] 18 | [license Boost Software License, Version 1.0.] 19 | ] 20 | 21 | [include 01-introduction.qbk] 22 | [include 02-design_rationale.qbk] 23 | [include 03-examples.qbk] 24 | [include 04-benchmarks.qbk] 25 | 26 | [xinclude apidoc.xml] 27 | 28 | [include 05-build_and_test.qbk] 29 | [include 06-acknowledgements.qbk] 30 | '''''' 31 | -------------------------------------------------------------------------------- /example/doc_batch_deque.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace de = boost::double_ended; 17 | 18 | namespace detail { 19 | 20 | // unistd.h, missing on non-POSIX systems 21 | int write(int, const void*, size_t) { return -1; } 22 | 23 | } 24 | 25 | void iterate_segments() 26 | { 27 | using detail::write; 28 | 29 | //[doc_batch_deque_sizing 30 | de::batch_deque> deque; 31 | //] 32 | 33 | int fd = 1; 34 | 35 | //[doc_batch_deque_segment_iterator 36 | for (auto it = deque.segment_begin(); it != deque.segment_end(); ++it) 37 | { 38 | write(fd, it.data(), it.data_size()); 39 | } 40 | //] 41 | } 42 | 43 | void insert_stable() 44 | { 45 | //[doc_batch_deque_stable_insert 46 | de::batch_deque deque{1, 2, 3, 4, 5, 6, 7, 8}; 47 | auto four_it = deque.begin() + 3; 48 | int& four = *four_it; 49 | 50 | std::vector input(100, 9); 51 | deque.stable_insert(four_it, input.begin(), input.end()); 52 | 53 | BOOST_ASSERT(four == 4); // stable_insert ensures reference stability 54 | //] 55 | (void) four; // unused in release build if NDEBUG is defined 56 | } 57 | 58 | int main() 59 | { 60 | iterate_segments(); 61 | insert_stable(); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /example/doc_devector.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | //[doc_devector_preamble 19 | #include 20 | #include 21 | 22 | namespace de = boost::double_ended; 23 | //] 24 | 25 | // sys/socket.h, missing on non-POSIX systems 26 | long recv(int, void*, size_t size, int) { return size; } 27 | 28 | //[doc_devector_growth_policy 29 | struct custom_growth_policy 30 | { 31 | using size_type = unsigned int; 32 | 33 | static constexpr unsigned growth_factor = 2u; 34 | static constexpr unsigned initial_size = 16u; 35 | 36 | static unsigned new_capacity(unsigned capacity) 37 | { 38 | return (capacity) ? capacity * growth_factor : initial_size; 39 | } 40 | 41 | static bool should_shrink(unsigned size, unsigned capacity, unsigned small_buffer_size) 42 | { 43 | (void)capacity; 44 | return size <= small_buffer_size; 45 | } 46 | }; 47 | 48 | de::devector, custom_growth_policy> custom_growth_devector; 49 | //] 50 | 51 | template 52 | using custom_allocator = std::allocator; 53 | 54 | int main() 55 | { 56 | (void) custom_growth_devector; 57 | 58 | //[doc_devector_sbo 59 | de::devector> small_devector; 60 | 61 | BOOST_ASSERT(small_devector.capacity() == 16); // but no dynamic memory was allocated 62 | 63 | small_devector.push_back(2); 64 | small_devector.push_back(3); 65 | small_devector.push_back(4); 66 | 67 | small_devector.push_front(1); // allocates 68 | //] 69 | 70 | //[doc_devector_ctr_reserve 71 | de::devector reserved_devector(32, 16, de::reserve_only_tag{}); 72 | 73 | for (int i = 0; i < 32; ++i) { 74 | reserved_devector.push_front(i); 75 | } 76 | 77 | for (int i = 0; i < 16; ++i) { 78 | reserved_devector.push_back(i); 79 | } 80 | //] 81 | 82 | std::cin.setstate(std::ios_base::eofbit); 83 | 84 | //[doc_devector_reverse_input 85 | de::devector reversed_lines; 86 | reversed_lines.reserve_front(24); 87 | 88 | std::string line; 89 | 90 | while (std::getline(std::cin, line)) 91 | { 92 | reversed_lines.push_front(line); 93 | } 94 | 95 | std::cout << "Reversed lines:\n"; 96 | for (auto&& line : reversed_lines) 97 | { 98 | std::cout << line << "\n"; 99 | } 100 | //] 101 | 102 | //[doc_devector_unsafe_push 103 | de::devector dv; 104 | dv.reserve_front(2); 105 | dv.reserve_back(2); // the previous reserve_front is still in effect 106 | 107 | dv.unsafe_push_front(2); 108 | dv.unsafe_push_front(1); 109 | dv.unsafe_push_back(3); 110 | dv.unsafe_push_back(4); 111 | //] 112 | 113 | int sockfd = 0; 114 | //[doc_devector_unsafe_uninit 115 | // int sockfd = socket(...); connect(...); 116 | de::devector buffer(256, de::unsafe_uninitialized_tag{}); 117 | long recvSize = recv(sockfd, buffer.data(), buffer.size(), 0); 118 | 119 | if (recvSize >= 0) 120 | { 121 | buffer.unsafe_uninitialized_resize_back(recvSize); 122 | // process contents of buffer 123 | } 124 | else { /* handle error */ } 125 | //] 126 | 127 | //[doc_devector_replace_vector 128 | std::vector regular_vector; 129 | de::devector regular_devector; 130 | 131 | std::vector> custom_alloc_vector; 132 | 133 | de::devector< 134 | int, 135 | de::small_buffer_size<0>, 136 | de::devector_growth_policy, 137 | custom_allocator 138 | > custom_alloc_devector; 139 | //] 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /example/doc_serialize.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | 13 | //[doc_serialize_basics 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | namespace de = boost::double_ended; 21 | 22 | int main() 23 | { 24 | const de::batch_deque bd{1, 2, 3, 4, 5, 6, 7, 8}; 25 | const de::devector dv{9, 10, 11, 12, 13, 14, 15, 16}; 26 | 27 | std::stringstream stream; 28 | 29 | { 30 | boost::archive::text_oarchive out(stream); 31 | out << bd; 32 | out << dv; 33 | } 34 | 35 | de::batch_deque new_bd; 36 | de::devector new_dv; 37 | 38 | { 39 | boost::archive::text_iarchive in(stream); 40 | in >> new_bd; 41 | in >> new_dv; 42 | } 43 | 44 | BOOST_ASSERT(bd == new_bd); 45 | BOOST_ASSERT(dv == new_dv); 46 | 47 | return 0; 48 | } 49 | //] 50 | -------------------------------------------------------------------------------- /include/boost/double_ended/detail/algorithm.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_DETAIL_ALGORITHM_HPP 12 | #define BOOST_DOUBLE_ENDED_DETAIL_ALGORITHM_HPP 13 | 14 | namespace boost { 15 | namespace double_ended { 16 | namespace detail { 17 | 18 | template 19 | void move_if_noexcept(Iterator first, Iterator last, Iterator dst) 20 | { 21 | while (first != last) 22 | { 23 | *dst++ = std::move_if_noexcept(*first++); 24 | } 25 | } 26 | 27 | template 28 | void move_if_noexcept_backward(Iterator first, Iterator last, Iterator dst_last) 29 | { 30 | while (first != last) 31 | { 32 | *(--dst_last) = std::move_if_noexcept(*(--last)); 33 | } 34 | } 35 | 36 | }}} // boost::double_ended::detail 37 | 38 | #endif // BOOST_DOUBLE_ENDED_DETAIL_ALGORITHM_HPP 39 | -------------------------------------------------------------------------------- /include/boost/double_ended/detail/allocator.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_DETAIL_ALLOCATOR_HPP 12 | #define BOOST_DOUBLE_ENDED_DETAIL_ALLOCATOR_HPP 13 | 14 | #include // allocator_traits 15 | #include 16 | 17 | #include 18 | 19 | namespace boost { 20 | namespace double_ended { 21 | namespace detail { 22 | 23 | template 24 | struct allocator_traits : public std::allocator_traits 25 | { 26 | using T = typename Allocator::value_type; 27 | 28 | // before C++14, std::allocator does not specify propagate_on_container_move_assignment, 29 | // std::allocator_traits sets it to false. Not something we would like to use. 30 | static constexpr bool propagate_on_move_assignment = 31 | std::allocator_traits::propagate_on_container_move_assignment::value 32 | || std::is_same>::value; 33 | 34 | // poor emulation of a C++17 feature 35 | static constexpr bool is_always_equal = 36 | std::is_same>::value 37 | || std::is_empty::value; 38 | 39 | // true, if there is a way to reconstruct t' from t in a noexcept way 40 | static constexpr bool t_is_nothrow_constructible = 41 | std::is_nothrow_copy_constructible::value 42 | || std::is_nothrow_move_constructible::value; 43 | 44 | // TODO use is_trivially_copyable instead of is_pod when available 45 | static constexpr bool t_is_trivially_copyable = std::is_pod::value; 46 | 47 | // Guard to deallocate buffer on exception 48 | typedef typename detail::allocation_guard allocation_guard; 49 | 50 | // Guard to destroy already constructed elements on exception 51 | // TODO enable null guard if required opearations are noexcept 52 | typedef typename detail::opt_construction_guard construction_guard; 53 | 54 | // Guard to destroy either source or target, on success or on exception 55 | typedef typename detail::opt_nand_construction_guard move_guard; 56 | }; 57 | 58 | }}} // namespace boost::double_ended::detail 59 | 60 | #endif // BOOST_DOUBLE_ENDED_DETAIL_ALLOCATOR_HPP 61 | -------------------------------------------------------------------------------- /include/boost/double_ended/detail/documentation.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_DETAIL_DOCUMENTATION_HPP 12 | #define BOOST_DOUBLE_ENDED_DETAIL_DOCUMENTATION_HPP 13 | 14 | namespace boost { 15 | namespace double_ended { 16 | namespace detail { 17 | 18 | #ifndef BOOST_DOUBLE_ENDED_DOXYGEN_INVOKED 19 | 20 | #define BOOST_DOUBLE_ENDED_IMPDEF(EXPR) EXPR 21 | 22 | #else 23 | 24 | #define BOOST_DOUBLE_ENDED_IMPDEF(EXPR) implementation_defined 25 | 26 | #endif // BOOST_DOUBLE_ENDED_DOXYGEN_INVOKED 27 | 28 | 29 | }}} // namespace boost::double_ended::detail 30 | 31 | #endif // BOOST_DOUBLE_ENDED_DETAIL_DOCUMENTATION_HPP 32 | -------------------------------------------------------------------------------- /include/boost/double_ended/detail/guards.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_DETAIL_GUARDS_HPP 12 | #define BOOST_DOUBLE_ENDED_DETAIL_GUARDS_HPP 13 | 14 | #include // BOOST_MOVABLE_BUT_NOT_COPYABLE 15 | 16 | namespace boost { 17 | namespace double_ended { 18 | namespace detail { 19 | 20 | class null_construction_guard 21 | { 22 | public: 23 | template 24 | null_construction_guard(Args&&...) {} 25 | 26 | void release() {} 27 | void extend() {} 28 | }; 29 | 30 | template 31 | class construction_guard 32 | { 33 | typedef typename std::allocator_traits::pointer pointer; 34 | typedef typename std::allocator_traits::size_type size_type; 35 | 36 | BOOST_MOVABLE_BUT_NOT_COPYABLE(construction_guard) 37 | 38 | public: 39 | construction_guard() = default; 40 | 41 | construction_guard(pointer alloc_ptr, Allocator& allocator) 42 | :_alloc_ptr(alloc_ptr), 43 | _elem_count(0), 44 | _allocator(&allocator) 45 | {} 46 | 47 | construction_guard(construction_guard&& rhs) 48 | :_alloc_ptr(rhs._alloc_ptr), 49 | _elem_count(rhs._elem_count), 50 | _allocator(rhs._allocator) 51 | { 52 | rhs._elem_count = 0; 53 | } 54 | 55 | ~construction_guard() 56 | { 57 | while (_elem_count--) 58 | { 59 | std::allocator_traits::destroy(*_allocator, _alloc_ptr++); 60 | } 61 | } 62 | 63 | void release() 64 | { 65 | _elem_count = 0; 66 | } 67 | 68 | void extend() 69 | { 70 | ++_elem_count; 71 | } 72 | 73 | private: 74 | pointer _alloc_ptr; 75 | size_type _elem_count = 0; 76 | Allocator* _allocator; 77 | }; 78 | 79 | template 80 | using opt_construction_guard = typename std::conditional< 81 | std::is_trivially_destructible::value_type>::value, 82 | null_construction_guard, 83 | construction_guard 84 | >::type; 85 | 86 | /** 87 | * Has two ranges 88 | * 89 | * On success, destroys the first range (src), 90 | * on failure, destroys the second range (dst). 91 | * 92 | * Can be used when copying/moving a range 93 | */ 94 | template 95 | class nand_construction_guard 96 | { 97 | typedef typename std::allocator_traits::pointer pointer; 98 | typedef typename std::allocator_traits::size_type size_type; 99 | 100 | construction_guard _src; 101 | construction_guard _dst; 102 | bool _dst_released = false; 103 | 104 | public: 105 | nand_construction_guard() = default; 106 | 107 | nand_construction_guard( 108 | pointer src, Allocator& src_alloc, 109 | pointer dst, Allocator& dst_alloc 110 | ) 111 | :_src(src, src_alloc), 112 | _dst(dst, dst_alloc), 113 | _dst_released(false) 114 | {} 115 | 116 | nand_construction_guard(nand_construction_guard&& rhs) = default; 117 | 118 | void extend() 119 | { 120 | _src.extend(); 121 | _dst.extend(); 122 | } 123 | 124 | void release() // on success 125 | { 126 | _dst.release(); 127 | _dst_released = true; 128 | } 129 | 130 | ~nand_construction_guard() 131 | { 132 | if (! _dst_released) { _src.release(); } 133 | } 134 | }; 135 | 136 | template 137 | using opt_nand_construction_guard = typename std::conditional< 138 | std::is_trivially_destructible::value_type>::value, 139 | null_construction_guard, 140 | nand_construction_guard 141 | >::type; 142 | 143 | template 144 | class allocation_guard 145 | { 146 | typedef typename std::allocator_traits::pointer pointer; 147 | typedef typename std::allocator_traits::size_type size_type; 148 | 149 | BOOST_MOVABLE_BUT_NOT_COPYABLE(allocation_guard) 150 | 151 | public: 152 | allocation_guard(pointer alloc_ptr, size_type alloc_size, Allocator& allocator) 153 | :_alloc_ptr(alloc_ptr), 154 | _alloc_size(alloc_size), 155 | _allocator(allocator) 156 | {} 157 | 158 | ~allocation_guard() 159 | { 160 | if (_alloc_ptr) 161 | { 162 | std::allocator_traits::deallocate(_allocator, _alloc_ptr, _alloc_size); 163 | } 164 | } 165 | 166 | void release() 167 | { 168 | _alloc_ptr = nullptr; 169 | } 170 | 171 | private: 172 | pointer _alloc_ptr; 173 | size_type _alloc_size; 174 | Allocator& _allocator; 175 | }; 176 | 177 | }}} // namespace boost::double_ended::detail 178 | 179 | #endif // BOOST_DOUBLE_ENDED_DETAIL_GUARDS_HPP 180 | -------------------------------------------------------------------------------- /include/boost/double_ended/detail/iterators.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_DETAIL_ITERATORS_HPP 12 | #define BOOST_DOUBLE_ENDED_DETAIL_ITERATORS_HPP 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | namespace boost { 20 | namespace double_ended { 21 | namespace detail { 22 | 23 | template 24 | class is_input_iterator 25 | { 26 | template 27 | static typename std::iterator_traits::iterator_category f(int); 28 | 29 | template 30 | static void f(...); 31 | 32 | public: 33 | enum { value = std::is_same< decltype(f(0)), std::input_iterator_tag>::value }; 34 | }; 35 | 36 | template 37 | struct is_not_input_iterator 38 | { 39 | enum { value = ! is_input_iterator::value }; 40 | }; 41 | 42 | template 43 | class is_forward_iterator 44 | { 45 | template 46 | static typename std::iterator_traits::iterator_category f(int); 47 | 48 | template 49 | static void f(...); 50 | 51 | public: 52 | enum { value = std::is_same< decltype(f(0)), std::forward_iterator_tag>::value }; 53 | }; 54 | 55 | #ifndef BOOST_DOUBLE_ENDED_DOXYGEN_INVOKED 56 | 57 | #define BOOST_DOUBLE_ENDED_REQUIRE_INPUT_ITERATOR(Iterator) \ 58 | typename Iterator, \ 59 | typename std::enable_if< \ 60 | detail::is_input_iterator::value \ 61 | ,int>::type = 0 62 | 63 | #define BOOST_DOUBLE_ENDED_REQUIRE_FW_ITERATOR(Iterator) \ 64 | typename Iterator, \ 65 | typename std::enable_if< \ 66 | detail::is_not_input_iterator::value \ 67 | ,int>::type = 0 68 | 69 | #else 70 | 71 | #define BOOST_DOUBLE_ENDED_REQUIRE_INPUT_ITERATOR(Iterator) typename Iterator 72 | #define BOOST_DOUBLE_ENDED_REQUIRE_FW_ITERATOR(Iterator) typename Iterator 73 | 74 | #endif // ifndef BOOST_DOUBLE_ENDED_DOXYGEN_INVOKED 75 | 76 | template 77 | T* iterator_to_pointer(T* p) { return p; } 78 | 79 | template 80 | typename std::iterator_traits::pointer 81 | iterator_to_pointer(const It& it) 82 | { 83 | return it.operator->(); 84 | } 85 | 86 | template 87 | class constant_iterator : public iterator_facade< 88 | constant_iterator, 89 | const T, 90 | random_access_traversal_tag, 91 | const T&, 92 | Difference 93 | > 94 | { 95 | public: 96 | constant_iterator() = default; 97 | constant_iterator(const T& value, Difference size) 98 | :_value(&value), 99 | _size(size) 100 | {} 101 | 102 | private: 103 | friend class boost::iterator_core_access; 104 | 105 | const T& dereference() const noexcept 106 | { 107 | return *_value; 108 | } 109 | 110 | bool equal(const constant_iterator& rhs) const noexcept 111 | { 112 | return _size == rhs._size; 113 | } 114 | 115 | void increment() noexcept 116 | { 117 | --_size; 118 | } 119 | 120 | void decrement() noexcept 121 | { 122 | ++_size; 123 | } 124 | 125 | void advance(Difference n) noexcept 126 | { 127 | _size -= n; 128 | } 129 | 130 | Difference distance_to(const constant_iterator& b) const noexcept 131 | { 132 | return _size - b._size; 133 | } 134 | 135 | const T* _value; 136 | Difference _size = 0; 137 | }; 138 | 139 | }}} // namespace boost::double_ended::detail 140 | 141 | #endif // BOOST_DOUBLE_ENDED_DETAIL_ITERATORS_HPP 142 | -------------------------------------------------------------------------------- /include/boost/double_ended/serialize_batch_deque.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_SERIALIZE_BATCH_DEQUE_HPP 12 | #define BOOST_DOUBLE_ENDED_SERIALIZE_BATCH_DEQUE_HPP 13 | 14 | #include 15 | #include 16 | 17 | namespace boost { 18 | namespace serialization { 19 | 20 | using double_ended::batch_deque; 21 | 22 | template 23 | using de_alloc_traits = double_ended::detail::allocator_traits; 24 | 25 | template 26 | void save(Archive& ar, const batch_deque& c, unsigned /*version*/) 27 | { 28 | auto size = c.size(); 29 | ar << size; 30 | 31 | if (de_alloc_traits::t_is_trivially_copyable) 32 | { 33 | auto first = c.segment_begin(); 34 | const auto last = c.segment_end(); 35 | for (; first != last; ++first) 36 | { 37 | ar.save_binary(first.data(), first.data_size() * sizeof(T)); 38 | } 39 | } 40 | else 41 | { 42 | for (const T& t : c) 43 | { 44 | ar << t; 45 | } 46 | } 47 | } 48 | 49 | namespace double_ended_detail { 50 | 51 | template 52 | void load_trivial(Archive& ar, batch_deque& c) 53 | { 54 | auto first = c.segment_begin(); 55 | const auto last = c.segment_end(); 56 | for (; first != last; ++first) 57 | { 58 | ar.load_binary(first.data(), first.data_size() * sizeof(T)); 59 | } 60 | } 61 | 62 | template 63 | void load_non_trivial(Archive& ar, batch_deque& c) 64 | { 65 | for (T& t : c) 66 | { 67 | ar >> t; 68 | } 69 | } 70 | 71 | } // namespace double_ended_detail 72 | 73 | template 74 | void load(Archive& ar, batch_deque& c, unsigned /*version*/) 75 | { 76 | using Deque = batch_deque; 77 | using size_type = typename Deque::size_type; 78 | 79 | size_type new_size; 80 | ar >> new_size; 81 | 82 | const size_type to_front = (std::min)(c.front_free_capacity() + c.size(), new_size); 83 | c.resize_front(to_front); 84 | c.resize_back(new_size - to_front + c.size()); 85 | 86 | if (de_alloc_traits::t_is_trivially_copyable) 87 | { 88 | double_ended_detail::load_trivial(ar, c); 89 | } 90 | else 91 | { 92 | double_ended_detail::load_non_trivial(ar, c); 93 | } 94 | } 95 | 96 | template 97 | void serialize(Archive& ar, batch_deque& c, unsigned version) 98 | { 99 | boost::serialization::split_free(ar, c, version); 100 | } 101 | 102 | }} // namespace boost::serialization 103 | 104 | #endif // BOOST_DOUBLE_ENDED_SERIALIZE_BATCH_DEQUE_HPP 105 | -------------------------------------------------------------------------------- /include/boost/double_ended/serialize_devector.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef BOOST_DOUBLE_ENDED_SERIALIZE_DEVECTOR_HPP 12 | #define BOOST_DOUBLE_ENDED_SERIALIZE_DEVECTOR_HPP 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace boost { 19 | namespace serialization { 20 | 21 | using double_ended::devector; 22 | 23 | template 24 | using de_alloc_traits = double_ended::detail::allocator_traits; 25 | 26 | template 27 | void save(Archive& ar, const devector& c, unsigned /*version*/) 28 | { 29 | auto size = c.size(); 30 | ar << size; 31 | 32 | if (de_alloc_traits::t_is_trivially_copyable) 33 | { 34 | ar.save_binary(c.data(), c.size() * sizeof(T)); 35 | } 36 | else 37 | { 38 | for (const T& t : c) 39 | { 40 | ar << t; 41 | } 42 | } 43 | } 44 | 45 | namespace double_ended_detail { 46 | 47 | template 48 | void load_trivial(Archive& ar, devector& c, ST new_size) 49 | { 50 | const bool fits = c.capacity() >= new_size; 51 | 52 | if (fits) 53 | { 54 | const auto to_front = (std::min)(c.front_free_capacity() + c.size(), new_size); 55 | c.unsafe_uninitialized_resize_front(to_front); 56 | c.unsafe_uninitialized_resize_back(new_size - to_front + c.size()); 57 | } 58 | else 59 | { 60 | c.clear(); 61 | c.unsafe_uninitialized_resize_back(new_size); 62 | } 63 | 64 | BOOST_TRY 65 | { 66 | ar.load_binary(c.data(), new_size * sizeof(T)); 67 | } 68 | BOOST_CATCH(...) 69 | { 70 | c.unsafe_uninitialized_resize_back(0); 71 | BOOST_RETHROW; 72 | } 73 | BOOST_CATCH_END 74 | } 75 | 76 | template 77 | void load_non_trivial(Archive& ar, devector& c, ST new_size) 78 | { 79 | const bool fits = c.capacity() >= new_size; 80 | 81 | if (fits) 82 | { 83 | const auto to_front = (std::min)(c.front_free_capacity() + c.size(), new_size); 84 | c.resize_front(to_front); 85 | c.resize_back(new_size - to_front + c.size()); 86 | } 87 | else 88 | { 89 | c.clear(); 90 | c.resize(new_size); 91 | } 92 | 93 | for (T& t : c) 94 | { 95 | ar >> t; 96 | } 97 | } 98 | 99 | } // namespace double_ended_detail 100 | 101 | template 102 | void load(Archive& ar, devector& c, unsigned /*version*/) 103 | { 104 | using Devector = devector; 105 | using size_type = typename Devector::size_type; 106 | 107 | size_type new_size; 108 | ar >> new_size; 109 | 110 | if (de_alloc_traits::t_is_trivially_copyable) 111 | { 112 | double_ended_detail::load_trivial(ar, c, new_size); 113 | } 114 | else 115 | { 116 | double_ended_detail::load_non_trivial(ar, c, new_size); 117 | } 118 | } 119 | 120 | template 121 | void serialize(Archive& ar, devector& c, unsigned version) 122 | { 123 | boost::serialization::split_free(ar, c, version); 124 | } 125 | 126 | }} // namespace boost::serialization 127 | 128 | #endif // BOOST_DOUBLE_ENDED_SERIALIZE_DEVECTOR_HPP 129 | -------------------------------------------------------------------------------- /test/Jamfile.v2: -------------------------------------------------------------------------------- 1 | # (C) Copyright Benedek Thaler 2015-2016. 2 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | using testing ; 6 | import notfile ; 7 | 8 | path-constant BUILD_DIR : ../bin/test ; 9 | path-constant TOP : ../ ; 10 | 11 | project boost/double_ended 12 | : build-dir $(BUILD_DIR) 13 | : requirements 14 | $(TOP)/include 15 | ; 16 | 17 | local toolset = 18 | gcc:-Wall 19 | gcc:-Wextra 20 | gcc:-Werror 21 | gcc:-std=c++11 22 | gcc:-isystem=$(BOOST_ROOT) 23 | 24 | clang:-Wall 25 | clang:-Wextra 26 | clang:-Werror 27 | clang:-std=c++11 28 | clang:-I$(BOOST_ROOT) 29 | clang:--system-header-prefix=boost/ 30 | clang:--no-system-header-prefix=boost/double_ended 31 | 32 | msvc:$(BOOST_ROOT) 33 | msvc:_SCL_SECURE_NO_WARNINGS 34 | msvc:/bigobj 35 | 36 | on 37 | ; 38 | 39 | variant sanitize : 40 | off 41 | on 42 | -fsanitize=address 43 | ; 44 | 45 | variant coverage : 46 | off 47 | on 48 | --coverage 49 | --coverage 50 | ; 51 | 52 | lib boost_serialization : : $(BOOST_ROOT)/stage/lib ; 53 | 54 | # lib asan must come first on the link line among the libraries, 55 | # specify every other library as dependency to force 56 | # Boost.Build to put it there. 57 | lib asan : : boost_serialization asan ; 58 | 59 | # Link asan library if variant=sanitize 60 | # 61 | # Specifying lib explicitly in variant requirements 62 | # results in recursive target definition, because 63 | # the build system wants to build the defined library 64 | # and link it to itself. 65 | # 66 | # We also can't just specify -lasan as a link flag, 67 | # because that would appear on the command line 68 | # later than other libraries, and asan must come first. 69 | rule link_asan ( properties * ) 70 | { 71 | local result ; 72 | if sanitize in $(properties) 73 | { 74 | result += asan ; 75 | } 76 | return $(result) ; 77 | } 78 | 79 | rule test_all 80 | { 81 | local rules = ; 82 | 83 | local sources = [ glob $(TOP)/test/*.cpp ] ; 84 | sources += [ glob $(TOP)/example/*.cpp ] ; 85 | 86 | for local source in $(sources) 87 | { 88 | local libs ; 89 | 90 | if [ MATCH (.*serialize.+) : $(source) ] 91 | { 92 | libs += boost_serialization ; 93 | } 94 | 95 | rules += [ run $(source) $(libs) 96 | : # args passed to tests 97 | : # input files 98 | : # requirements 99 | $(toolset) 100 | @link_asan 101 | ] ; 102 | } 103 | 104 | return $(rules) ; 105 | } 106 | 107 | alias double_ended_test : [ test_all ] ; 108 | 109 | notfile lcov : @lcov ; 110 | explicit lcov ; 111 | 112 | # Generate Code Coverage report 113 | # 114 | # Add --rc lcov_branch_coverage=1 to lcov and --branch-coverage to genhtml to get branch coverage. 115 | # To make the --no-external lcov switch work, move this file one dir up and adjust $TOP 116 | actions lcov 117 | { 118 | lcov -c -d "$(BUILD_DIR)" -b "$(TOP)" -o "$(BUILD_DIR)/double_ended-coverage.info"; 119 | genhtml "$(BUILD_DIR)/double_ended-coverage.info" -o "$(BUILD_DIR)/coverage-report"; 120 | } 121 | 122 | rule bench_compile 123 | { 124 | local sources = [ glob $(TOP)/benchmark/*.cpp ] ; 125 | local rules = [ link $(sources) 126 | : # requirements 127 | $(toolset) 128 | @link_asan 129 | multi 130 | gcc:-march=native 131 | ] ; 132 | 133 | return $(rules) ; 134 | } 135 | 136 | alias bench : [ bench_compile ] ; 137 | explicit bench ; 138 | -------------------------------------------------------------------------------- /test/batch_deque_test.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | #include 13 | 14 | 15 | #define BOOST_TEST_MODULE batch_deque 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #define BOOST_DOUBLE_ENDED_TEST 25 | #include 26 | #undef BOOST_DOUBLE_ENDED_TEST 27 | 28 | using namespace boost::double_ended; 29 | 30 | #include "test_util.hpp" 31 | #include "test_elem.hpp" 32 | #include "input_iterator.hpp" 33 | 34 | template 35 | using make_batch_deque = batch_deque, std::allocator>; 36 | 37 | #if 1 38 | typedef boost::mpl::list< 39 | make_batch_deque, 40 | make_batch_deque, 41 | make_batch_deque, 42 | make_batch_deque, 43 | make_batch_deque, 44 | make_batch_deque 45 | > all_deques; 46 | #else 47 | typedef boost::mpl::list< 48 | //make_batch_deque 49 | //make_batch_deque 50 | > all_deques; 51 | #endif 52 | 53 | using t_is_default_constructible = if_t_is_default_constructible; 54 | using t_is_copy_constructible = if_t_is_copy_constructible; 55 | using t_is_trivial = if_t_is_trivial; 56 | 57 | template class NA> 58 | struct rebind_allocator 59 | { 60 | template struct apply; 61 | 62 | template 63 | struct apply> 64 | { 65 | using type = batch_deque>; 66 | }; 67 | }; 68 | 69 | template 70 | using and_allocs = typename boost::mpl::joint_view< 71 | Deques, 72 | typename boost::mpl::transform_view>::type 73 | >::type; 74 | 75 | // END HELPERS 76 | 77 | // TODO iterator operations 78 | 79 | BOOST_AUTO_TEST_CASE_TEMPLATE(segment_iterator, Deque, t_is_trivial) 80 | { 81 | typedef typename Deque::value_type T; 82 | 83 | devector expected = get_range>(); 84 | Deque a = get_range(); 85 | a.reserve_front(139); 86 | a.reserve_back(147); 87 | 88 | expected.pop_front(); 89 | expected.pop_front(); 90 | a.pop_front(); 91 | a.pop_front(); 92 | 93 | T* p_expected = expected.data(); 94 | 95 | auto a_first = a.segment_begin(); 96 | auto a_last = a.segment_end(); 97 | 98 | while (a_first != a_last) 99 | { 100 | auto size = a_first.data_size(); 101 | bool ok = std::memcmp(a_first.data(), p_expected, size) == 0; 102 | BOOST_TEST(ok); 103 | 104 | ++a_first; 105 | p_expected += size; 106 | } 107 | 108 | BOOST_TEST(p_expected == expected.data() + expected.size()); 109 | } 110 | 111 | BOOST_AUTO_TEST_CASE_TEMPLATE(consturctor_default, Deque, all_deques) 112 | { 113 | { 114 | Deque a; 115 | 116 | BOOST_TEST(a.size() == 0u); 117 | BOOST_TEST(a.empty()); 118 | } 119 | 120 | { 121 | typename Deque::allocator_type allocator; 122 | Deque b(allocator); 123 | 124 | BOOST_TEST(b.size() == 0u); 125 | BOOST_TEST(b.empty()); 126 | } 127 | } 128 | 129 | BOOST_AUTO_TEST_CASE_TEMPLATE(constructor_n_value, Deque, t_is_default_constructible) 130 | { 131 | typedef typename Deque::value_type T; 132 | 133 | { 134 | Deque a(0); 135 | BOOST_TEST(a.empty()); 136 | } 137 | 138 | { 139 | Deque b(18); 140 | BOOST_TEST(b.size() == 18u); 141 | 142 | const T tmp{}; 143 | 144 | for (auto&& elem : b) 145 | { 146 | BOOST_TEST(elem == tmp); 147 | } 148 | } 149 | 150 | { 151 | Deque b(8); 152 | BOOST_TEST(b.size() == 8u); 153 | 154 | const T tmp{}; 155 | 156 | for (auto&& elem : b) 157 | { 158 | BOOST_TEST(elem == tmp); 159 | } 160 | } 161 | 162 | if (! std::is_nothrow_constructible::value) 163 | { 164 | test_elem_throw::on_ctor_after(10); 165 | 166 | BOOST_CHECK_THROW(Deque a(12), test_exception); 167 | 168 | BOOST_TEST(test_elem_base::no_living_elem()); 169 | } 170 | } 171 | 172 | BOOST_AUTO_TEST_CASE_TEMPLATE(constructor_n_copy, Deque, t_is_copy_constructible) 173 | { 174 | typedef typename Deque::value_type T; 175 | 176 | { 177 | const T x(9); 178 | Deque a(0, x); 179 | BOOST_TEST(a.empty()); 180 | } 181 | 182 | { 183 | const T x(9); 184 | Deque b(18, x); 185 | BOOST_TEST(b.size() == 18u); 186 | 187 | for (auto&& elem : b) 188 | { 189 | BOOST_TEST(elem == x); 190 | } 191 | } 192 | 193 | { 194 | const T x(9); 195 | Deque b(8, x); 196 | BOOST_TEST(b.size() == 8u); 197 | 198 | for (auto&& elem : b) 199 | { 200 | BOOST_TEST(elem == x); 201 | } 202 | } 203 | 204 | if (! std::is_nothrow_copy_constructible::value) 205 | { 206 | test_elem_throw::on_copy_after(10); 207 | 208 | const T x(9); 209 | 210 | BOOST_CHECK_THROW(Deque a(12, x), test_exception); 211 | } 212 | 213 | BOOST_TEST(test_elem_base::no_living_elem()); 214 | } 215 | 216 | BOOST_AUTO_TEST_CASE_TEMPLATE(constructor_input_range, Deque, t_is_copy_constructible) 217 | { 218 | typedef typename Deque::value_type T; 219 | 220 | { 221 | const devector expected = get_range>(18); 222 | devector input = expected; 223 | 224 | auto input_begin = make_input_iterator(input, input.begin()); 225 | auto input_end = make_input_iterator(input, input.end()); 226 | 227 | Deque a(input_begin, input_end); 228 | BOOST_TEST(a == expected, boost::test_tools::per_element()); 229 | } 230 | 231 | { // empty range 232 | devector input; 233 | auto input_begin = make_input_iterator(input, input.begin()); 234 | 235 | Deque b(input_begin, input_begin); 236 | 237 | BOOST_TEST(b.empty()); 238 | } 239 | 240 | if (! std::is_nothrow_copy_constructible::value) 241 | { 242 | devector input = get_range>(18); 243 | 244 | auto input_begin = make_input_iterator(input, input.begin()); 245 | auto input_end = make_input_iterator(input, input.end()); 246 | 247 | test_elem_throw::on_copy_after(17); 248 | 249 | BOOST_CHECK_THROW(Deque c(input_begin, input_end), test_exception); 250 | } 251 | 252 | BOOST_TEST(test_elem_base::no_living_elem()); 253 | } 254 | 255 | BOOST_AUTO_TEST_CASE_TEMPLATE(constructor_forward_range, Deque, t_is_copy_constructible) 256 | { 257 | typedef typename Deque::value_type T; 258 | const std::forward_list x{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; 259 | 260 | { 261 | Deque a(x.begin(), x.end()); 262 | test_equal_range(a, x); 263 | } 264 | 265 | { 266 | Deque b(x.begin(), x.begin()); 267 | BOOST_TEST(b.empty()); 268 | } 269 | 270 | if (! std::is_nothrow_copy_constructible::value) 271 | { 272 | test_elem_throw::on_copy_after(10); 273 | 274 | BOOST_CHECK_THROW(Deque c(x.begin(), x.end()), test_exception); 275 | } 276 | } 277 | 278 | BOOST_AUTO_TEST_CASE_TEMPLATE(copy_constructor, Deque, t_is_copy_constructible) 279 | { 280 | typedef typename Deque::value_type T; 281 | 282 | { 283 | const Deque a; 284 | Deque b(a); 285 | 286 | BOOST_TEST(b.empty()); 287 | } 288 | 289 | { 290 | const Deque a = get_range(); 291 | Deque b(a); 292 | 293 | test_equal_range(a, b); 294 | } 295 | 296 | if (! std::is_nothrow_copy_constructible::value) 297 | { 298 | Deque a = get_range(); 299 | 300 | test_elem_throw::on_copy_after(12); 301 | 302 | BOOST_CHECK_THROW(Deque b(a), test_exception); 303 | } 304 | 305 | BOOST_TEST(test_elem_base::no_living_elem()); 306 | } 307 | 308 | BOOST_AUTO_TEST_CASE_TEMPLATE(move_constructor, Deque, all_deques) 309 | { 310 | { // empty 311 | Deque a; 312 | Deque b(std::move(a)); 313 | 314 | BOOST_TEST(a.empty()); 315 | BOOST_TEST(b.empty()); 316 | } 317 | 318 | { 319 | Deque a = get_range(1, 5, 5, 9); 320 | Deque b(std::move(a)); 321 | 322 | test_equal_range(b, get_range(1, 5, 5, 9)); 323 | 324 | // a is unspecified but valid state 325 | a.clear(); 326 | BOOST_TEST(a.empty()); 327 | } 328 | } 329 | 330 | BOOST_AUTO_TEST_CASE_TEMPLATE(move_constructor_allocator, Deque, and_allocs) 331 | { 332 | using Alloc = typename Deque::allocator_type; 333 | 334 | { // empty 335 | Deque a; 336 | Deque b(std::move(a), Alloc{}); 337 | 338 | BOOST_TEST(a.empty()); 339 | BOOST_TEST(b.empty()); 340 | } 341 | 342 | { 343 | Deque a = get_range(1, 5, 5, 9); 344 | Deque b(std::move(a), Alloc{}); 345 | 346 | test_equal_range(b, get_range(1, 5, 5, 9)); 347 | 348 | // a is unspecified but valid state 349 | a.clear(); 350 | BOOST_TEST(a.empty()); 351 | } 352 | } 353 | 354 | BOOST_AUTO_TEST_CASE_TEMPLATE(constructor_il, Deque, t_is_copy_constructible) 355 | { 356 | { 357 | Deque a({}); 358 | BOOST_TEST(a.empty()); 359 | } 360 | 361 | { 362 | Deque b{1, 2, 3, 4, 5, 6, 7, 8}; 363 | test_equal_range(b, get_range(8)); 364 | } 365 | } 366 | 367 | BOOST_AUTO_TEST_CASE_TEMPLATE(destructor, Deque, all_deques) 368 | { 369 | { Deque a; } 370 | { 371 | Deque b = get_range(); 372 | } 373 | } 374 | 375 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_copy_assign, Deque, and_allocs) 376 | { 377 | // assign to empty 378 | { 379 | Deque a; 380 | 381 | const Deque empty; 382 | a = empty; 383 | BOOST_TEST(a.empty()); 384 | 385 | const Deque input{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 386 | a = input; 387 | test_equal_range(a, {1,2,3,4,5,6,7,8,9,10,11,12}); 388 | } 389 | 390 | // input exceeds content 391 | { 392 | Deque b = get_range(64,68,68,74); 393 | Deque input{1,2,3,4,5,6,7,8,9,10,11,12}; 394 | 395 | b = input; 396 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12}); 397 | } 398 | 399 | // input fits content 400 | { 401 | Deque c = get_range(64,72,72,80); 402 | Deque input{1,2,3,4}; 403 | 404 | c = input; 405 | test_equal_range(c, {1,2,3,4}); 406 | } 407 | 408 | // input fits free front 409 | { 410 | Deque d = get_range(10,16,16,16); 411 | d.pop_front(); 412 | d.pop_front(); 413 | d.pop_front(); 414 | d.pop_front(); 415 | 416 | Deque input{1,2,3,4}; 417 | d = input; 418 | test_equal_range(d, {1,2,3,4}); 419 | } 420 | 421 | // throw while assign to free front 422 | typedef typename Deque::value_type T; 423 | 424 | bool can_throw = 425 | ! std::is_nothrow_move_constructible::value 426 | && ! std::is_nothrow_copy_constructible::value; 427 | 428 | if (can_throw) 429 | { 430 | Deque e = get_range(10); 431 | e.pop_front(); 432 | e.pop_front(); 433 | e.pop_front(); 434 | e.pop_front(); 435 | 436 | Deque input{1,2,3,4}; 437 | 438 | test_elem_throw::on_copy_after(2); 439 | test_elem_throw::on_move_after(2); 440 | 441 | BOOST_CHECK_THROW(e = input, test_exception); 442 | 443 | test_elem_throw::do_not_throw(); 444 | } 445 | 446 | BOOST_TEST(test_elem_base::no_living_elem()); 447 | } 448 | 449 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_move_assign, Deque, and_allocs) 450 | { 451 | // assign to empty 452 | { 453 | Deque a; 454 | Deque b; 455 | 456 | a = std::move(b); 457 | b.clear(); // b is in an unspecified but valid state 458 | 459 | BOOST_TEST(b.empty()); 460 | BOOST_TEST(a.empty()); 461 | } 462 | 463 | // assign smaller to larger 464 | { 465 | Deque a = get_range(16); 466 | Deque b = get_range(9); 467 | 468 | a = std::move(b); 469 | b.clear(); 470 | 471 | test_equal_range(a, {1,2,3,4,5,6,7,8,9}); 472 | BOOST_TEST(b.empty()); 473 | } 474 | 475 | // assign larger to smaller 476 | { 477 | Deque a = get_range(4); 478 | Deque b = get_range(9); 479 | 480 | a = std::move(b); 481 | b.clear(); 482 | 483 | test_equal_range(a, {1,2,3,4,5,6,7,8,9}); 484 | BOOST_TEST(b.empty()); 485 | } 486 | } 487 | 488 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_assign_il, Deque, t_is_copy_constructible) 489 | { 490 | typedef typename Deque::value_type T; 491 | 492 | // assign to empty 493 | { 494 | Deque a; 495 | 496 | a = {}; 497 | BOOST_TEST(a.empty()); 498 | 499 | a = {1,2,3,4,5,6,7,8,9,10,11,12}; 500 | test_equal_range(a, {1,2,3,4,5,6,7,8,9,10,11,12}); 501 | } 502 | 503 | // input exceeds content 504 | { 505 | Deque b = get_range(64,68,68,74); 506 | 507 | b = {1,2,3,4,5,6,7,8,9,10,11,12,13}; 508 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12,13}); 509 | } 510 | 511 | // input fits content 512 | { 513 | Deque c = get_range(64,72,72,80); 514 | 515 | c = {1,2,3,4,5}; 516 | test_equal_range(c, {1,2,3,4,5}); 517 | } 518 | 519 | // input fits free front 520 | { 521 | Deque d = get_range(10,16,16,16); 522 | d.pop_front(); 523 | d.pop_front(); 524 | d.pop_front(); 525 | d.pop_front(); 526 | 527 | d = {1,2,3,4}; 528 | test_equal_range(d, {1,2,3,4}); 529 | } 530 | 531 | // throw while assign to free front 532 | bool can_throw = 533 | ! std::is_nothrow_move_constructible::value 534 | && ! std::is_nothrow_copy_constructible::value; 535 | 536 | if (can_throw) 537 | { 538 | Deque e = get_range(10); 539 | e.pop_front(); 540 | e.pop_front(); 541 | e.pop_front(); 542 | e.pop_front(); 543 | 544 | test_elem_throw::on_copy_after(2); 545 | test_elem_throw::on_move_after(2); 546 | 547 | try 548 | { 549 | e = {404,404,404,404,404}; 550 | BOOST_TEST(false); 551 | } catch (const test_exception&) {} 552 | 553 | test_elem_throw::do_not_throw(); 554 | } 555 | 556 | BOOST_TEST(test_elem_base::no_living_elem()); 557 | } 558 | 559 | BOOST_AUTO_TEST_CASE_TEMPLATE(assign_input_range, Deque, t_is_copy_constructible) 560 | { 561 | typedef typename Deque::value_type T; 562 | 563 | // assign to empty 564 | { 565 | Deque a; 566 | devector input = get_range>(12); 567 | 568 | auto input_begin = make_input_iterator(input, input.begin()); 569 | auto input_end = make_input_iterator(input, input.end()); 570 | 571 | a.assign(input_begin, input_begin); 572 | BOOST_TEST(a.empty()); 573 | 574 | a.assign(input_begin, input_end); 575 | test_equal_range(a, {1,2,3,4,5,6,7,8,9,10,11,12}); 576 | } 577 | 578 | // input exceeds content 579 | { 580 | Deque b = get_range(64,68,68,74); 581 | devector input = get_range>(13); 582 | 583 | auto input_begin = make_input_iterator(input, input.begin()); 584 | auto input_end = make_input_iterator(input, input.end()); 585 | 586 | b.assign(input_begin, input_end); 587 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12,13}); 588 | } 589 | 590 | // input fits content 591 | { 592 | Deque c = get_range(64,72,72,80); 593 | devector input = get_range>(6); 594 | 595 | auto input_begin = make_input_iterator(input, input.begin()); 596 | auto input_end = make_input_iterator(input, input.end()); 597 | 598 | c.assign(input_begin, input_end); 599 | test_equal_range(c, {1,2,3,4,5,6}); 600 | } 601 | 602 | // input fits free front 603 | { 604 | Deque d = get_range(10,16,16,16); 605 | d.pop_front(); 606 | d.pop_front(); 607 | d.pop_front(); 608 | d.pop_front(); 609 | 610 | devector input = get_range>(3); 611 | 612 | auto input_begin = make_input_iterator(input, input.begin()); 613 | auto input_end = make_input_iterator(input, input.end()); 614 | 615 | d.assign(input_begin, input_end); 616 | test_equal_range(d, {1,2,3}); 617 | } 618 | 619 | // throw while assign to free front 620 | bool can_throw = 621 | ! std::is_nothrow_move_constructible::value 622 | && ! std::is_nothrow_copy_constructible::value; 623 | 624 | if (can_throw) 625 | { 626 | Deque e = get_range(10); 627 | e.pop_front(); 628 | e.pop_front(); 629 | e.pop_front(); 630 | e.pop_front(); 631 | 632 | devector input = get_range>(3); 633 | 634 | auto input_begin = make_input_iterator(input, input.begin()); 635 | auto input_end = make_input_iterator(input, input.end()); 636 | 637 | test_elem_throw::on_copy_after(2); 638 | test_elem_throw::on_move_after(2); 639 | 640 | BOOST_CHECK_THROW(e.assign(input_begin, input_end), test_exception); 641 | 642 | test_elem_throw::do_not_throw(); 643 | } 644 | 645 | BOOST_TEST(test_elem_base::no_living_elem()); 646 | } 647 | 648 | BOOST_AUTO_TEST_CASE_TEMPLATE(assign_forward_range, Deque, t_is_copy_constructible) 649 | { 650 | typedef typename Deque::value_type T; 651 | 652 | { // scope of input 653 | 654 | const std::forward_list input{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 655 | auto input_begin = input.begin(); 656 | auto input_mid = input.begin(); 657 | auto input_end = input.end(); 658 | 659 | std::advance(input_mid, 4); 660 | 661 | // assign to empty 662 | { 663 | Deque a; 664 | 665 | a.assign(input_begin, input_begin); 666 | BOOST_TEST(a.empty()); 667 | 668 | a.assign(input_begin, input_end); 669 | test_equal_range(a, {1,2,3,4,5,6,7,8,9,10,11,12}); 670 | } 671 | 672 | // input exceeds content 673 | { 674 | Deque b = get_range(64,68,68,74); 675 | 676 | b.assign(input_begin, input_end); 677 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12}); 678 | } 679 | 680 | // input fits content 681 | { 682 | Deque c = get_range(64,72,72,80); 683 | 684 | c.assign(input_begin, input_mid); 685 | test_equal_range(c, {1,2,3,4}); 686 | } 687 | 688 | // input fits free front 689 | { 690 | Deque d = get_range(10,16,16,16); 691 | d.pop_front(); 692 | d.pop_front(); 693 | d.pop_front(); 694 | d.pop_front(); 695 | 696 | d.assign(input_begin, input_mid); 697 | test_equal_range(d, {1,2,3,4}); 698 | } 699 | 700 | // throw while assign to free front 701 | bool can_throw = 702 | ! std::is_nothrow_move_constructible::value 703 | && ! std::is_nothrow_copy_constructible::value; 704 | 705 | if (can_throw) 706 | { 707 | Deque e = get_range(10); 708 | e.pop_front(); 709 | e.pop_front(); 710 | e.pop_front(); 711 | e.pop_front(); 712 | 713 | test_elem_throw::on_copy_after(2); 714 | test_elem_throw::on_move_after(2); 715 | 716 | BOOST_CHECK_THROW(e.assign(input_begin, input_end), test_exception); 717 | 718 | test_elem_throw::do_not_throw(); 719 | } 720 | 721 | } // end scope of input 722 | 723 | BOOST_TEST(test_elem_base::no_living_elem()); 724 | } 725 | 726 | BOOST_AUTO_TEST_CASE_TEMPLATE(assign_n_copy, Deque, t_is_copy_constructible) 727 | { 728 | typedef typename Deque::value_type T; 729 | 730 | { // scope of input 731 | 732 | const T input(9); 733 | 734 | // assign to empty 735 | { 736 | Deque a; 737 | 738 | a.assign(0, input); 739 | BOOST_TEST(a.empty()); 740 | 741 | a.assign(12, input); 742 | test_equal_range(a, {9,9,9,9,9,9,9,9,9,9,9,9}); 743 | } 744 | 745 | // input exceeds content 746 | { 747 | Deque b = get_range(64,68,68,74); 748 | 749 | b.assign(12, input); 750 | test_equal_range(b, {9,9,9,9,9,9,9,9,9,9,9,9}); 751 | } 752 | 753 | // input fits content 754 | { 755 | Deque c = get_range(64,72,72,80); 756 | 757 | c.assign(4, input); 758 | test_equal_range(c, {9,9,9,9}); 759 | } 760 | 761 | // input fits free front 762 | { 763 | Deque d = get_range(10,16,16,16); 764 | d.pop_front(); 765 | d.pop_front(); 766 | d.pop_front(); 767 | d.pop_front(); 768 | 769 | d.assign(4, input); 770 | test_equal_range(d, {9,9,9,9}); 771 | } 772 | 773 | // throw while assign to free front 774 | bool can_throw = 775 | ! std::is_nothrow_move_constructible::value 776 | && ! std::is_nothrow_copy_constructible::value; 777 | 778 | if (can_throw) 779 | { 780 | Deque e = get_range(10); 781 | e.pop_front(); 782 | e.pop_front(); 783 | e.pop_front(); 784 | e.pop_front(); 785 | 786 | test_elem_throw::on_copy_after(2); 787 | test_elem_throw::on_move_after(2); 788 | 789 | BOOST_CHECK_THROW(e.assign(10, input), test_exception); 790 | 791 | test_elem_throw::do_not_throw(); 792 | } 793 | 794 | } // end scope of input 795 | 796 | BOOST_TEST(test_elem_base::no_living_elem()); 797 | } 798 | 799 | BOOST_AUTO_TEST_CASE_TEMPLATE(assign_il, Deque, t_is_copy_constructible) 800 | { 801 | typedef typename Deque::value_type T; 802 | 803 | // assign to empty 804 | { 805 | Deque a; 806 | 807 | a.assign({}); 808 | BOOST_TEST(a.empty()); 809 | 810 | a.assign({1,2,3,4,5,6,7,8,9,10,11,12}); 811 | test_equal_range(a, {1,2,3,4,5,6,7,8,9,10,11,12}); 812 | } 813 | 814 | // input exceeds content 815 | { 816 | Deque b = get_range(64,68,68,74); 817 | 818 | b.assign({1,2,3,4,5,6,7,8,9,10,11,12,13}); 819 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12,13}); 820 | } 821 | 822 | // input fits content 823 | { 824 | Deque c = get_range(64,72,72,80); 825 | 826 | c.assign({1,2,3,4,5}); 827 | test_equal_range(c, {1,2,3,4,5}); 828 | } 829 | 830 | // input fits free front 831 | { 832 | Deque d = get_range(10,16,16,16); 833 | d.pop_front(); 834 | d.pop_front(); 835 | d.pop_front(); 836 | d.pop_front(); 837 | 838 | d.assign({1,2,3,4}); 839 | test_equal_range(d, {1,2,3,4}); 840 | } 841 | 842 | // throw while assign to free front 843 | bool can_throw = 844 | ! std::is_nothrow_move_constructible::value 845 | && ! std::is_nothrow_copy_constructible::value; 846 | 847 | if (can_throw) 848 | { 849 | Deque e = get_range(10); 850 | e.pop_front(); 851 | e.pop_front(); 852 | e.pop_front(); 853 | e.pop_front(); 854 | 855 | test_elem_throw::on_copy_after(2); 856 | test_elem_throw::on_move_after(2); 857 | 858 | BOOST_CHECK_THROW(e.assign({404,404,404,404,404}), test_exception); 859 | 860 | test_elem_throw::do_not_throw(); 861 | } 862 | 863 | BOOST_TEST(test_elem_base::no_living_elem()); 864 | } 865 | 866 | BOOST_AUTO_TEST_CASE_TEMPLATE(get_allocator, Deque, all_deques) 867 | { 868 | Deque a; 869 | (void)a.get_allocator(); 870 | } 871 | 872 | template 873 | void test_begin_end_impl() 874 | { 875 | { 876 | Deque a; 877 | BOOST_TEST(a.begin() == a.end()); 878 | } 879 | 880 | { 881 | Deque b = get_range(1, 13, 13, 25); 882 | auto expected = get_range>(24); 883 | 884 | BOOST_TEST(boost::algorithm::equal( 885 | b.begin(), b.end(), 886 | expected.begin(), expected.end() 887 | )); 888 | } 889 | } 890 | 891 | BOOST_AUTO_TEST_CASE_TEMPLATE(begin_end, Deque, all_deques) 892 | { 893 | test_begin_end_impl< Deque, Deque>(); 894 | test_begin_end_impl(); 895 | } 896 | 897 | template 898 | void test_rbegin_rend_impl() 899 | { 900 | { 901 | Deque a; 902 | BOOST_TEST(a.rbegin() == a.rend()); 903 | } 904 | 905 | { 906 | Deque b = get_range(1, 13, 13, 25); 907 | auto expected = get_range>(24); 908 | 909 | BOOST_TEST(boost::algorithm::equal( 910 | b.rbegin(), b.rend(), 911 | expected.rbegin(), expected.rend() 912 | )); 913 | } 914 | } 915 | 916 | BOOST_AUTO_TEST_CASE_TEMPLATE(rbegin_rend, Deque, all_deques) 917 | { 918 | test_rbegin_rend_impl< Deque, Deque>(); 919 | test_rbegin_rend_impl(); 920 | } 921 | 922 | BOOST_AUTO_TEST_CASE_TEMPLATE(cbegin_cend, Deque, all_deques) 923 | { 924 | { 925 | Deque a; 926 | BOOST_TEST(a.cbegin() == a.cend()); 927 | } 928 | 929 | { 930 | Deque b = get_range(1, 13, 13, 25); 931 | auto expected = get_range>(24); 932 | 933 | BOOST_TEST(boost::algorithm::equal( 934 | b.cbegin(), b.cend(), 935 | expected.cbegin(), expected.cend() 936 | )); 937 | } 938 | } 939 | 940 | BOOST_AUTO_TEST_CASE_TEMPLATE(crbegin_crend, Deque, all_deques) 941 | { 942 | { 943 | Deque a; 944 | BOOST_TEST(a.crbegin() == a.crend()); 945 | } 946 | 947 | { 948 | Deque b = get_range(1, 13, 13, 25); 949 | auto expected = get_range>(24); 950 | 951 | BOOST_TEST(boost::algorithm::equal( 952 | b.crbegin(), b.crend(), 953 | expected.crbegin(), expected.crend() 954 | )); 955 | } 956 | } 957 | 958 | BOOST_AUTO_TEST_CASE_TEMPLATE(empty, Deque, all_deques) 959 | { 960 | Deque a; 961 | BOOST_TEST(a.empty()); 962 | 963 | a.emplace_front(1); 964 | 965 | BOOST_TEST(! a.empty()); 966 | } 967 | 968 | BOOST_AUTO_TEST_CASE_TEMPLATE(size, Deque, all_deques) 969 | { 970 | Deque a; 971 | BOOST_TEST(a.size() == 0u); 972 | 973 | a.emplace_front(1); 974 | a.emplace_front(2); 975 | a.emplace_front(3); 976 | 977 | BOOST_TEST(a.size() == 3u); 978 | 979 | a.pop_front(); 980 | a.pop_front(); 981 | 982 | BOOST_TEST(a.size() == 1u); 983 | 984 | a.emplace_back(2); 985 | a.emplace_back(3); 986 | a.emplace_back(4); 987 | a.emplace_back(5); 988 | a.emplace_back(6); 989 | 990 | BOOST_TEST(a.size() == 6u); 991 | 992 | a.emplace_back(7); 993 | a.emplace_back(8); 994 | 995 | a.emplace_back(9); 996 | a.emplace_back(10); 997 | a.emplace_back(11); 998 | 999 | BOOST_TEST(a.size() == 11u); 1000 | 1001 | Deque b = get_range(1,9,0,0); 1002 | BOOST_TEST(b.size() == 8u); 1003 | } 1004 | 1005 | BOOST_AUTO_TEST_CASE_TEMPLATE(max_size, Deque, all_deques) 1006 | { 1007 | Deque a; 1008 | (void)a.max_size(); 1009 | } 1010 | 1011 | BOOST_AUTO_TEST_CASE_TEMPLATE(capacity, Deque, all_deques) 1012 | { 1013 | { 1014 | Deque a; 1015 | BOOST_TEST(a.capacity() == 0); 1016 | } 1017 | 1018 | { 1019 | Deque b = get_range(20); 1020 | BOOST_TEST(b.capacity() >= 20u); 1021 | } 1022 | } 1023 | 1024 | BOOST_AUTO_TEST_CASE_TEMPLATE(resize_front_value, Deque, t_is_default_constructible) 1025 | { 1026 | typedef typename Deque::value_type T; 1027 | 1028 | { 1029 | Deque a; 1030 | a.resize_front(0); 1031 | BOOST_TEST(a.empty()); 1032 | 1033 | a.emplace_front(1); 1034 | a.resize_front(10); 1035 | BOOST_TEST(a.size() == 10u); 1036 | test_equal_range(a, {0,0,0,0,0,0,0,0,0,1}); 1037 | 1038 | a.resize_front(10); 1039 | BOOST_TEST(a.size() == 10u); 1040 | test_equal_range(a, {0,0,0,0,0,0,0,0,0,1}); 1041 | 1042 | a.resize_front(5); 1043 | BOOST_TEST(a.size() == 5u); 1044 | test_equal_range(a, {0,0,0,0,1}); 1045 | 1046 | a.resize_front(0); 1047 | BOOST_TEST(a.empty()); 1048 | } 1049 | 1050 | { 1051 | Deque b = get_range(10); 1052 | b.reserve_front(48); 1053 | b.resize_front(5); 1054 | test_equal_range(b, {6, 7, 8, 9, 10}); 1055 | } 1056 | 1057 | if (! std::is_nothrow_constructible::value) 1058 | { 1059 | Deque c = get_range(5); 1060 | test_elem_throw::on_ctor_after(3); 1061 | 1062 | BOOST_CHECK_THROW(c.resize_front(256), test_exception); 1063 | 1064 | test_equal_range(c, {1, 2, 3, 4, 5}); 1065 | } 1066 | } 1067 | 1068 | BOOST_AUTO_TEST_CASE_TEMPLATE(resize_front_copy, Deque, t_is_copy_constructible) 1069 | { 1070 | typedef typename Deque::value_type T; 1071 | const T x(9); 1072 | 1073 | { 1074 | Deque a; 1075 | a.resize_front(0, x); 1076 | BOOST_TEST(a.empty()); 1077 | 1078 | a.emplace_front(1); 1079 | a.resize_front(10, x); 1080 | BOOST_TEST(a.size() == 10u); 1081 | test_equal_range(a, {9,9,9,9,9,9,9,9,9,1}); 1082 | 1083 | a.resize_front(10, x); 1084 | BOOST_TEST(a.size() == 10u); 1085 | test_equal_range(a, {9,9,9,9,9,9,9,9,9,1}); 1086 | 1087 | a.resize_front(5, x); 1088 | BOOST_TEST(a.size() == 5u); 1089 | test_equal_range(a, {9,9,9,9,1}); 1090 | 1091 | a.resize_front(0, x); 1092 | BOOST_TEST(a.empty()); 1093 | } 1094 | 1095 | { 1096 | Deque b = get_range(10); 1097 | b.reserve_front(48); 1098 | b.resize_front(5, x); 1099 | test_equal_range(b, {6, 7, 8, 9, 10}); 1100 | } 1101 | 1102 | if (! std::is_nothrow_copy_constructible::value) 1103 | { 1104 | Deque c = get_range(5); 1105 | test_elem_throw::on_copy_after(14); 1106 | 1107 | BOOST_CHECK_THROW(c.resize_front(256, x), test_exception); 1108 | 1109 | test_equal_range(c, {1, 2, 3, 4, 5}); 1110 | } 1111 | } 1112 | 1113 | BOOST_AUTO_TEST_CASE_TEMPLATE(resize_value, Deque, t_is_default_constructible) 1114 | { 1115 | typedef typename Deque::value_type T; 1116 | 1117 | { 1118 | Deque a; 1119 | a.resize(0); 1120 | BOOST_TEST(a.empty()); 1121 | 1122 | a.emplace_back(1); 1123 | a.resize(10); 1124 | BOOST_TEST(a.size() == 10u); 1125 | test_equal_range(a, {1,0,0,0,0,0,0,0,0,0}); 1126 | 1127 | a.resize(10); 1128 | BOOST_TEST(a.size() == 10u); 1129 | test_equal_range(a, {1,0,0,0,0,0,0,0,0,0}); 1130 | 1131 | a.resize(5); 1132 | BOOST_TEST(a.size() == 5u); 1133 | test_equal_range(a, {1,0,0,0,0}); 1134 | 1135 | a.resize(0); 1136 | BOOST_TEST(a.empty()); 1137 | } 1138 | 1139 | { 1140 | Deque b = get_range(10); 1141 | b.reserve_back(48); 1142 | b.resize_back(5); 1143 | test_equal_range(b, {1, 2, 3, 4, 5}); 1144 | } 1145 | 1146 | if (! std::is_nothrow_constructible::value) 1147 | { 1148 | Deque c = get_range(5); 1149 | test_elem_throw::on_ctor_after(3); 1150 | 1151 | BOOST_CHECK_THROW(c.resize(256), test_exception); 1152 | 1153 | test_equal_range(c, {1, 2, 3, 4, 5}); 1154 | } 1155 | } 1156 | 1157 | BOOST_AUTO_TEST_CASE_TEMPLATE(resize_copy, Deque, t_is_copy_constructible) 1158 | { 1159 | typedef typename Deque::value_type T; 1160 | const T x(9); 1161 | 1162 | { 1163 | Deque a; 1164 | a.resize(0, x); 1165 | BOOST_TEST(a.empty()); 1166 | 1167 | a.emplace_back(1); 1168 | a.resize(10, x); 1169 | BOOST_TEST(a.size() == 10u); 1170 | test_equal_range(a, {1,9,9,9,9,9,9,9,9,9}); 1171 | 1172 | a.resize(10, x); 1173 | BOOST_TEST(a.size() == 10u); 1174 | test_equal_range(a, {1,9,9,9,9,9,9,9,9,9}); 1175 | 1176 | a.resize(5, x); 1177 | BOOST_TEST(a.size() == 5u); 1178 | test_equal_range(a, {1,9,9,9,9}); 1179 | 1180 | a.resize(0, x); 1181 | BOOST_TEST(a.empty()); 1182 | } 1183 | 1184 | { 1185 | Deque b = get_range(10); 1186 | b.reserve_back(48); 1187 | b.resize_back(5, x); 1188 | test_equal_range(b, {1, 2, 3, 4, 5}); 1189 | } 1190 | 1191 | if (! std::is_nothrow_copy_constructible::value) 1192 | { 1193 | Deque c = get_range(5); 1194 | test_elem_throw::on_copy_after(14); 1195 | 1196 | BOOST_CHECK_THROW(c.resize(256, T(404)), test_exception); 1197 | 1198 | test_equal_range(c, {1, 2, 3, 4, 5}); 1199 | } 1200 | } 1201 | 1202 | BOOST_AUTO_TEST_CASE_TEMPLATE(reserve_front, Deque, all_deques) 1203 | { 1204 | Deque a; 1205 | 1206 | a.reserve_front(100); 1207 | for (unsigned i = 0; i < 100u; ++i) 1208 | { 1209 | a.push_front(i); 1210 | } 1211 | 1212 | Deque b; 1213 | b.reserve_front(4); 1214 | b.reserve_front(6); 1215 | b.reserve_front(4); 1216 | b.reserve_front(8); 1217 | b.reserve_front(16); 1218 | } 1219 | 1220 | BOOST_AUTO_TEST_CASE_TEMPLATE(reserve_back, Deque, all_deques) 1221 | { 1222 | Deque a; 1223 | 1224 | a.reserve_back(100); 1225 | for (unsigned i = 0; i < 100; ++i) 1226 | { 1227 | a.push_back(i); 1228 | } 1229 | 1230 | Deque b; 1231 | b.reserve_back(4); 1232 | b.reserve_back(6); 1233 | b.reserve_back(4); 1234 | b.reserve_back(8); 1235 | b.reserve_back(16); 1236 | } 1237 | 1238 | BOOST_AUTO_TEST_CASE_TEMPLATE(shrink_to_fit, Deque, all_deques) 1239 | { 1240 | { 1241 | Deque a; 1242 | a.shrink_to_fit(); 1243 | BOOST_TEST(a.front_free_capacity() == 0u); 1244 | 1245 | a.emplace_front(1); 1246 | a.pop_front(); 1247 | a.shrink_to_fit(); 1248 | BOOST_TEST(a.front_free_capacity() == 0u); 1249 | 1250 | a.emplace_front(1); 1251 | a.shrink_to_fit(); 1252 | test_equal_range(a, {1}); 1253 | 1254 | const auto min_cap = a.front_free_capacity(); 1255 | a.reserve_front(123); 1256 | a.shrink_to_fit(); 1257 | BOOST_TEST(a.front_free_capacity() == min_cap); 1258 | test_equal_range(a, {1}); 1259 | } 1260 | 1261 | { 1262 | Deque b; 1263 | b.shrink_to_fit(); 1264 | BOOST_TEST(b.back_free_capacity() == 0u); 1265 | 1266 | b.emplace_back(1); 1267 | b.pop_back(); 1268 | b.shrink_to_fit(); 1269 | BOOST_TEST(b.back_free_capacity() == 0u); 1270 | 1271 | b.emplace_back(1); 1272 | b.shrink_to_fit(); 1273 | test_equal_range(b, {1}); 1274 | 1275 | const auto min_cap = b.back_free_capacity(); 1276 | b.reserve_back(123); 1277 | b.shrink_to_fit(); 1278 | BOOST_TEST(b.back_free_capacity() == min_cap); 1279 | test_equal_range(b, {1}); 1280 | } 1281 | 1282 | { 1283 | Deque c; 1284 | 1285 | c.emplace_back(2); 1286 | c.emplace_front(1); 1287 | c.pop_back(); 1288 | c.pop_front(); 1289 | c.shrink_to_fit(); 1290 | BOOST_TEST(c.back_free_capacity() == 0u); 1291 | 1292 | c.emplace_back(2); 1293 | c.emplace_front(1); 1294 | c.shrink_to_fit(); 1295 | test_equal_range(c, {1, 2}); 1296 | 1297 | const auto min_front_cap = c.front_free_capacity(); 1298 | const auto min_back_cap = c.back_free_capacity(); 1299 | c.reserve_back(123); 1300 | c.reserve_front(123); 1301 | 1302 | c.shrink_to_fit(); 1303 | 1304 | BOOST_TEST(c.front_free_capacity() == min_front_cap); 1305 | BOOST_TEST(c.back_free_capacity() == min_back_cap); 1306 | test_equal_range(c, {1, 2}); 1307 | } 1308 | } 1309 | 1310 | template 1311 | void test_op_at_impl() 1312 | { 1313 | typedef typename Deque::value_type T; 1314 | 1315 | MutableDeque a = get_range(26); 1316 | 1317 | a.pop_front(); 1318 | a.pop_front(); 1319 | 1320 | Deque b(std::move(a)); 1321 | 1322 | BOOST_TEST(b[0] == T(3)); 1323 | BOOST_TEST(b[8] == T(11)); 1324 | BOOST_TEST(b[14] == T(17)); 1325 | BOOST_TEST(b[23] == T(26)); 1326 | } 1327 | 1328 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_at, Deque, all_deques) 1329 | { 1330 | test_op_at_impl< Deque, Deque>(); 1331 | test_op_at_impl(); 1332 | } 1333 | 1334 | template 1335 | void test_at_impl() 1336 | { 1337 | typedef typename Deque::value_type T; 1338 | 1339 | MutableDeque a = get_range(26); 1340 | 1341 | a.pop_front(); 1342 | a.pop_front(); 1343 | 1344 | Deque b(std::move(a)); 1345 | 1346 | BOOST_TEST(b.at(0) == T(3)); 1347 | BOOST_TEST(b.at(8) == T(11)); 1348 | BOOST_TEST(b.at(14) == T(17)); 1349 | BOOST_TEST(b.at(23) == T(26)); 1350 | 1351 | BOOST_CHECK_THROW(b.at(24), std::out_of_range); 1352 | } 1353 | 1354 | BOOST_AUTO_TEST_CASE_TEMPLATE(at, Deque, all_deques) 1355 | { 1356 | test_at_impl< Deque, Deque>(); 1357 | test_at_impl(); 1358 | } 1359 | 1360 | BOOST_AUTO_TEST_CASE_TEMPLATE(front, Deque, all_deques) 1361 | { 1362 | typedef typename Deque::value_type T; 1363 | 1364 | { // non-const front 1365 | Deque a = get_range(3); 1366 | BOOST_TEST(a.front() == T(1)); 1367 | a.front() = T(100); 1368 | BOOST_TEST(a.front() == T(100)); 1369 | } 1370 | 1371 | { // const front 1372 | const Deque a = get_range(3); 1373 | BOOST_TEST(a.front() == T(1)); 1374 | } 1375 | } 1376 | 1377 | BOOST_AUTO_TEST_CASE_TEMPLATE(back, Deque, all_deques) 1378 | { 1379 | typedef typename Deque::value_type T; 1380 | 1381 | { // non-const back 1382 | Deque a = get_range(3); 1383 | BOOST_TEST(a.back() == T(3)); 1384 | a.back() = T(100); 1385 | BOOST_TEST(a.back() == T(100)); 1386 | } 1387 | 1388 | { // const back 1389 | const Deque a = get_range(3); 1390 | BOOST_TEST(a.back() == T(3)); 1391 | } 1392 | 1393 | { // at segment boundary 1394 | Deque a = get_range(8); 1395 | BOOST_TEST(a.back() == T(8)); 1396 | a.push_back(T(9)); 1397 | BOOST_TEST(a.back() == T(9)); 1398 | } 1399 | } 1400 | 1401 | BOOST_AUTO_TEST_CASE_TEMPLATE(emplace_front, Deque, all_deques) 1402 | { 1403 | typedef typename Deque::value_type T; 1404 | 1405 | { 1406 | Deque a; 1407 | 1408 | a.emplace_front(3); 1409 | a.emplace_front(2); 1410 | a.emplace_front(1); 1411 | 1412 | test_equal_range(a, {1, 2, 3}); 1413 | } 1414 | 1415 | if (! std::is_nothrow_constructible::value) 1416 | { 1417 | Deque b = get_range(8); 1418 | 1419 | test_elem_throw::on_ctor_after(1); 1420 | 1421 | BOOST_CHECK_THROW(b.emplace_front(404), test_exception); 1422 | 1423 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8}); 1424 | } 1425 | } 1426 | 1427 | BOOST_AUTO_TEST_CASE_TEMPLATE(push_front_copy, Deque, t_is_copy_constructible) 1428 | { 1429 | typedef typename Deque::value_type T; 1430 | 1431 | { 1432 | Deque a; 1433 | 1434 | for (std::size_t i = 1; i <= 12; ++i) 1435 | { 1436 | const T elem(i); 1437 | a.push_front(elem); 1438 | } 1439 | 1440 | test_equal_range(a, {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); 1441 | } 1442 | 1443 | if (! std::is_nothrow_copy_constructible::value) 1444 | { 1445 | Deque b = get_range(10); 1446 | 1447 | const T elem(404); 1448 | test_elem_throw::on_copy_after(1); 1449 | 1450 | BOOST_CHECK_THROW(b.push_front(elem), test_exception); 1451 | 1452 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 1453 | } 1454 | } 1455 | 1456 | BOOST_AUTO_TEST_CASE_TEMPLATE(push_front_rvalue, Deque, all_deques) 1457 | { 1458 | typedef typename Deque::value_type T; 1459 | 1460 | { 1461 | Deque a; 1462 | 1463 | for (std::size_t i = 1; i <= 12; ++i) 1464 | { 1465 | T elem(i); 1466 | a.push_front(std::move(elem)); 1467 | } 1468 | 1469 | test_equal_range(a, {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); 1470 | } 1471 | 1472 | if (! std::is_nothrow_move_constructible::value) 1473 | { 1474 | Deque b = get_range(8); 1475 | 1476 | test_elem_throw::on_move_after(1); 1477 | 1478 | BOOST_CHECK_THROW(b.push_front(T(404)), test_exception); 1479 | 1480 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8}); 1481 | } 1482 | } 1483 | 1484 | BOOST_AUTO_TEST_CASE_TEMPLATE(unsafe_push_front_copy, Deque, t_is_copy_constructible) 1485 | { 1486 | typedef typename Deque::value_type T; 1487 | 1488 | { 1489 | Deque a; 1490 | a.reserve_front(12); 1491 | 1492 | for (int i = 1; i <= 12; ++i) 1493 | { 1494 | const T elem(i); 1495 | a.unsafe_push_front(elem); 1496 | } 1497 | 1498 | test_equal_range(a, {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); 1499 | } 1500 | 1501 | if (! std::is_nothrow_copy_constructible::value) 1502 | { 1503 | Deque b = get_range(10); 1504 | b.reserve_front(11); 1505 | 1506 | const T elem(404); 1507 | test_elem_throw::on_copy_after(1); 1508 | 1509 | BOOST_CHECK_THROW(b.unsafe_push_front(elem), test_exception); 1510 | 1511 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 1512 | } 1513 | } 1514 | 1515 | BOOST_AUTO_TEST_CASE_TEMPLATE(unsafe_push_front_rvalue, Deque, all_deques) 1516 | { 1517 | typedef typename Deque::value_type T; 1518 | 1519 | { 1520 | Deque a; 1521 | a.reserve_front(12); 1522 | 1523 | for (int i = 1; i <= 12; ++i) 1524 | { 1525 | T elem(i); 1526 | a.unsafe_push_front(std::move(elem)); 1527 | } 1528 | 1529 | test_equal_range(a, {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); 1530 | } 1531 | 1532 | if (! std::is_nothrow_move_constructible::value) 1533 | { 1534 | Deque b = get_range(8); 1535 | b.reserve_front(9); 1536 | 1537 | test_elem_throw::on_move_after(1); 1538 | 1539 | BOOST_CHECK_THROW(b.unsafe_push_front(T(404)), test_exception); 1540 | 1541 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8}); 1542 | } 1543 | } 1544 | 1545 | BOOST_AUTO_TEST_CASE_TEMPLATE(emplace_back, Deque, all_deques) 1546 | { 1547 | typedef typename Deque::value_type T; 1548 | 1549 | { 1550 | Deque a; 1551 | 1552 | a.emplace_back(1); 1553 | a.emplace_back(2); 1554 | a.emplace_back(3); 1555 | 1556 | test_equal_range(a, {1, 2, 3}); 1557 | } 1558 | 1559 | if (! std::is_nothrow_constructible::value) 1560 | { 1561 | Deque b = get_range(8); 1562 | 1563 | test_elem_throw::on_ctor_after(1); 1564 | 1565 | BOOST_CHECK_THROW(b.emplace_back(404), test_exception); 1566 | 1567 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8}); 1568 | } 1569 | } 1570 | 1571 | BOOST_AUTO_TEST_CASE_TEMPLATE(push_back_copy, Deque, t_is_copy_constructible) 1572 | { 1573 | typedef typename Deque::value_type T; 1574 | 1575 | { 1576 | Deque a; 1577 | 1578 | for (std::size_t i = 1; i <= 12; ++i) 1579 | { 1580 | const T elem(i); 1581 | a.push_back(elem); 1582 | } 1583 | 1584 | test_equal_range(a, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}); 1585 | } 1586 | 1587 | if (! std::is_nothrow_copy_constructible::value) 1588 | { 1589 | Deque b = get_range(10); 1590 | 1591 | const T elem(404); 1592 | test_elem_throw::on_copy_after(1); 1593 | 1594 | BOOST_CHECK_THROW(b.push_back(elem), test_exception); 1595 | 1596 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 1597 | } 1598 | } 1599 | 1600 | BOOST_AUTO_TEST_CASE_TEMPLATE(push_back_rvalue, Deque, all_deques) 1601 | { 1602 | typedef typename Deque::value_type T; 1603 | 1604 | { 1605 | Deque a; 1606 | 1607 | for (std::size_t i = 1; i <= 12; ++i) 1608 | { 1609 | T elem(i); 1610 | a.push_back(std::move(elem)); 1611 | } 1612 | 1613 | test_equal_range(a, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}); 1614 | } 1615 | 1616 | if (! std::is_nothrow_move_constructible::value) 1617 | { 1618 | Deque b = get_range(8); 1619 | 1620 | test_elem_throw::on_move_after(1); 1621 | 1622 | BOOST_CHECK_THROW(b.push_back(T(404)), test_exception); 1623 | 1624 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8}); 1625 | } 1626 | } 1627 | 1628 | BOOST_AUTO_TEST_CASE_TEMPLATE(unsafe_push_back_copy, Deque, t_is_copy_constructible) 1629 | { 1630 | typedef typename Deque::value_type T; 1631 | 1632 | { 1633 | Deque a; 1634 | a.reserve_back(12); 1635 | 1636 | for (int i = 1; i <= 12; ++i) 1637 | { 1638 | const T elem(i); 1639 | a.unsafe_push_back(elem); 1640 | } 1641 | 1642 | test_equal_range(a, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}); 1643 | } 1644 | 1645 | if (! std::is_nothrow_copy_constructible::value) 1646 | { 1647 | Deque b = get_range(10); 1648 | b.reserve_back(11); 1649 | 1650 | const T elem(404); 1651 | test_elem_throw::on_copy_after(1); 1652 | 1653 | BOOST_CHECK_THROW(b.unsafe_push_back(elem), test_exception); 1654 | 1655 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 1656 | } 1657 | } 1658 | 1659 | BOOST_AUTO_TEST_CASE_TEMPLATE(unsafe_push_back_rvalue, Deque, all_deques) 1660 | { 1661 | typedef typename Deque::value_type T; 1662 | 1663 | { 1664 | Deque a; 1665 | a.reserve_back(12); 1666 | 1667 | for (std::size_t i = 1; i <= 12; ++i) 1668 | { 1669 | T elem(i); 1670 | a.unsafe_push_back(std::move(elem)); 1671 | } 1672 | 1673 | test_equal_range(a, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}); 1674 | } 1675 | 1676 | if (! std::is_nothrow_move_constructible::value) 1677 | { 1678 | Deque b = get_range(8); 1679 | b.reserve_back(9); 1680 | 1681 | test_elem_throw::on_move_after(1); 1682 | 1683 | BOOST_CHECK_THROW(b.unsafe_push_back(T(404)), test_exception); 1684 | 1685 | test_equal_range(b, {1, 2, 3, 4, 5, 6, 7, 8}); 1686 | } 1687 | } 1688 | 1689 | BOOST_AUTO_TEST_CASE_TEMPLATE(emplace, Deque, all_deques) 1690 | { 1691 | typedef typename Deque::value_type T; 1692 | 1693 | // emplace to empty begin 1694 | { 1695 | Deque a; 1696 | auto res = a.emplace(a.begin(), 1); 1697 | test_equal_range(a, {1}); 1698 | BOOST_TEST(*res == T(1)); 1699 | } 1700 | 1701 | // to empty end 1702 | { 1703 | Deque b; 1704 | auto res = b.emplace(b.end(), 2); 1705 | test_equal_range(b, {2}); 1706 | BOOST_TEST(*res == T(2)); 1707 | } 1708 | 1709 | // to front 1710 | { 1711 | Deque c = get_range(8); 1712 | auto res = c.emplace(c.begin(), 9); 1713 | test_equal_range(c, {9,1,2,3,4,5,6,7,8}); 1714 | BOOST_TEST(*res == T(9)); 1715 | } 1716 | 1717 | // to back 1718 | { 1719 | Deque d = get_range(8); 1720 | auto res = d.emplace(d.end(), 9); 1721 | test_equal_range(d, {1,2,3,4,5,6,7,8,9}); 1722 | BOOST_TEST(*res == T(9)); 1723 | } 1724 | 1725 | // to near front 1726 | { 1727 | Deque e = get_range(8); 1728 | auto res = e.emplace(e.begin() + 3, 9); 1729 | test_equal_range(e, {1,2,3,9,4,5,6,7,8}); 1730 | BOOST_TEST(*res == T(9)); 1731 | } 1732 | 1733 | // to near back 1734 | { 1735 | Deque f = get_range(8); 1736 | auto res = f.emplace(f.end() - 3, 9); 1737 | test_equal_range(f, {1,2,3,4,5,9,6,7,8}); 1738 | BOOST_TEST(*res == T(9)); 1739 | } 1740 | 1741 | // middle with ctor exception 1742 | if (! std::is_nothrow_constructible::value) 1743 | { 1744 | Deque g = get_range(8); 1745 | auto begin = g.begin(); 1746 | 1747 | test_elem_throw::on_ctor_after(1); 1748 | 1749 | BOOST_CHECK_THROW(g.emplace(g.begin() + 4, 404), test_exception); 1750 | 1751 | BOOST_TEST(begin == g.begin()); 1752 | } 1753 | } 1754 | 1755 | BOOST_AUTO_TEST_CASE_TEMPLATE(insert_copy, Deque, t_is_copy_constructible) 1756 | { 1757 | typedef typename Deque::value_type T; 1758 | 1759 | const T x(9); 1760 | 1761 | // insert to empty begin 1762 | { 1763 | Deque a; 1764 | auto res = a.insert(a.begin(), x); 1765 | test_equal_range(a, {9}); 1766 | BOOST_TEST(*res == x); 1767 | } 1768 | 1769 | // to empty end 1770 | { 1771 | Deque b; 1772 | auto res = b.insert(b.end(), x); 1773 | test_equal_range(b, {9}); 1774 | BOOST_TEST(*res == x); 1775 | } 1776 | 1777 | // to front 1778 | { 1779 | Deque c = get_range(8); 1780 | auto res = c.insert(c.begin(), x); 1781 | test_equal_range(c, {9,1,2,3,4,5,6,7,8}); 1782 | BOOST_TEST(*res == x); 1783 | } 1784 | 1785 | // to back 1786 | { 1787 | Deque d = get_range(8); 1788 | auto res = d.insert(d.end(), x); 1789 | test_equal_range(d, {1,2,3,4,5,6,7,8,9}); 1790 | BOOST_TEST(*res == x); 1791 | } 1792 | 1793 | // to near front 1794 | { 1795 | Deque e = get_range(8); 1796 | auto res = e.insert(e.begin() + 3, x); 1797 | test_equal_range(e, {1,2,3,9,4,5,6,7,8}); 1798 | BOOST_TEST(*res == x); 1799 | } 1800 | 1801 | // to near back 1802 | { 1803 | Deque f = get_range(8); 1804 | auto res = f.insert(f.end() - 3, x); 1805 | test_equal_range(f, {1,2,3,4,5,9,6,7,8}); 1806 | BOOST_TEST(*res == x); 1807 | } 1808 | 1809 | // middle with copy ctor exception 1810 | if (! std::is_nothrow_copy_constructible::value) 1811 | { 1812 | Deque g = get_range(8); 1813 | auto begin = g.begin(); 1814 | 1815 | test_elem_throw::on_copy_after(1); 1816 | 1817 | BOOST_CHECK_THROW(g.insert(g.begin() + 4, x), test_exception); 1818 | 1819 | BOOST_TEST(begin == g.begin()); 1820 | } 1821 | } 1822 | 1823 | BOOST_AUTO_TEST_CASE_TEMPLATE(insert_rvalue, Deque, all_deques) 1824 | { 1825 | typedef typename Deque::value_type T; 1826 | 1827 | // insert to empty begin 1828 | { 1829 | Deque a; 1830 | auto res = a.insert(a.begin(), T(1)); 1831 | test_equal_range(a, {1}); 1832 | BOOST_TEST(*res == T(1)); 1833 | } 1834 | 1835 | // to empty end 1836 | { 1837 | Deque b; 1838 | auto res = b.insert(b.end(), T(2)); 1839 | test_equal_range(b, {2}); 1840 | BOOST_TEST(*res == T(2)); 1841 | } 1842 | 1843 | // to front 1844 | { 1845 | Deque c = get_range(8); 1846 | auto res = c.insert(c.begin(), T(9)); 1847 | test_equal_range(c, {9,1,2,3,4,5,6,7,8}); 1848 | BOOST_TEST(*res == T(9)); 1849 | } 1850 | 1851 | // to back 1852 | { 1853 | Deque d = get_range(8); 1854 | auto res = d.insert(d.end(), T(9)); 1855 | test_equal_range(d, {1,2,3,4,5,6,7,8,9}); 1856 | BOOST_TEST(*res == T(9)); 1857 | } 1858 | 1859 | // to near front 1860 | { 1861 | Deque e = get_range(8); 1862 | auto res = e.insert(e.begin() + 3, T(9)); 1863 | test_equal_range(e, {1,2,3,9,4,5,6,7,8}); 1864 | BOOST_TEST(*res == T(9)); 1865 | } 1866 | 1867 | // to near back 1868 | { 1869 | Deque f = get_range(8); 1870 | auto res = f.insert(f.end() - 3, T(9)); 1871 | test_equal_range(f, {1,2,3,4,5,9,6,7,8}); 1872 | BOOST_TEST(*res == T(9)); 1873 | } 1874 | 1875 | // middle with ctor exception 1876 | if (! std::is_nothrow_constructible::value) 1877 | { 1878 | Deque g = get_range(8); 1879 | auto begin = g.begin(); 1880 | 1881 | test_elem_throw::on_ctor_after(1); 1882 | 1883 | BOOST_CHECK_THROW(g.insert(g.begin() + 4, T(404)), test_exception); 1884 | 1885 | BOOST_TEST(begin == g.begin()); 1886 | } 1887 | } 1888 | 1889 | BOOST_AUTO_TEST_CASE_TEMPLATE(insert_n_copy, Deque, t_is_copy_constructible) 1890 | { 1891 | typedef typename Deque::value_type T; 1892 | 1893 | { // scope of x 1894 | 1895 | const T x(9); 1896 | 1897 | // empty to empty 1898 | { 1899 | Deque a; 1900 | 1901 | auto ret = a.insert(a.begin(), 0, x); 1902 | BOOST_TEST(a.empty()); 1903 | BOOST_TEST(ret == a.begin()); 1904 | 1905 | ret = a.insert(a.end(), 0, x); 1906 | BOOST_TEST(a.empty()); 1907 | BOOST_TEST(ret == a.end()); 1908 | } 1909 | 1910 | // range to empty 1911 | { 1912 | Deque b; 1913 | 1914 | auto ret = b.insert(b.end(), 10, x); 1915 | test_equal_range(b, {9,9,9,9,9,9,9,9,9,9}); 1916 | BOOST_TEST(ret == b.begin()); 1917 | } 1918 | 1919 | // range to non-empty front 1920 | { 1921 | Deque c = get_range(6); 1922 | 1923 | auto ret = c.insert(c.begin(), 4, x); 1924 | test_equal_range(c, {9,9,9,9,1,2,3,4,5,6}); 1925 | BOOST_TEST(ret == c.begin()); 1926 | } 1927 | 1928 | // range to non-empty back 1929 | { 1930 | Deque d = get_range(6); 1931 | 1932 | auto ret = d.insert(d.end(), 4, x); 1933 | test_equal_range(d, {1,2,3,4,5,6,9,9,9,9}); 1934 | BOOST_TEST(ret == d.begin() + 6); 1935 | } 1936 | 1937 | // range to non-empty near front 1938 | { 1939 | Deque e = get_range(12); 1940 | 1941 | auto ret = e.insert(e.begin() + 2, 3, x); 1942 | test_equal_range(e, {1,2,9,9,9,3,4,5,6,7,8,9,10,11,12}); 1943 | BOOST_TEST(ret == e.begin() + 2); 1944 | } 1945 | 1946 | // range to non-empty near back 1947 | { 1948 | Deque f = get_range(12); 1949 | 1950 | auto ret = f.insert(f.begin() + 8, 3, x); 1951 | test_equal_range(f, {1,2,3,4,5,6,7,8,9,9,9,9,10,11,12}); 1952 | BOOST_TEST(ret == f.begin() + 8); 1953 | } 1954 | 1955 | // exception in the middle 1956 | if (! std::is_nothrow_copy_constructible::value) 1957 | { 1958 | test_elem_throw::on_copy_after(10); 1959 | 1960 | Deque g = get_range(8); 1961 | 1962 | BOOST_CHECK_THROW(g.insert(g.begin() + 4, 12, x), test_exception); 1963 | } 1964 | 1965 | } // scope of x 1966 | 1967 | BOOST_TEST(test_elem_base::no_living_elem()); 1968 | } 1969 | 1970 | BOOST_AUTO_TEST_CASE_TEMPLATE(insert_input_range, Deque, t_is_copy_constructible) 1971 | { 1972 | typedef typename Deque::value_type T; 1973 | 1974 | // empty to empty 1975 | { 1976 | Deque a; 1977 | 1978 | devector input; 1979 | 1980 | auto input_begin = make_input_iterator(input, input.begin()); 1981 | auto input_end = make_input_iterator(input, input.end()); 1982 | 1983 | auto ret = a.insert(a.begin(), input_begin, input_end); 1984 | BOOST_TEST(a.empty()); 1985 | BOOST_TEST(ret == a.begin()); 1986 | 1987 | ret = a.insert(a.end(), input_begin, input_end); 1988 | BOOST_TEST(a.empty()); 1989 | BOOST_TEST(ret == a.end()); 1990 | } 1991 | 1992 | // range to empty 1993 | { 1994 | Deque b; 1995 | 1996 | devector input = get_range>(10); 1997 | 1998 | auto input_begin = make_input_iterator(input, input.begin()); 1999 | auto input_end = make_input_iterator(input, input.end()); 2000 | 2001 | auto ret = b.insert(b.begin(), input_begin, input_end); 2002 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10}); 2003 | BOOST_TEST(ret == b.begin()); 2004 | } 2005 | 2006 | // range to non-empty front 2007 | { 2008 | Deque c = get_range(6); 2009 | 2010 | devector input = get_range>(4); 2011 | 2012 | auto input_begin = make_input_iterator(input, input.begin()); 2013 | auto input_end = make_input_iterator(input, input.end()); 2014 | 2015 | auto ret = c.insert(c.begin(), input_begin, input_end); 2016 | test_equal_range(c, {1,2,3,4,1,2,3,4,5,6}); 2017 | BOOST_TEST(ret == c.begin()); 2018 | } 2019 | 2020 | // range to non-empty back 2021 | { 2022 | Deque d = get_range(6); 2023 | 2024 | devector input = get_range>(4); 2025 | 2026 | auto input_begin = make_input_iterator(input, input.begin()); 2027 | auto input_end = make_input_iterator(input, input.end()); 2028 | 2029 | auto ret = d.insert(d.end(), input_begin, input_end); 2030 | test_equal_range(d, {1,2,3,4,5,6,1,2,3,4}); 2031 | BOOST_TEST(ret == d.begin() + 6); 2032 | } 2033 | 2034 | // range to non-empty near front 2035 | { 2036 | Deque e = get_range(12); 2037 | 2038 | devector input = get_range>(4); 2039 | 2040 | auto input_begin = make_input_iterator(input, input.begin()); 2041 | auto input_end = make_input_iterator(input, input.end()); 2042 | 2043 | auto ret = e.insert(e.begin() + 2, input_begin, input_end); 2044 | test_equal_range(e, {1,2,1,2,3,4,3,4,5,6,7,8,9,10,11,12}); 2045 | BOOST_TEST(ret == e.begin() + 2); 2046 | } 2047 | 2048 | // range to non-empty near back 2049 | { 2050 | Deque f = get_range(12); 2051 | 2052 | devector input = get_range>(4); 2053 | 2054 | auto input_begin = make_input_iterator(input, input.begin()); 2055 | auto input_end = make_input_iterator(input, input.end()); 2056 | 2057 | auto ret = f.insert(f.begin() + 8, input_begin, input_end); 2058 | test_equal_range(f, {1,2,3,4,5,6,7,8,1,2,3,4,9,10,11,12}); 2059 | BOOST_TEST(ret == f.begin() + 8); 2060 | } 2061 | 2062 | // exception in the middle 2063 | if (! std::is_nothrow_copy_constructible::value) 2064 | { 2065 | devector input = get_range>(12); 2066 | 2067 | auto input_begin = make_input_iterator(input, input.begin()); 2068 | auto input_end = make_input_iterator(input, input.end()); 2069 | 2070 | Deque g = get_range(8); 2071 | 2072 | test_elem_throw::on_copy_after(10); 2073 | 2074 | BOOST_CHECK_THROW(g.insert(g.begin() + 4, input_begin, input_end), test_exception); 2075 | } 2076 | 2077 | BOOST_TEST(test_elem_base::no_living_elem()); 2078 | } 2079 | 2080 | BOOST_AUTO_TEST_CASE_TEMPLATE(insert_forward_range, Deque, t_is_copy_constructible) 2081 | { 2082 | typedef typename Deque::value_type T; 2083 | 2084 | { // scope of input 2085 | 2086 | const std::forward_list input{1,2,3,4,5,6,7,8,9,10,11,12}; 2087 | auto four = input.begin(); 2088 | std::advance(four, 4); 2089 | 2090 | // empty to empty 2091 | { 2092 | Deque a; 2093 | 2094 | auto ret = a.insert(a.begin(), input.begin(), input.begin()); 2095 | BOOST_TEST(a.empty()); 2096 | BOOST_TEST(ret == a.begin()); 2097 | 2098 | ret = a.insert(a.end(), input.begin(), input.begin()); 2099 | BOOST_TEST(a.empty()); 2100 | BOOST_TEST(ret == a.end()); 2101 | } 2102 | 2103 | // range to empty 2104 | { 2105 | Deque b; 2106 | 2107 | auto ret = b.insert(b.begin(), input.begin(), input.end()); 2108 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12}); 2109 | BOOST_TEST(ret == b.begin()); 2110 | } 2111 | 2112 | // range to non-empty front 2113 | { 2114 | Deque c = get_range(6); 2115 | 2116 | auto ret = c.insert(c.begin(), input.begin(), input.end()); 2117 | test_equal_range(c, {1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6}); 2118 | BOOST_TEST(ret == c.begin()); 2119 | } 2120 | 2121 | // range to non-empty back 2122 | { 2123 | Deque d = get_range(6); 2124 | 2125 | auto ret = d.insert(d.end(), input.begin(), four); 2126 | test_equal_range(d, {1,2,3,4,5,6,1,2,3,4}); 2127 | BOOST_TEST(ret == d.begin() + 6); 2128 | } 2129 | 2130 | // range to non-empty near front 2131 | { 2132 | Deque e = get_range(12); 2133 | 2134 | auto ret = e.insert(e.begin() + 2, input.begin(), four); 2135 | test_equal_range(e, {1,2,1,2,3,4,3,4,5,6,7,8,9,10,11,12}); 2136 | BOOST_TEST(ret == e.begin() + 2); 2137 | } 2138 | 2139 | // range to non-empty near back 2140 | { 2141 | Deque f = get_range(12); 2142 | 2143 | auto ret = f.insert(f.begin() + 8, input.begin(), four); 2144 | test_equal_range(f, {1,2,3,4,5,6,7,8,1,2,3,4,9,10,11,12}); 2145 | BOOST_TEST(ret == f.begin() + 8); 2146 | } 2147 | 2148 | // exception in the middle 2149 | if (! std::is_nothrow_copy_constructible::value) 2150 | { 2151 | Deque g = get_range(8); 2152 | 2153 | test_elem_throw::on_copy_after(10); 2154 | 2155 | BOOST_CHECK_THROW(g.insert(g.begin() + 4, input.begin(), input.end()), test_exception); 2156 | } 2157 | 2158 | } // scope of input 2159 | 2160 | BOOST_TEST(test_elem_base::no_living_elem()); 2161 | } 2162 | 2163 | BOOST_AUTO_TEST_CASE_TEMPLATE(insert_il, Deque, t_is_copy_constructible) 2164 | { 2165 | // empty to empty 2166 | { 2167 | Deque a; 2168 | 2169 | auto ret = a.insert(a.begin(), {}); 2170 | BOOST_TEST(a.empty()); 2171 | BOOST_TEST(ret == a.begin()); 2172 | 2173 | ret = a.insert(a.end(), {}); 2174 | BOOST_TEST(a.empty()); 2175 | BOOST_TEST(ret == a.end()); 2176 | } 2177 | 2178 | // range to empty 2179 | { 2180 | Deque b; 2181 | 2182 | auto ret = b.insert(b.begin(), {1,2,3,4,5,6,7,8,9,10,11,12}); 2183 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12}); 2184 | BOOST_TEST(ret == b.begin()); 2185 | } 2186 | 2187 | // range to non-empty front 2188 | { 2189 | Deque c = get_range(6); 2190 | 2191 | auto ret = c.insert(c.begin(), {1,2,3,4,5,6,7,8,9,10,11,12}); 2192 | test_equal_range(c, {1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6}); 2193 | BOOST_TEST(ret == c.begin()); 2194 | } 2195 | 2196 | // range to non-empty back 2197 | { 2198 | Deque d = get_range(6); 2199 | 2200 | auto ret = d.insert(d.end(), {1,2,3,4}); 2201 | test_equal_range(d, {1,2,3,4,5,6,1,2,3,4}); 2202 | BOOST_TEST(ret == d.begin() + 6); 2203 | } 2204 | 2205 | // range to non-empty near front 2206 | { 2207 | Deque e = get_range(12); 2208 | 2209 | auto ret = e.insert(e.begin() + 2, {1,2,3,4}); 2210 | test_equal_range(e, {1,2,1,2,3,4,3,4,5,6,7,8,9,10,11,12}); 2211 | BOOST_TEST(ret == e.begin() + 2); 2212 | } 2213 | 2214 | // range to non-empty near back 2215 | { 2216 | Deque f = get_range(12); 2217 | 2218 | auto ret = f.insert(f.begin() + 8, {1,2,3,4}); 2219 | test_equal_range(f, {1,2,3,4,5,6,7,8,1,2,3,4,9,10,11,12}); 2220 | BOOST_TEST(ret == f.begin() + 8); 2221 | } 2222 | 2223 | typedef typename Deque::value_type T; 2224 | 2225 | // exception in the middle 2226 | if (! std::is_nothrow_copy_constructible::value) 2227 | { 2228 | Deque g = get_range(8); 2229 | 2230 | test_elem_throw::on_copy_after(10); 2231 | 2232 | BOOST_CHECK_THROW(g.insert(g.begin() + 4, {1,2,3,4,5,6,7,8,9,10,11,12}), test_exception); 2233 | } 2234 | 2235 | BOOST_TEST(test_elem_base::no_living_elem()); 2236 | } 2237 | 2238 | BOOST_AUTO_TEST_CASE_TEMPLATE(stable_insert, Deque, t_is_default_constructible) 2239 | { 2240 | typedef typename Deque::value_type T; 2241 | typedef typename Deque::iterator iterator; 2242 | 2243 | devector valid_pointers; 2244 | 2245 | auto save_pointers = [&](const Deque& d) 2246 | { 2247 | valid_pointers.clear(); 2248 | valid_pointers.reserve(d.size()); 2249 | for (auto&& e : d) { valid_pointers.unsafe_push_back(&e); } 2250 | }; 2251 | 2252 | auto check_pointers = [&]() 2253 | { 2254 | unsigned i = 0; 2255 | for (const T* p : valid_pointers) { BOOST_TEST(*p == T(++i)); } 2256 | }; 2257 | 2258 | auto check_inserted = [](iterator f, iterator l) 2259 | { 2260 | unsigned i = 0; 2261 | while (f != l) { BOOST_TEST(*f++ == T(++i)); } 2262 | }; 2263 | 2264 | // empty to empty 2265 | { 2266 | devector x; 2267 | auto xb = std::make_move_iterator(x.begin()); 2268 | auto xe = std::make_move_iterator(x.end()); 2269 | 2270 | Deque a; 2271 | auto res = a.stable_insert(a.begin(), xb, xb); 2272 | BOOST_TEST(a.empty()); 2273 | BOOST_TEST(res == a.begin()); 2274 | 2275 | res = a.stable_insert(a.end(), xe, xe); 2276 | BOOST_TEST(a.empty()); 2277 | BOOST_TEST(res == a.end()); 2278 | } 2279 | 2280 | // range to empty 2281 | { 2282 | devector x = get_range>(12); 2283 | auto xb = std::make_move_iterator(x.begin()); 2284 | auto xe = std::make_move_iterator(x.end()); 2285 | 2286 | Deque b; 2287 | auto res = b.stable_insert(b.end(), xb, xe); 2288 | test_equal_range(b, {1,2,3,4,5,6,7,8,9,10,11,12}); 2289 | BOOST_TEST(res == b.begin()); 2290 | } 2291 | 2292 | // range to begin 2293 | { 2294 | devector x = get_range>(12); 2295 | auto xb = std::make_move_iterator(x.begin()); 2296 | auto xe = std::make_move_iterator(x.end()); 2297 | 2298 | Deque c = get_range(4); 2299 | save_pointers(c); 2300 | 2301 | auto res = c.stable_insert(c.begin(), xb, xe); 2302 | 2303 | check_pointers(); 2304 | check_inserted(res, res + x.size()); 2305 | BOOST_TEST(c.size() >= 16u); 2306 | } 2307 | 2308 | // range to end 2309 | { 2310 | devector x = get_range>(12); 2311 | auto xb = std::make_move_iterator(x.begin()); 2312 | auto xe = std::make_move_iterator(x.end()); 2313 | 2314 | Deque d = get_range(4); 2315 | save_pointers(d); 2316 | 2317 | auto res = d.stable_insert(d.end(), xb, xe); 2318 | 2319 | check_pointers(); 2320 | check_inserted(res, res + x.size()); 2321 | BOOST_TEST(d.size() >= 16u); 2322 | } 2323 | 2324 | // range to mid mid-segment 2325 | { 2326 | devector x = get_range>(4); 2327 | auto xb = std::make_move_iterator(x.begin()); 2328 | auto xe = std::make_move_iterator(x.end()); 2329 | 2330 | Deque e = get_range(14); 2331 | save_pointers(e); 2332 | 2333 | auto res = e.stable_insert(e.begin() + 6, xb, xe); 2334 | 2335 | check_pointers(); 2336 | check_inserted(res, res + x.size()); 2337 | BOOST_TEST(e.size() >= 18u); 2338 | } 2339 | 2340 | // range to mid segment boundary 2341 | { 2342 | devector x = get_range>(6); 2343 | auto xb = std::make_move_iterator(x.begin()); 2344 | auto xe = std::make_move_iterator(x.end()); 2345 | 2346 | Deque f = get_range(11); 2347 | save_pointers(f); 2348 | 2349 | auto res = f.stable_insert(f.begin() + 8, xb, xe); 2350 | 2351 | check_pointers(); 2352 | check_inserted(res, res + x.size()); 2353 | BOOST_TEST(f.size() >= 17u); 2354 | } 2355 | 2356 | // range to mid segment, no default ctr 2357 | { 2358 | devector x = get_range>(8); 2359 | auto xb = std::make_move_iterator(x.begin()); 2360 | auto xe = std::make_move_iterator(x.end()); 2361 | 2362 | Deque g = get_range(14); 2363 | save_pointers(g); 2364 | 2365 | auto res = g.stable_insert(g.begin() + 6, xb, xe); 2366 | 2367 | check_pointers(); 2368 | check_inserted(res, res + x.size()); 2369 | BOOST_TEST(g.size() >= 22u); 2370 | } 2371 | } 2372 | 2373 | BOOST_AUTO_TEST_CASE_TEMPLATE(pop_front, Deque, all_deques) 2374 | { 2375 | { 2376 | Deque a; 2377 | a.emplace_front(1); 2378 | a.pop_front(); 2379 | BOOST_TEST(a.empty()); 2380 | 2381 | a.emplace_back(2); 2382 | a.pop_front(); 2383 | BOOST_TEST(a.empty()); 2384 | 2385 | a.emplace_front(3); 2386 | a.pop_front(); 2387 | BOOST_TEST(a.empty()); 2388 | } 2389 | 2390 | { 2391 | Deque b = get_range(20); 2392 | for (int i = 0; i < 20; ++i) 2393 | { 2394 | BOOST_TEST(!b.empty()); 2395 | b.pop_front(); 2396 | } 2397 | BOOST_TEST(b.empty()); 2398 | } 2399 | } 2400 | 2401 | BOOST_AUTO_TEST_CASE_TEMPLATE(pop_back, Deque, all_deques) 2402 | { 2403 | { 2404 | Deque a; 2405 | a.emplace_front(1); 2406 | a.pop_back(); 2407 | BOOST_TEST(a.empty()); 2408 | 2409 | a.emplace_back(2); 2410 | a.pop_back(); 2411 | BOOST_TEST(a.empty()); 2412 | 2413 | a.emplace_front(3); 2414 | a.pop_back(); 2415 | BOOST_TEST(a.empty()); 2416 | } 2417 | 2418 | { 2419 | Deque b = get_range(20); 2420 | for (int i = 0; i < 20; ++i) 2421 | { 2422 | BOOST_TEST(!b.empty()); 2423 | b.pop_back(); 2424 | } 2425 | BOOST_TEST(b.empty()); 2426 | } 2427 | } 2428 | 2429 | BOOST_AUTO_TEST_CASE_TEMPLATE(erase, Deque, all_deques) 2430 | { 2431 | typedef typename Deque::value_type T; 2432 | 2433 | { 2434 | Deque a = get_range(10); 2435 | 2436 | auto it = a.erase(a.begin()); 2437 | test_equal_range(a, {2,3,4,5,6,7,8,9,10}); 2438 | BOOST_TEST(*it == T(2)); 2439 | 2440 | it = a.erase(a.begin() + 8); 2441 | test_equal_range(a, {2,3,4,5,6,7,8,9}); 2442 | BOOST_TEST(it == a.end()); 2443 | 2444 | it = a.erase(a.begin() + 4); 2445 | test_equal_range(a, {2,3,4,5,7,8,9}); 2446 | BOOST_TEST(*it == T(7)); 2447 | } 2448 | } 2449 | 2450 | BOOST_AUTO_TEST_CASE_TEMPLATE(erase_range, Deque, all_deques) 2451 | { 2452 | typedef typename Deque::value_type T; 2453 | 2454 | { 2455 | Deque a; 2456 | a.erase(a.begin(), a.begin()); 2457 | BOOST_TEST(a.empty()); 2458 | a.erase(a.begin(), a.end()); 2459 | BOOST_TEST(a.empty()); 2460 | a.erase(a.end(), a.end()); 2461 | BOOST_TEST(a.empty()); 2462 | } 2463 | 2464 | { 2465 | Deque b = get_range(18); 2466 | auto it = b.erase(b.begin() + 1, b.begin() + 4); 2467 | test_equal_range(b, {1,5,6,7,8,9,10,11,12,13,14,15,16,17,18}); 2468 | BOOST_TEST(*it == T(5)); 2469 | } 2470 | 2471 | { 2472 | Deque c = get_range(18); 2473 | auto it = c.erase(c.begin() + 3, c.begin() + 14); 2474 | test_equal_range(c, {1,2,3,15,16,17,18}); 2475 | BOOST_TEST(*it == T(15)); 2476 | } 2477 | 2478 | { 2479 | Deque d = get_range(22); 2480 | d.erase(d.begin(), d.end()); 2481 | BOOST_TEST(d.empty()); 2482 | } 2483 | } 2484 | 2485 | BOOST_AUTO_TEST_CASE_TEMPLATE(member_swap, Deque, all_deques) 2486 | { 2487 | { 2488 | Deque a; 2489 | Deque b; 2490 | 2491 | a.swap(b); 2492 | 2493 | BOOST_TEST(a.empty()); 2494 | BOOST_TEST(b.empty()); 2495 | } 2496 | 2497 | { 2498 | Deque a; 2499 | Deque b = get_range(4); 2500 | 2501 | a.swap(b); 2502 | 2503 | test_equal_range(a, {1, 2, 3, 4}); 2504 | BOOST_TEST(b.empty()); 2505 | } 2506 | 2507 | { 2508 | Deque a = get_range(5, 9, 9, 13); 2509 | Deque b = get_range(4); 2510 | 2511 | a.swap(b); 2512 | 2513 | test_equal_range(a, {1, 2, 3, 4}); 2514 | test_equal_range(b, {5, 6, 7, 8, 9, 10, 11, 12}); 2515 | } 2516 | } 2517 | 2518 | BOOST_AUTO_TEST_CASE_TEMPLATE(clear, Deque, all_deques) 2519 | { 2520 | { 2521 | Deque a; 2522 | a.clear(); 2523 | BOOST_TEST(a.empty()); 2524 | } 2525 | 2526 | { 2527 | Deque b = get_range(); 2528 | b.clear(); 2529 | BOOST_TEST(b.empty()); 2530 | } 2531 | 2532 | { 2533 | Deque c = get_range(); 2534 | c.clear(); 2535 | c.emplace_back(1); 2536 | c.emplace_back(2); 2537 | c.emplace_back(3); 2538 | c.emplace_back(4); 2539 | } 2540 | } 2541 | 2542 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_eq, Deque, all_deques) 2543 | { 2544 | { // equal 2545 | Deque a = get_range(8); 2546 | Deque b = get_range(8); 2547 | 2548 | BOOST_TEST(a == b); 2549 | } 2550 | 2551 | { // diff size 2552 | Deque a = get_range(8); 2553 | Deque b = get_range(9); 2554 | 2555 | BOOST_TEST(!(a == b)); 2556 | } 2557 | 2558 | { // diff content 2559 | Deque a = get_range(8); 2560 | Deque b = get_range(2,6,6,10); 2561 | 2562 | BOOST_TEST(!(a == b)); 2563 | } 2564 | } 2565 | 2566 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_lt, Deque, all_deques) 2567 | { 2568 | { // little than 2569 | Deque a = get_range(7); 2570 | Deque b = get_range(8); 2571 | 2572 | BOOST_TEST((a < b)); 2573 | } 2574 | 2575 | { // equal 2576 | Deque a = get_range(8); 2577 | Deque b = get_range(8); 2578 | 2579 | BOOST_TEST(!(a < b)); 2580 | } 2581 | 2582 | { // greater than 2583 | Deque a = get_range(8); 2584 | Deque b = get_range(7); 2585 | 2586 | BOOST_TEST(!(a < b)); 2587 | } 2588 | } 2589 | 2590 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_ne, Deque, all_deques) 2591 | { 2592 | { // equal 2593 | Deque a = get_range(8); 2594 | Deque b = get_range(8); 2595 | 2596 | BOOST_TEST(!(a != b)); 2597 | } 2598 | 2599 | { // diff size 2600 | Deque a = get_range(8); 2601 | Deque b = get_range(9); 2602 | 2603 | BOOST_TEST((a != b)); 2604 | } 2605 | 2606 | { // diff content 2607 | Deque a = get_range(8); 2608 | Deque b = get_range(2,6,6,10); 2609 | 2610 | BOOST_TEST((a != b)); 2611 | } 2612 | } 2613 | 2614 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_gt, Deque, all_deques) 2615 | { 2616 | { // little than 2617 | Deque a = get_range(7); 2618 | Deque b = get_range(8); 2619 | 2620 | BOOST_TEST(!(a > b)); 2621 | } 2622 | 2623 | { // equal 2624 | Deque a = get_range(8); 2625 | Deque b = get_range(8); 2626 | 2627 | BOOST_TEST(!(a > b)); 2628 | } 2629 | 2630 | { // greater than 2631 | Deque a = get_range(8); 2632 | Deque b = get_range(7); 2633 | 2634 | BOOST_TEST((a > b)); 2635 | } 2636 | } 2637 | 2638 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_ge, Deque, all_deques) 2639 | { 2640 | { // little than 2641 | Deque a = get_range(7); 2642 | Deque b = get_range(8); 2643 | 2644 | BOOST_TEST(!(a >= b)); 2645 | } 2646 | 2647 | { // equal 2648 | Deque a = get_range(8); 2649 | Deque b = get_range(8); 2650 | 2651 | BOOST_TEST((a >= b)); 2652 | } 2653 | 2654 | { // greater than 2655 | Deque a = get_range(8); 2656 | Deque b = get_range(7); 2657 | 2658 | BOOST_TEST((a >= b)); 2659 | } 2660 | } 2661 | 2662 | BOOST_AUTO_TEST_CASE_TEMPLATE(op_le, Deque, all_deques) 2663 | { 2664 | { // little than 2665 | Deque a = get_range(7); 2666 | Deque b = get_range(8); 2667 | 2668 | BOOST_TEST((a <= b)); 2669 | } 2670 | 2671 | { // equal 2672 | Deque a = get_range(8); 2673 | Deque b = get_range(8); 2674 | 2675 | BOOST_TEST((a <= b)); 2676 | } 2677 | 2678 | { // greater than 2679 | Deque a = get_range(8); 2680 | Deque b = get_range(7); 2681 | 2682 | BOOST_TEST(!(a <= b)); 2683 | } 2684 | } 2685 | 2686 | BOOST_AUTO_TEST_CASE_TEMPLATE(free_swap, Deque, all_deques) 2687 | { 2688 | using std::swap; // test the ADL trick 2689 | 2690 | { 2691 | Deque a; 2692 | Deque b; 2693 | 2694 | swap(a, b); 2695 | 2696 | BOOST_TEST(a.empty()); 2697 | BOOST_TEST(b.empty()); 2698 | } 2699 | 2700 | { 2701 | Deque a; 2702 | Deque b = get_range(4); 2703 | 2704 | swap(a, b); 2705 | 2706 | test_equal_range(a, {1, 2, 3, 4}); 2707 | BOOST_TEST(b.empty()); 2708 | } 2709 | 2710 | { 2711 | Deque a = get_range(5, 9, 9, 13); 2712 | Deque b = get_range(4); 2713 | 2714 | swap(a, b); 2715 | 2716 | test_equal_range(a, {1, 2, 3, 4}); 2717 | test_equal_range(b, {5, 6, 7, 8, 9, 10, 11, 12}); 2718 | } 2719 | } 2720 | -------------------------------------------------------------------------------- /test/explicit_inst_batch_deque_test.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | 13 | struct empty 14 | { 15 | friend bool operator == (const empty &, const empty &){ return true; } 16 | friend bool operator < (const empty &, const empty &){ return true; } 17 | }; 18 | 19 | template class ::boost::double_ended::batch_deque; 20 | 21 | int main() 22 | { 23 | ::boost::double_ended::batch_deque dummy; 24 | (void)dummy; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /test/explicit_inst_devector_test.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | 13 | struct empty 14 | { 15 | friend bool operator == (const empty &, const empty &){ return true; } 16 | friend bool operator < (const empty &, const empty &){ return true; } 17 | }; 18 | 19 | template class ::boost::double_ended::devector; 20 | 21 | int main() 22 | { 23 | ::boost::double_ended::devector dummy; 24 | (void)dummy; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /test/input_iterator.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | /** 12 | * Emulates input iterator, validates algorithm 13 | * does a single pass only, removes visited elements. 14 | * 15 | * Container::erase(front_iterator) must not invalidate other iterators. 16 | * 17 | * The hack around `_erase_on_destroy` is required to make `*it++` work. 18 | */ 19 | template 20 | class input_iterator : public std::iterator 21 | { 22 | typedef typename Container::iterator iterator; 23 | 24 | struct erase_on_destroy {}; 25 | 26 | public: 27 | typedef typename Container::value_type value_type; 28 | 29 | input_iterator() = default; 30 | 31 | input_iterator(Container& c, iterator it) 32 | :_container(&c), 33 | _it(it) 34 | {} 35 | 36 | input_iterator(const input_iterator& rhs) 37 | :_container(rhs._container), 38 | _it(rhs._it), 39 | _erase_on_destroy(rhs._erase_on_destroy) 40 | { 41 | rhs._erase_on_destroy = false; 42 | } 43 | 44 | input_iterator(const input_iterator& rhs, erase_on_destroy) 45 | :_container(rhs._container), 46 | _it(rhs._it), 47 | _erase_on_destroy(true) 48 | {} 49 | 50 | ~input_iterator() 51 | { 52 | if (_erase_on_destroy) 53 | { 54 | _container->erase(_it); // must not invalidate other iterators 55 | } 56 | } 57 | 58 | const value_type& operator*() 59 | { 60 | return *_it; 61 | } 62 | 63 | input_iterator operator++() 64 | { 65 | _container->erase(_it); 66 | ++_it; 67 | return *this; 68 | } 69 | 70 | input_iterator operator++(int) 71 | { 72 | input_iterator old(*this, erase_on_destroy{}); 73 | ++_it; 74 | return old; 75 | } 76 | 77 | friend bool operator==(const input_iterator a, const input_iterator b) 78 | { 79 | return a._it == b._it; 80 | } 81 | 82 | friend bool operator!=(const input_iterator a, const input_iterator b) 83 | { 84 | return !(a == b); 85 | } 86 | 87 | private: 88 | Container* _container; 89 | iterator _it; 90 | mutable bool _erase_on_destroy = false; 91 | }; 92 | 93 | template 94 | input_iterator make_input_iterator(Container& c, typename Container::iterator it) 95 | { 96 | return input_iterator(c, it); 97 | } 98 | -------------------------------------------------------------------------------- /test/serialize_batch_deque_test.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include "test_elem.hpp" 19 | 20 | using namespace boost::double_ended; 21 | 22 | #define BOOST_TEST_MODULE serialize_batch_deque 23 | #include 24 | 25 | #include 26 | 27 | using all_batch_deques = boost::mpl::list< 28 | batch_deque, 29 | batch_deque 30 | >; 31 | 32 | BOOST_AUTO_TEST_CASE_TEMPLATE(empty, Deque, all_batch_deques) 33 | { 34 | const Deque a; 35 | 36 | std::stringstream stream; 37 | 38 | { 39 | boost::archive::text_oarchive out(stream); 40 | out << a; 41 | } 42 | 43 | Deque b; 44 | boost::archive::text_iarchive in(stream); 45 | in >> b; 46 | 47 | BOOST_TEST(b.empty()); 48 | } 49 | 50 | BOOST_AUTO_TEST_CASE_TEMPLATE(into_empty, Deque, all_batch_deques) 51 | { 52 | const Deque a{1, 2, 3, 4, 5, 6, 7, 8}; 53 | 54 | std::stringstream stream; 55 | 56 | { 57 | boost::archive::text_oarchive out(stream); 58 | out << a; 59 | } 60 | 61 | Deque b; 62 | boost::archive::text_iarchive in(stream); 63 | in >> b; 64 | 65 | BOOST_TEST(b == a); 66 | } 67 | 68 | BOOST_AUTO_TEST_CASE_TEMPLATE(into_non_empty, Deque, all_batch_deques) 69 | { 70 | const Deque a{1, 2, 3, 4, 5, 6, 7, 8}; 71 | 72 | std::stringstream stream; 73 | 74 | { 75 | boost::archive::text_oarchive out(stream); 76 | out << a; 77 | } 78 | 79 | Deque b{11, 12, 13, 14}; 80 | boost::archive::text_iarchive in(stream); 81 | in >> b; 82 | 83 | BOOST_TEST(b == a); 84 | } 85 | 86 | BOOST_AUTO_TEST_CASE_TEMPLATE(with_enough_capacity, Deque, all_batch_deques) 87 | { 88 | const Deque a{1, 2, 3, 4, 5, 6, 7, 8}; 89 | 90 | std::stringstream stream; 91 | 92 | { 93 | boost::archive::text_oarchive out(stream); 94 | out << a; 95 | } 96 | 97 | Deque b{11, 12, 13, 14, 15, 16, 17, 18}; 98 | b.pop_front(); 99 | b.pop_front(); 100 | b.pop_back(); 101 | 102 | boost::archive::text_iarchive in(stream); 103 | in >> b; 104 | 105 | BOOST_TEST(b == a); 106 | } 107 | 108 | BOOST_AUTO_TEST_CASE_TEMPLATE(with_enough_size, Deque, all_batch_deques) 109 | { 110 | const Deque a{1, 2, 3, 4}; 111 | 112 | std::stringstream stream; 113 | 114 | { 115 | boost::archive::text_oarchive out(stream); 116 | out << a; 117 | } 118 | 119 | Deque b{11, 12, 13, 14, 15, 16, 17, 18}; 120 | b.pop_front(); 121 | b.pop_back(); 122 | b.pop_back(); 123 | 124 | boost::archive::text_iarchive in(stream); 125 | in >> b; 126 | 127 | BOOST_TEST(b == a); 128 | } 129 | -------------------------------------------------------------------------------- /test/serialize_devector_test.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include "test_elem.hpp" 19 | 20 | using namespace boost::double_ended; 21 | 22 | #define BOOST_TEST_MODULE serialize_devector 23 | #include 24 | 25 | #include 26 | 27 | using all_devectors = boost::mpl::list< 28 | devector, 29 | devector 30 | >; 31 | 32 | BOOST_AUTO_TEST_CASE_TEMPLATE(empty, Devector, all_devectors) 33 | { 34 | const Devector a; 35 | 36 | std::stringstream stream; 37 | 38 | { 39 | boost::archive::text_oarchive out(stream); 40 | out << a; 41 | } 42 | 43 | Devector b; 44 | boost::archive::text_iarchive in(stream); 45 | in >> b; 46 | 47 | BOOST_TEST(b.empty()); 48 | } 49 | 50 | BOOST_AUTO_TEST_CASE_TEMPLATE(into_empty, Devector, all_devectors) 51 | { 52 | const Devector a{1, 2, 3, 4, 5, 6, 7, 8}; 53 | 54 | std::stringstream stream; 55 | 56 | { 57 | boost::archive::text_oarchive out(stream); 58 | out << a; 59 | } 60 | 61 | Devector b; 62 | boost::archive::text_iarchive in(stream); 63 | in >> b; 64 | 65 | BOOST_TEST(b == a); 66 | } 67 | 68 | BOOST_AUTO_TEST_CASE_TEMPLATE(into_non_empty, Devector, all_devectors) 69 | { 70 | const Devector a{1, 2, 3, 4, 5, 6, 7, 8}; 71 | 72 | std::stringstream stream; 73 | 74 | { 75 | boost::archive::text_oarchive out(stream); 76 | out << a; 77 | } 78 | 79 | Devector b{11, 12, 13, 14}; 80 | boost::archive::text_iarchive in(stream); 81 | in >> b; 82 | 83 | BOOST_TEST(b == a); 84 | } 85 | 86 | BOOST_AUTO_TEST_CASE_TEMPLATE(with_enough_capacity, Devector, all_devectors) 87 | { 88 | const Devector a{1, 2, 3, 4, 5, 6, 7, 8}; 89 | 90 | std::stringstream stream; 91 | 92 | { 93 | boost::archive::text_oarchive out(stream); 94 | out << a; 95 | } 96 | 97 | Devector b{11, 12, 13, 14, 15, 16, 17, 18}; 98 | b.pop_front(); 99 | b.pop_front(); 100 | b.pop_back(); 101 | 102 | boost::archive::text_iarchive in(stream); 103 | in >> b; 104 | 105 | BOOST_TEST(b == a); 106 | } 107 | 108 | BOOST_AUTO_TEST_CASE_TEMPLATE(with_enough_size, Devector, all_devectors) 109 | { 110 | const Devector a{1, 2, 3, 4}; 111 | 112 | std::stringstream stream; 113 | 114 | { 115 | boost::archive::text_oarchive out(stream); 116 | out << a; 117 | } 118 | 119 | Devector b{11, 12, 13, 14, 15, 16, 17, 18}; 120 | b.pop_front(); 121 | b.pop_back(); 122 | b.pop_back(); 123 | 124 | boost::archive::text_iarchive in(stream); 125 | in >> b; 126 | 127 | BOOST_TEST(b == a); 128 | } 129 | -------------------------------------------------------------------------------- /test/test_elem.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | 13 | struct test_exception {}; 14 | 15 | struct test_elem_throw 16 | { 17 | static int throw_on_ctor_after /*= -1*/; 18 | static int throw_on_copy_after /*= -1*/; 19 | static int throw_on_move_after /*= -1*/; 20 | 21 | static void on_ctor_after(int x) { throw_on_ctor_after = x; } 22 | static void on_copy_after(int x) { throw_on_copy_after = x; } 23 | static void on_move_after(int x) { throw_on_move_after = x; } 24 | 25 | static void do_not_throw() 26 | { 27 | throw_on_ctor_after = -1; 28 | throw_on_copy_after = -1; 29 | throw_on_move_after = -1; 30 | } 31 | 32 | static void in_constructor() { maybe_throw(throw_on_ctor_after); } 33 | static void in_copy() { maybe_throw(throw_on_copy_after); } 34 | static void in_move() { maybe_throw(throw_on_move_after); } 35 | 36 | private: 37 | static void maybe_throw(int& counter) 38 | { 39 | if (counter > 0) 40 | { 41 | --counter; 42 | if (counter == 0) 43 | { 44 | --counter; 45 | throw test_exception(); 46 | } 47 | } 48 | } 49 | }; 50 | 51 | int test_elem_throw::throw_on_ctor_after = -1; 52 | int test_elem_throw::throw_on_copy_after = -1; 53 | int test_elem_throw::throw_on_move_after = -1; 54 | 55 | struct test_elem_base 56 | { 57 | test_elem_base() 58 | { 59 | test_elem_throw::in_constructor(); 60 | _index = new int(0); 61 | ++_live_count; 62 | } 63 | 64 | test_elem_base(int index) 65 | { 66 | test_elem_throw::in_constructor(); 67 | _index = new int(index); 68 | ++_live_count; 69 | } 70 | 71 | explicit test_elem_base(const test_elem_base& rhs) 72 | { 73 | test_elem_throw::in_copy(); 74 | _index = new int(*rhs._index); 75 | ++_live_count; 76 | } 77 | 78 | test_elem_base(test_elem_base&& rhs) 79 | { 80 | test_elem_throw::in_move(); 81 | _index = rhs._index; 82 | rhs._index = nullptr; 83 | ++_live_count; 84 | } 85 | 86 | void operator=(const test_elem_base& rhs) 87 | { 88 | test_elem_throw::in_copy(); 89 | if (_index) { delete _index; } 90 | _index = new int(*rhs._index); 91 | } 92 | 93 | void operator=(test_elem_base&& rhs) 94 | { 95 | test_elem_throw::in_move(); 96 | if (_index) { delete _index; } 97 | _index = rhs._index; 98 | rhs._index = nullptr; 99 | } 100 | 101 | ~test_elem_base() 102 | { 103 | if (_index) { delete _index; } 104 | --_live_count; 105 | } 106 | 107 | friend bool operator==(const test_elem_base& a, const test_elem_base& b) 108 | { 109 | return a._index && b._index && *(a._index) == *(b._index); 110 | } 111 | 112 | friend bool operator<(const test_elem_base& a, const test_elem_base& b) 113 | { 114 | return boost::less_pointees(a._index, b._index); 115 | } 116 | 117 | friend std::ostream& operator<<(std::ostream& out, const test_elem_base& elem) 118 | { 119 | if (elem._index) { out << *elem._index; } 120 | else { out << "null"; } 121 | return out; 122 | } 123 | 124 | template 125 | void serialize(Archive& ar, unsigned /* version */) 126 | { 127 | ar & *_index; 128 | } 129 | 130 | static bool no_living_elem() 131 | { 132 | return _live_count == 0; 133 | } 134 | 135 | private: 136 | int* _index; 137 | 138 | static int _live_count; 139 | }; 140 | 141 | int test_elem_base::_live_count = 0; 142 | 143 | struct regular_elem : test_elem_base 144 | { 145 | regular_elem() = default; 146 | 147 | regular_elem(int index) : test_elem_base(index) {} 148 | 149 | regular_elem(const regular_elem& rhs) 150 | :test_elem_base(rhs) 151 | {} 152 | 153 | regular_elem(regular_elem&& rhs) 154 | :test_elem_base(std::move(rhs)) 155 | {} 156 | 157 | void operator=(const regular_elem& rhs) 158 | { 159 | static_cast(*this) = rhs; 160 | } 161 | 162 | void operator=(regular_elem&& rhs) 163 | { 164 | static_cast(*this) = std::move(rhs); 165 | } 166 | }; 167 | 168 | struct noex_move : test_elem_base 169 | { 170 | noex_move() = default; 171 | 172 | noex_move(int index) : test_elem_base(index) {} 173 | 174 | noex_move(const noex_move& rhs) 175 | :test_elem_base(rhs) 176 | {} 177 | 178 | noex_move(noex_move&& rhs) noexcept 179 | :test_elem_base(std::move(rhs)) 180 | {} 181 | 182 | void operator=(const noex_move& rhs) 183 | { 184 | static_cast(*this) = rhs; 185 | } 186 | 187 | void operator=(noex_move&& rhs) noexcept 188 | { 189 | static_cast(*this) = std::move(rhs); 190 | } 191 | }; 192 | 193 | struct noex_copy : test_elem_base 194 | { 195 | noex_copy() = default; 196 | 197 | noex_copy(int index) : test_elem_base(index) {} 198 | 199 | noex_copy(const noex_copy& rhs) noexcept 200 | :test_elem_base(rhs) 201 | {} 202 | 203 | noex_copy(noex_copy&& rhs) 204 | :test_elem_base(std::move(rhs)) 205 | {} 206 | 207 | void operator=(const noex_copy& rhs) noexcept 208 | { 209 | static_cast(*this) = rhs; 210 | } 211 | 212 | void operator=(noex_copy&& rhs) 213 | { 214 | static_cast(*this) = std::move(rhs); 215 | } 216 | }; 217 | 218 | struct only_movable : test_elem_base 219 | { 220 | only_movable() = default; 221 | 222 | only_movable(int index) : test_elem_base(index) {} 223 | 224 | only_movable(only_movable&& rhs) 225 | :test_elem_base(std::move(rhs)) 226 | {} 227 | 228 | void operator=(only_movable&& rhs) 229 | { 230 | static_cast(*this) = std::move(rhs); 231 | } 232 | }; 233 | 234 | struct no_default_ctor : test_elem_base 235 | { 236 | no_default_ctor(int index) : test_elem_base(index) {} 237 | 238 | no_default_ctor(const no_default_ctor& rhs) 239 | :test_elem_base(rhs) 240 | {} 241 | 242 | no_default_ctor(no_default_ctor&& rhs) 243 | :test_elem_base(std::move(rhs)) 244 | {} 245 | 246 | void operator=(const no_default_ctor& rhs) 247 | { 248 | static_cast(*this) = rhs; 249 | } 250 | 251 | void operator=(no_default_ctor&& rhs) 252 | { 253 | static_cast(*this) = std::move(rhs); 254 | } 255 | }; 256 | -------------------------------------------------------------------------------- /test/test_util.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // (C) Copyright Benedek Thaler 2015-2016. Distributed under the Boost 4 | // Software License, Version 1.0. (See accompanying file 5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // See http://erenon.hu/double_ended for documentation. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | // get_range 16 | 17 | template 18 | DeContainer get_range(int fbeg, int fend, int bbeg, int bend) 19 | { 20 | DeContainer c; 21 | 22 | for (int i = fend; i > fbeg ;) 23 | { 24 | c.emplace_front(--i); 25 | } 26 | 27 | for (int i = bbeg; i < bend; ++i) 28 | { 29 | c.emplace_back(i); 30 | } 31 | 32 | return c; 33 | } 34 | 35 | template 36 | Container get_range(int count) 37 | { 38 | Container c; 39 | 40 | for (int i = 1; i <= count; ++i) 41 | { 42 | c.emplace_back(i); 43 | } 44 | 45 | return c; 46 | } 47 | 48 | template 49 | Container get_range() 50 | { 51 | return get_range(1, 13, 13, 25); 52 | } 53 | 54 | // Allocator with different propagation policies than std::allocator 55 | 56 | template 57 | struct different_allocator : public std::allocator 58 | { 59 | bool operator==(const different_allocator&) const { return false; } 60 | bool operator!=(const different_allocator&) const { return true; } 61 | 62 | using propagate_on_container_copy_assignment = std::true_type; 63 | using propagate_on_container_move_assignment = std::false_type; 64 | using propagate_on_container_swap = std::true_type; 65 | }; 66 | 67 | // MPL sequence util 68 | 69 | template class Predicate> 70 | struct if_value_type 71 | { 72 | template 73 | struct apply : Predicate {}; 74 | }; 75 | 76 | template 77 | using if_t_is_default_constructible = 78 | typename boost::mpl::filter_view>::type; 79 | 80 | template 81 | using if_t_is_copy_constructible = 82 | typename boost::mpl::filter_view>::type; 83 | 84 | template 85 | using if_t_is_trivial = 86 | typename boost::mpl::filter_view>::type; 87 | 88 | // Container comparison 89 | 90 | template 91 | void print_range(std::ostream& out, const Range& range) 92 | { 93 | out << '['; 94 | bool first = true; 95 | for (auto&& elem : range) 96 | { 97 | if (first) { first = false; } 98 | else { out << ','; } 99 | 100 | out << elem; 101 | } 102 | out << ']'; 103 | } 104 | 105 | template 106 | void test_equal_range(const C1& a, const C2&b) 107 | { 108 | bool equals = boost::algorithm::equal( 109 | a.begin(), a.end(), 110 | b.begin(), b.end() 111 | ); 112 | 113 | BOOST_TEST(equals); 114 | 115 | if (!equals) 116 | { 117 | print_range(std::cerr, a); 118 | std::cerr << "\n"; 119 | print_range(std::cerr, b); 120 | std::cerr << "\n"; 121 | } 122 | } 123 | 124 | // support initializer_list 125 | template 126 | void test_equal_range(const C& a, std::initializer_list il) 127 | { 128 | typedef typename C::value_type T; 129 | std::vector b; 130 | 131 | for (auto&& elem : il) 132 | { 133 | b.emplace_back(elem); 134 | } 135 | 136 | test_equal_range(a, b); 137 | } 138 | 139 | --------------------------------------------------------------------------------