diff --git a/libs/core/datastructures/include/hpx/datastructures/detail/small_vector.hpp b/libs/core/datastructures/include/hpx/datastructures/detail/small_vector.hpp index d6379378d138..2a4f4310dde2 100644 --- a/libs/core/datastructures/include/hpx/datastructures/detail/small_vector.hpp +++ b/libs/core/datastructures/include/hpx/datastructures/detail/small_vector.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2022 Hartmut Kaiser +// Copyright (c) 2024 Isidoros Tsaousis-Seiras // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -48,6 +49,7 @@ #include #include #include +#include namespace hpx::detail { @@ -85,8 +87,10 @@ namespace hpx::detail { inline constexpr bool is_argument_iterator_v = is_argument_iterator::value; + // clang-format off constexpr auto round_up(std::size_t n, std::size_t multiple) noexcept -> std::size_t + // clang-format on { return ((n + (multiple - 1)) / multiple) * multiple; } @@ -207,9 +211,11 @@ namespace hpx::detail { alignment_of_small_vector()); } + // clang-format off template constexpr auto automatic_capacity(std::size_t min_inline_capacity) noexcept -> std::size_t + // clang-format on { return cx_min( (size_of_small_vector(min_inline_capacity) - 1U) / sizeof(T), @@ -218,7 +224,8 @@ namespace hpx::detail { // note: Allocator is currently unused template > + typename Allocator = std::allocator, + bool emulate_inplace_vector = false> class small_vector { static_assert(MinInlineCapacity <= 127, @@ -249,7 +256,10 @@ namespace hpx::detail { [[nodiscard]] constexpr auto is_direct() const noexcept -> bool { - return (m_data[0] & 1U) != 0U; + if constexpr (emulate_inplace_vector) + return true; + else + return (m_data[0] & 1U) != 0U; } [[nodiscard]] auto indirect() noexcept -> storage* @@ -362,9 +372,11 @@ namespace hpx::detail { } } + // clang-format off [[nodiscard]] static constexpr auto calculate_new_capacity( std::size_t size_to_fit, std::size_t starting_capacity) noexcept -> std::size_t + // clang-format on { if (size_to_fit == 0) { @@ -539,9 +551,20 @@ namespace hpx::detail { template void assign(It first, It last, std::forward_iterator_tag /*unused*/) { + auto s = std::distance(first, last); + if constexpr (emulate_inplace_vector) + { + // Can not have an inplace_vector with a size larger than N + + // Enforce same signedness (forward iterator so s > 0) + if (static_cast(s) > capacity()) + { + throw std::bad_alloc(); + } + } + clear(); - auto s = std::distance(first, last); reserve(s); std::uninitialized_copy(first, last, data()); set_size(s); @@ -577,7 +600,7 @@ namespace hpx::detail { // * source_begin <= target_begin // * source_end onwards is uninitialized memory // - // Destroys then empty elements in [source_begin, source_end) + // Destroys the empty elements in [source_begin, source_end) auto shift_right( T* source_begin, T* source_end, T* target_begin) noexcept { @@ -594,7 +617,7 @@ namespace hpx::detail { std::destroy(source_begin, (std::min)(source_end, target_begin)); } - // makes space for uninitialized data of cout elements. Also updates + // makes space for uninitialized data of count elements. Also updates // size. template [[nodiscard]] auto make_uninitialized_space_new( @@ -602,7 +625,7 @@ namespace hpx::detail { { auto target = small_vector(); - // we know target is indirect because we're increasing capacity + // we know target is indirect because we are increasing capacity target.reserve(s + count); // move everything [begin, pos[ @@ -634,7 +657,7 @@ namespace hpx::detail { return p; } - // makes space for uninitialized data of cout elements. Also updates size. + // makes space for uninitialized data of count elements. Also updates size. [[nodiscard]] auto make_uninitialized_space( T const* pos, std::size_t count) -> T* { @@ -710,12 +733,28 @@ namespace hpx::detail { std::size_t count, T const& value, Allocator const& = Allocator()) : small_vector() { + if constexpr (emulate_inplace_vector) + { + // Can not have an inplace_vector with a size larger than N + if (count > N) + { + throw std::bad_alloc(); + } + } resize(count, value); } explicit small_vector(std::size_t count, Allocator const& = Allocator()) : small_vector() { + if constexpr (emulate_inplace_vector) + { + // Can not have an inplace_vector with a size larger than N + if (count > N) + { + throw std::bad_alloc(); + } + } reserve(count); if (is_direct()) { @@ -741,7 +780,14 @@ namespace hpx::detail { : small_vector() { auto s = other.size(); - reserve(s); + // If both vectors are direct with the same capacity it will fit + // without further allocations needed. + + if constexpr (!emulate_inplace_vector) + { + reserve(s); + } + std::uninitialized_copy(other.begin(), other.end(), begin()); set_size(s); } @@ -816,6 +862,15 @@ namespace hpx::detail { void resize(std::size_t count) { + if constexpr (emulate_inplace_vector) + { + // Static vector cannot be resized beyond capacity + if (count > N) + { + throw std::bad_alloc(); + } + } + if (count > capacity()) { reserve(count); @@ -833,6 +888,15 @@ namespace hpx::detail { void resize(std::size_t count, value_type const& value) { + if constexpr (emulate_inplace_vector) + { + // Static vector cannot resize beyond capacity + if (count > N) + { + throw std::bad_alloc(); + } + } + if (count > capacity()) { reserve(count); @@ -850,16 +914,33 @@ namespace hpx::detail { auto reserve(std::size_t s) { - auto const old_capacity = capacity(); - auto const new_capacity = calculate_new_capacity(s, old_capacity); - if (new_capacity > old_capacity) + if constexpr (emulate_inplace_vector) { - realloc(new_capacity); + // Static vector cannot reserve beyond capacity + if (s > N) + { + throw std::bad_alloc(); + } + // No-op + } + else + { + auto const old_capacity = capacity(); + auto const new_capacity = + calculate_new_capacity(s, old_capacity); + if (new_capacity > old_capacity) + { + realloc(new_capacity); + } } } [[nodiscard]] constexpr auto capacity() const noexcept -> std::size_t { + if constexpr (emulate_inplace_vector) + { + return capacity(); + } if (is_direct()) { return capacity(); @@ -894,32 +975,46 @@ namespace hpx::detail { template auto emplace_back(Args&&... args) -> T& { - std::size_t s; // NOLINT(cppcoreguidelines-init-variables) - if (is_direct()) + if constexpr (emulate_inplace_vector) { - s = direct_size(); - if (s < N) + std::size_t s = direct_size(); + if (s == N) { - set_direct_and_size(s + 1); - return *hpx::construct_at( - static_cast(direct_data() + s), - HPX_FORWARD(Args, args)...); + throw std::bad_alloc(); } - realloc(calculate_new_capacity(N + 1, N)); + set_direct_and_size(s + 1); + return *hpx::construct_at(static_cast(direct_data() + s), + HPX_FORWARD(Args, args)...); } else { - s = size(); - if (s == capacity()) + std::size_t s; // NOLINT(cppcoreguidelines-init-variables) + if (is_direct()) { - realloc(calculate_new_capacity(s + 1, s)); + s = direct_size(); + if (s < N) + { + set_direct_and_size(s + 1); + return *hpx::construct_at( + static_cast(direct_data() + s), + HPX_FORWARD(Args, args)...); + } + realloc(calculate_new_capacity(N + 1, N)); + } + else + { + s = size(); + if (s == capacity()) + { + realloc(calculate_new_capacity(s + 1, s)); + } } - } - set_size(s + 1); - return *hpx::construct_at( - static_cast(data() + s), - HPX_FORWARD(Args, args)...); + set_size(s + 1); + return *hpx::construct_at( + static_cast(data() + s), + HPX_FORWARD(Args, args)...); + } } void push_back(T const& value) @@ -932,8 +1027,10 @@ namespace hpx::detail { emplace_back(HPX_MOVE(value)); } + // clang-format off [[nodiscard]] auto operator[](std::size_t idx) const noexcept -> T const& + // clang-format on { return *(data() + idx); } @@ -978,11 +1075,20 @@ namespace hpx::detail { [[nodiscard]] auto end() const noexcept -> T const* { - if (is_direct()) + if constexpr (emulate_inplace_vector) { return data() + size(); } - return data() + size(); + else + { + if (is_direct()) + { + return data() + + size(); + } + return data() + + size(); + } } [[nodiscard]] auto cend() const noexcept -> T const* @@ -1097,7 +1203,10 @@ namespace hpx::detail { [[nodiscard]] static constexpr auto max_size() -> std::size_t { - return (std::numeric_limits::max)(); + if constexpr (emulate_inplace_vector) + return N; + else + return (std::numeric_limits::max)(); } void swap(small_vector& other) noexcept @@ -1110,26 +1219,43 @@ namespace hpx::detail { { // per the standard we wouldn't need to do anything here. But since // we are so nice, let's do the shrink. - auto const c = capacity(); - auto const s = size(); - if (s >= c) - { - return; - } - auto new_capacity = calculate_new_capacity(s, N); - if (new_capacity == c) + if constexpr (emulate_inplace_vector) { - // nothing change! + // Can not change the capacity of a static vector so noop return; } + else + { + auto const c = capacity(); + auto const s = size(); + if (s >= c) + { + return; + } - realloc(new_capacity); + auto new_capacity = calculate_new_capacity(s, N); + if (new_capacity == c) + { + // nothing change! + return; + } + + realloc(new_capacity); + } } template auto emplace(const_iterator pos, Args&&... args) -> iterator { + if constexpr (emulate_inplace_vector) + { + // it will be expanded by one element + if (direct_size() + 1 > N) + { + throw std::bad_alloc(); + } + } auto* p = make_uninitialized_space(pos, 1); return hpx::construct_at( static_cast(p), HPX_FORWARD(Args, args)...); @@ -1145,9 +1271,18 @@ namespace hpx::detail { return emplace(pos, HPX_MOVE(value)); } + // clang-format off auto insert(const_iterator pos, std::size_t count, T const& value) -> iterator + // clang-format on { + if constexpr (emulate_inplace_vector) + { + if (direct_size() + count > N) + { + throw std::bad_alloc(); + } + } auto* p = make_uninitialized_space(pos, count); std::uninitialized_fill_n(p, count, value); return p; @@ -1170,6 +1305,8 @@ namespace hpx::detail { auto s = size(); while (first != last) { + // if we are emulating inplace_vector, the out-of-bounds + // emplacement is caught in emplace_back emplace_back(*first); ++first; } @@ -1185,7 +1322,15 @@ namespace hpx::detail { auto insert(const_iterator pos, It first, It last, std::forward_iterator_tag /*unused*/) { - auto* p = make_uninitialized_space(pos, std::distance(first, last)); + auto d = std::distance(first, last); + if constexpr (emulate_inplace_vector) + { + if (direct_size() + d > N) + { + throw std::bad_alloc(); + } + } + auto* p = make_uninitialized_space(pos, d); std::uninitialized_copy(first, last, p); return p; } @@ -1219,49 +1364,83 @@ namespace hpx::detail { } }; - template - [[nodiscard]] constexpr auto operator==(small_vector const& a, - small_vector const& b) noexcept -> bool + // clang-format off + template + [[nodiscard]] constexpr auto operator==( + small_vector const& a, + small_vector const& b) noexcept + -> bool + // clang-format on { return std::equal(a.begin(), a.end(), b.begin(), b.end()); } - template - [[nodiscard]] constexpr auto operator!=(small_vector const& a, - small_vector const& b) noexcept -> bool + // clang-format off + template + [[nodiscard]] constexpr auto operator!=( + small_vector const& a, + small_vector const& b) noexcept + -> bool + // clang-format on { return !(a == b); } - template - [[nodiscard]] constexpr auto operator<(small_vector const& a, - small_vector const& b) noexcept -> bool + // clang-format off + template + [[nodiscard]] constexpr auto operator<( + small_vector const& a, + small_vector const& b) noexcept + -> bool + // clang-format on { return std::lexicographical_compare( a.begin(), a.end(), b.begin(), b.end()); } - template - [[nodiscard]] constexpr auto operator>=(small_vector const& a, - small_vector const& b) noexcept -> bool + // clang-format off + template + [[nodiscard]] constexpr auto operator>=( + small_vector const& a, + small_vector const& b) noexcept + -> bool + // clang-format on { return !(a < b); } - template - [[nodiscard]] constexpr auto operator>(small_vector const& a, - small_vector const& b) noexcept -> bool + // clang-format off + template + [[nodiscard]] constexpr auto operator>( + small_vector const& a, + small_vector const& b) noexcept + -> bool + // clang-format on { return std::lexicographical_compare( b.begin(), b.end(), a.begin(), a.end()); } - template - [[nodiscard]] constexpr auto operator<=(small_vector const& a, - small_vector const& b) noexcept -> bool + // clang-format off + template + [[nodiscard]] constexpr auto operator<=( + small_vector const& a, + small_vector const& b) noexcept + -> bool + // clang-format on { return !(a > b); } + + template + using inplace_vector = small_vector; } // namespace hpx::detail // NOLINTNEXTLINE(cert-dcl58-cpp) diff --git a/libs/core/datastructures/tests/unit/CMakeLists.txt b/libs/core/datastructures/tests/unit/CMakeLists.txt index 5d4fdb4ab645..edaaa19a6737 100644 --- a/libs/core/datastructures/tests/unit/CMakeLists.txt +++ b/libs/core/datastructures/tests/unit/CMakeLists.txt @@ -15,6 +15,7 @@ set(tests dynamic_bitset5 flat_map flat_set + inplace_vector intrusive_list is_tuple_like serializable_any diff --git a/libs/core/datastructures/tests/unit/inplace_vector.cpp b/libs/core/datastructures/tests/unit/inplace_vector.cpp new file mode 100644 index 000000000000..579d725af071 --- /dev/null +++ b/libs/core/datastructures/tests/unit/inplace_vector.cpp @@ -0,0 +1,772 @@ +// Copyright (c) 2021-2024 Hartmut Kaiser +// Copyright (c) 2024 Isidoros Tsaousis-Seiras +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// (C) Copyright Ion Gaztanaga 2004-2014. Distributed under the Boost +// Copyright (C) 2013 Cromwell D. Enage +// +// See http://www.boost.org/libs/container for documentation. + +#include + +#if !defined(HPX_HAVE_HIP) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace test { + + class movable_and_copyable_int + { + public: + static inline unsigned int count = 0; + + movable_and_copyable_int() noexcept + : int_(0) + { + ++count; + } + + explicit movable_and_copyable_int(int a) noexcept + : int_(a) + { + // Disallow INT_MIN + HPX_TEST(this->int_ != INT_MIN); + ++count; + } + + movable_and_copyable_int(movable_and_copyable_int const& mmi) noexcept + : int_(mmi.int_) + { + ++count; + } + + movable_and_copyable_int(movable_and_copyable_int&& mmi) noexcept + : int_(mmi.int_) + { + mmi.int_ = 0; + ++count; + } + + ~movable_and_copyable_int() + { + HPX_TEST(this->int_ != INT_MIN); + this->int_ = INT_MIN; + --count; + } + + movable_and_copyable_int& operator=( + movable_and_copyable_int const& mi) noexcept + { + this->int_ = mi.int_; + return *this; + } + + movable_and_copyable_int& operator=( + movable_and_copyable_int&& mmi) noexcept + { + this->int_ = mmi.int_; + mmi.int_ = 0; + return *this; + } + + movable_and_copyable_int& operator=(int i) noexcept + { + this->int_ = i; + HPX_TEST(this->int_ != INT_MIN); + return *this; + } + + friend bool operator==(const movable_and_copyable_int& l, + const movable_and_copyable_int& r) noexcept + { + return l.int_ == r.int_; + } + + friend bool operator!=(const movable_and_copyable_int& l, + const movable_and_copyable_int& r) noexcept + { + return l.int_ != r.int_; + } + + friend bool operator<(const movable_and_copyable_int& l, + const movable_and_copyable_int& r) noexcept + { + return l.int_ < r.int_; + } + + friend bool operator<=(const movable_and_copyable_int& l, + const movable_and_copyable_int& r) noexcept + { + return l.int_ <= r.int_; + } + + friend bool operator>=(const movable_and_copyable_int& l, + const movable_and_copyable_int& r) noexcept + { + return l.int_ >= r.int_; + } + + friend bool operator>(const movable_and_copyable_int& l, + const movable_and_copyable_int& r) noexcept + { + return l.int_ > r.int_; + } + + int get_int() const noexcept + { + return int_; + } + + friend bool operator==( + const movable_and_copyable_int& l, int r) noexcept + { + return l.get_int() == r; + } + + friend bool operator==( + int l, const movable_and_copyable_int& r) noexcept + { + return l == r.get_int(); + } + + private: + int int_; + }; +} // namespace test + +namespace hpx { + + // Explicit instantiation to detect compilation errors + + // inplace_vector is an alias of small_vector so we have to write it out + // explicitly: + /* + template + using inplace_vector = small_vector< + T, + MinInlineCapacity, + std::monostate, // Ignore the allocator + true>; // emulate inplace_vector + */ + + // inplace_vector + template class hpx::detail::small_vector; + + // inplace_vector + template class hpx::detail::small_vector; + + // inplace_vector + template class hpx::detail::small_vector; + + // inplace_vector + template class hpx::detail::small_vector; + + // inplace_vector + template class hpx::detail::small_vector; + + // inplace_vector + template class hpx::detail::small_vector; + + // inplace_vector + template class hpx::detail::small_vector; +} // namespace hpx + +namespace test { + + void inplace_vector_test() + { + // basic test with fewer elements than static size + { + using sm5_t = hpx::detail::small_vector; + static_assert( + sm5_t::static_capacity == 5, "sm5_t::static_capacity == 5"); + + sm5_t sm5; + sm5.push_back(1); + HPX_TEST_EQ(sm5[0], 1); + + sm5_t const sm5_copy(sm5); + HPX_TEST(sm5 == sm5_copy); + } + { + using sm7_t = hpx::detail::inplace_vector; + static_assert( + sm7_t::static_capacity == 7, "sm7_t::static_capacity == 7"); + + sm7_t sm7; + sm7.push_back(1); + HPX_TEST_EQ(sm7[0], 1); + + sm7_t const sm7_copy(sm7); + HPX_TEST(sm7 == sm7_copy); + } + { + using sm5_t = hpx::detail::inplace_vector; + sm5_t sm5; + sm5.push_back(1); + HPX_TEST_EQ(sm5[0], 1); + + sm5_t sm5_copy(sm5); + HPX_TEST(sm5 == sm5_copy); + + sm5.push_back(2); + HPX_TEST_EQ(sm5[1], 2); + HPX_TEST_EQ(sm5.size(), static_cast(2)); + + sm5_copy = sm5; + HPX_TEST(sm5 == sm5_copy); + + sm5[0] = 3; + HPX_TEST_EQ(sm5[0], 3); + HPX_TEST_EQ(sm5_copy[0], 1); + + sm5_copy = sm5; + sm5_t sm5_move(std::move(sm5)); + sm5 = sm5_t(); + HPX_TEST(sm5_move == sm5_copy); + + sm5 = sm5_copy; + sm5_move = std::move(sm5); + sm5 = sm5_t(); + HPX_TEST(sm5_move == sm5_copy); + } + + // basic test with more elements than static size + { + using sm2_t = hpx::detail::inplace_vector; + sm2_t sm2; + sm2.push_back(1); + HPX_TEST_EQ(sm2[0], 1); + + sm2_t sm2_copy(sm2); + HPX_TEST(sm2 == sm2_copy); + + sm2.push_back(2); + sm2.push_back(3); + HPX_TEST_EQ(sm2[1], 2); + HPX_TEST_EQ(sm2[2], 3); + HPX_TEST_EQ(sm2.size(), static_cast(3)); + + sm2_copy = sm2; + HPX_TEST(sm2 == sm2_copy); + + sm2[2] = 4; + HPX_TEST_EQ(sm2[2], 4); + HPX_TEST_EQ(sm2_copy[2], 3); + + sm2_copy = sm2; + sm2_t sm2_move(std::move(sm2)); + sm2 = sm2_t(); + HPX_TEST(sm2_move == sm2_copy); + + sm2 = sm2_copy; + sm2_move = std::move(sm2); + sm2 = sm2_t(); + HPX_TEST(sm2_move == sm2_copy); + } + } + + // inplace vector has internal storage so some special swap cases must be + // tested + void test_swap() + { + using vec = hpx::detail::inplace_vector; + + { + // v has elements, w empty + vec v; + for (std::size_t i = 0, max = v.capacity() - 1; i != max; ++i) + { + v.push_back(static_cast(i)); + } + + vec w; + + vec const v_copy(v); + vec const w_copy(w); + + v.swap(w); + HPX_TEST(v == w_copy); + HPX_TEST(w == v_copy); + } + { + // v & w have elements + vec v; + for (std::size_t i = 0, max = v.capacity() - 1; i != max; ++i) + { + v.push_back(static_cast(i)); + } + + vec w; + for (std::size_t i = 0, max = v.capacity() / 2; i != max; ++i) + { + w.push_back(static_cast(i)); + } + + vec const v_copy(v); + vec const w_copy(w); + + v.swap(w); + HPX_TEST(v == w_copy); + HPX_TEST(w == v_copy); + } + } + + /////////////////////////////////////////////////////////////////////////// + template + void check_equal_containers(ContA const& cont_a, ContB const& cont_b) + { + HPX_TEST_EQ(cont_a.size(), cont_b.size()); + + auto itcont_a(cont_a.begin()); + auto itcont_a_end(cont_a.end()); + std::size_t dist = std::distance(itcont_a, itcont_a_end); + HPX_TEST_EQ(dist, cont_a.size()); + + auto itcont_b(cont_b.begin()); + auto itcont_b_end(cont_b.end()); + std::size_t dist2 = std::distance(itcont_b, itcont_b_end); + HPX_TEST_EQ(dist2, cont_b.size()); + + for (std::size_t i = 0; itcont_a != itcont_a_end; + ++itcont_a, ++itcont_b, ++i) + { + HPX_TEST_EQ(*itcont_a, *itcont_b); + } + } + + template + void test_insert_range(std::deque& std_deque, + SeqContainer& seq_container, std::deque const& input_deque, + std::size_t index) + { + HPX_ASSERT(std::size(input_deque) + std::size(seq_container) <= + seq_container.max_size()); + check_equal_containers(std_deque, seq_container); + + std_deque.insert( + std_deque.begin() + index, input_deque.begin(), input_deque.end()); + seq_container.insert(seq_container.begin() + index, input_deque.begin(), + input_deque.end()); + + check_equal_containers(std_deque, seq_container); + } + + template + void test_range_insertion() + { + using value_type = typename SeqContainer::value_type; + constexpr auto max_size = SeqContainer::static_capacity; + static_assert( + max_size > 100, "max_size must be greater than 100 for this test."); + + std::deque input_deque; + for (int element = -10; element < 10; ++element) + { + input_deque.push_back(element + 20); + } + + for (std::size_t i = 0; i <= input_deque.size(); ++i) + { + std::deque std_deque; + SeqContainer seq_container; + + for (int element = -10; element < 10; ++element) + { + std_deque.push_back(element); + seq_container.push_back(value_type(element)); + } + + test_insert_range(std_deque, seq_container, input_deque, i); + } + } + + /////////////////////////////////////////////////////////////////////////// + class emplace_int + { + public: + explicit emplace_int( + int a = 0, int b = 0, int c = 0, int d = 0, int e = 0) noexcept + : a_(a) + , b_(b) + , c_(c) + , d_(d) + , e_(e) + { + } + + emplace_int(emplace_int const& o) = delete; + emplace_int(emplace_int&& o) noexcept + : a_(o.a_) + , b_(o.b_) + , c_(o.c_) + , d_(o.d_) + , e_(o.e_) + { + } + + emplace_int& operator=(emplace_int const& o) = delete; + emplace_int& operator=(emplace_int&& o) noexcept + { + a_ = o.a_; + b_ = o.b_; + c_ = o.c_; + d_ = o.d_; + e_ = o.e_; + return *this; + } + + friend bool operator==(emplace_int const& l, emplace_int const& r) + { + return l.a_ == r.a_ && l.b_ == r.b_ && l.c_ == r.c_ && + l.d_ == r.d_ && l.e_ == r.e_; + } + + friend bool operator<(emplace_int const& l, emplace_int const& r) + { + return l.sum() < r.sum(); + } + + friend bool operator>(emplace_int const& l, emplace_int const& r) + { + return l.sum() > r.sum(); + } + + friend bool operator!=(emplace_int const& l, emplace_int const& r) + { + return !(l == r); + } + + ~emplace_int() + { + a_ = b_ = c_ = d_ = e_ = 0; + } + + int sum() const + { + return a_ + b_ + c_ + d_ + e_; + } + + int a_, b_, c_, d_, e_; + int padding[6] = {}; + }; + + static emplace_int expected[10]; + + template + void test_expected_container(Container const& ec, + emplace_int const* expected, unsigned int only_first_n, + unsigned int cont_offset = 0) + { + HPX_TEST(cont_offset <= ec.size()); + HPX_TEST(only_first_n <= (ec.size() - cont_offset)); + + using const_iterator = typename Container::const_iterator; + + const_iterator itb(ec.begin()), ite(ec.end()); + unsigned int cur = 0; + while (cont_offset--) + { + ++itb; + } + + for (; itb != ite && only_first_n--; ++itb, ++cur) + { + emplace_int const& cr = *itb; + HPX_TEST(cr == expected[cur]); + } + } + + template + void test_emplace_back() + { + { + new (&expected[0]) emplace_int(); + new (&expected[1]) emplace_int(1); + new (&expected[2]) emplace_int(1, 2); + new (&expected[3]) emplace_int(1, 2, 3); + new (&expected[4]) emplace_int(1, 2, 3, 4); + new (&expected[5]) emplace_int(1, 2, 3, 4, 5); + + Container c; + using reference = typename Container::reference; + + { + reference r = c.emplace_back(); + HPX_TEST(&r == &c.back()); + test_expected_container(c, &expected[0], 1); + } + { + reference r = c.emplace_back(1); + HPX_TEST(&r == &c.back()); + test_expected_container(c, &expected[0], 2); + } + + c.emplace_back(1, 2); + test_expected_container(c, &expected[0], 3); + + c.emplace_back(1, 2, 3); + test_expected_container(c, &expected[0], 4); + + c.emplace_back(1, 2, 3, 4); + test_expected_container(c, &expected[0], 5); + + c.emplace_back(1, 2, 3, 4, 5); + test_expected_container(c, &expected[0], 6); + } + } + + template + void test_emplace_before() + { + { + new (&expected[0]) emplace_int(); + new (&expected[1]) emplace_int(1); + new (&expected[2]) emplace_int(); + + Container c; + c.emplace(c.cend(), 1); + c.emplace(c.cbegin()); + test_expected_container(c, &expected[0], 2); + + c.emplace(c.cend()); + test_expected_container(c, &expected[0], 3); + } + { + new (&expected[0]) emplace_int(); + new (&expected[1]) emplace_int(1); + new (&expected[2]) emplace_int(1, 2); + new (&expected[3]) emplace_int(1, 2, 3); + new (&expected[4]) emplace_int(1, 2, 3, 4); + new (&expected[5]) emplace_int(1, 2, 3, 4, 5); + + // emplace_front-like + Container c; + c.emplace(c.cbegin(), 1, 2, 3, 4, 5); + c.emplace(c.cbegin(), 1, 2, 3, 4); + c.emplace(c.cbegin(), 1, 2, 3); + c.emplace(c.cbegin(), 1, 2); + c.emplace(c.cbegin(), 1); + c.emplace(c.cbegin()); + test_expected_container(c, &expected[0], 6); + c.clear(); + + // emplace_back-like + auto i = c.emplace(c.cend()); + test_expected_container(c, &expected[0], 1); + + i = c.emplace(++i, 1); + test_expected_container(c, &expected[0], 2); + + i = c.emplace(++i, 1, 2); + test_expected_container(c, &expected[0], 3); + + i = c.emplace(++i, 1, 2, 3); + test_expected_container(c, &expected[0], 4); + + i = c.emplace(++i, 1, 2, 3, 4); + test_expected_container(c, &expected[0], 5); + + i = c.emplace(++i, 1, 2, 3, 4, 5); + test_expected_container(c, &expected[0], 6); + c.clear(); + + // emplace in the middle + c.emplace(c.cbegin()); + test_expected_container(c, &expected[0], 1); + + i = c.emplace(c.cend(), 1, 2, 3, 4, 5); + test_expected_container(c, &expected[0], 1); + + test_expected_container(c, &expected[5], 1, 1); + + i = c.emplace(i, 1, 2, 3, 4); + test_expected_container(c, &expected[0], 1); + + test_expected_container(c, &expected[4], 2, 1); + + i = c.emplace(i, 1, 2, 3); + test_expected_container(c, &expected[0], 1); + test_expected_container(c, &expected[3], 3, 1); + + i = c.emplace(i, 1, 2); + test_expected_container(c, &expected[0], 1); + test_expected_container(c, &expected[2], 4, 1); + + i = c.emplace(i, 1); + test_expected_container(c, &expected[0], 6); + } + } + + template + void test_vector_methods_with_initializer_list_as_argument_for() + { + using allocator_type = typename VectorContainerType::allocator_type; + + { + VectorContainerType const tested_vector = {1, 2, 3}; + std::vector const expected_vector = {1, 2, 3}; + check_equal_containers(tested_vector, expected_vector); + } + { + VectorContainerType const tested_vector( + {1, 2, 3}, allocator_type()); + std::vector const expected_vector = {1, 2, 3}; + check_equal_containers(tested_vector, expected_vector); + } + { + VectorContainerType tested_vector = {1, 2, 3}; + tested_vector = {11, 12, 13}; + + std::vector const expected_vector = {11, 12, 13}; + check_equal_containers(tested_vector, expected_vector); + } + + { + VectorContainerType tested_vector = {1, 2, 3}; + tested_vector.assign({5, 6, 7}); + + std::vector const expected_vector = {5, 6, 7}; + check_equal_containers(tested_vector, expected_vector); + } + + { + VectorContainerType tested_vector = {1, 2, 3}; + tested_vector.insert(tested_vector.cend(), {5, 6, 7}); + + std::vector const expected_vector = {1, 2, 3, 5, 6, 7}; + check_equal_containers(tested_vector, expected_vector); + } + } + + void test_general() + { + using hpx::detail::inplace_vector; + + { + inplace_vector const v1 = {1, 2, 3, 4, 5}; + inplace_vector const v2 = {1, 2, 3, 4, 5}; + + HPX_TEST(v1 == v2); + } + { + inplace_vector const v_init_list = {1, 2, 3, 4, 5}; + inplace_vector v_push_back; + inplace_vector v_emplace_back; + inplace_vector v_insert; + + for (int i = 0; i < 5; ++i) + { + v_push_back.push_back(i); + v_emplace_back.emplace_back(i); + v_insert.insert(v_insert.end(), i); + } + + HPX_TEST(v_init_list == v_push_back); + HPX_TEST(v_init_list == v_emplace_back); + HPX_TEST(v_init_list == v_insert); + } + } + + void test_exceptions() + { + using hpx::detail::inplace_vector; + { + inplace_vector v = {1, 2, 3, 4, 5}; + try + { + v.push_back(42); + } + catch (std::bad_alloc&) + { + } + catch (...) + { + HPX_ASSERT(false); + } + } + { + inplace_vector v = {1, 2, 3, 4, 5}; + try + { + v.emplace_back(42); + } + catch (std::bad_alloc&) + { + } + catch (...) + { + HPX_ASSERT(false); + } + } + { + inplace_vector v = {1, 2, 3, 4, 5}; + try + { + v.insert(v.end(), 3); + } + catch (std::bad_alloc&) + { + } + catch (...) + { + HPX_ASSERT(false); + } + } + { + inplace_vector v = {1, 2, 3, 4, 5}; + try + { + (void) v.at(6); + } + catch (std::out_of_range&) + { + } + catch (...) + { + HPX_ASSERT(false); + } + } + } +} // namespace test + +int main() +{ + test::inplace_vector_test(); + test::test_swap(); + test::test_exceptions(); + + // Emplace testing + test::test_emplace_before< + hpx::detail::inplace_vector>(); + + // Initializer lists testing + test::test_vector_methods_with_initializer_list_as_argument_for< + hpx::detail::inplace_vector>(); + + return hpx::util::report_errors(); +} + +#else + +int main() +{ + return 0; +} + +#endif