From 43c1b97e2e17100480eafebe47735b6ba1f5ffce Mon Sep 17 00:00:00 2001 From: Kohei Takahashi Date: Wed, 29 May 2024 20:25:10 +0900 Subject: [PATCH 1/7] Added test for stateful allocator --- test/test_case/deduction_guide.ipp | 15 ++------------- test/test_case/memory.hpp | 25 +++++++++++++++++++++++++ test/tied_sequence.cpp | 13 +++++++++++++ 3 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 test/test_case/memory.hpp diff --git a/test/test_case/deduction_guide.ipp b/test/test_case/deduction_guide.ipp index dc32dd2..b8f5c0e 100644 --- a/test/test_case/deduction_guide.ipp +++ b/test/test_case/deduction_guide.ipp @@ -1,4 +1,4 @@ -// Copyright (c) 2021,2023 Kohei Takahashi +// Copyright (c) 2021,2023-2024 Kohei Takahashi // This software is released under the MIT License, see LICENSE. #include @@ -7,18 +7,7 @@ #include #include "config.hpp" - -template -struct stateful_allocator : public std::allocator -{ - std::string state; - - // Hide std::allocator::rebind for C++17 mode. - template - struct rebind { using other = stateful_allocator; }; - - stateful_allocator(char const* state) : state{state} {}; -}; +#include "memory.hpp" TEST_CASE("deduction guide", "[deduction guide]") { diff --git a/test/test_case/memory.hpp b/test/test_case/memory.hpp new file mode 100644 index 0000000..6c8ffea --- /dev/null +++ b/test/test_case/memory.hpp @@ -0,0 +1,25 @@ +// Copyright (c) 2024 Kohei Takahashi +// This software is released under the MIT License, see LICENSE. + +#pragma once + +template +struct stateful_allocator : public std::allocator +{ + std::string state; + + // Hide std::allocator::rebind for C++17 mode. + template + struct rebind { using other = stateful_allocator; }; + + stateful_allocator(char const* state) : state{state} {} + + stateful_allocator(stateful_allocator const&) noexcept = default; + stateful_allocator(stateful_allocator&&) noexcept = default; + + template + stateful_allocator(stateful_allocator const& other) noexcept : state{other.state} { } + + template + stateful_allocator(stateful_allocator&& other) noexcept : state{std::move(other.state)} { } +}; diff --git a/test/tied_sequence.cpp b/test/tied_sequence.cpp index a1840af..86d318f 100644 --- a/test/tied_sequence.cpp +++ b/test/tied_sequence.cpp @@ -11,6 +11,7 @@ #include "flat_map/tied_sequence.hpp" #include "test_case/catch2_tuple.hpp" +#include "test_case/memory.hpp" TEST_CASE("zip_iterator", "[iterator]") { @@ -1086,3 +1087,15 @@ TEST_CASE("non contiguous", "[contiguous]") [[maybe_unused]] flat_map::tied_sequence, std::deque> ts; } } + +TEST_CASE("allocator", "[allocator]") +{ + SECTION("stateful allocator") + { + [[maybe_unused]] flat_map::tied_sequence>, std::deque>> ts + { + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + }; + } +} From e5afb26f977e6364887f311eafae93128701e7d6 Mon Sep 17 00:00:00 2001 From: Kohei Takahashi Date: Thu, 30 May 2024 10:59:17 +0900 Subject: [PATCH 2/7] Added allocator forwarder --- flat_map/__memory.hpp | 31 ++++++++++++++++++++++++++----- flat_map/tied_sequence.hpp | 4 ++-- test/test_case/memory.hpp | 2 ++ test/tied_sequence.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/flat_map/__memory.hpp b/flat_map/__memory.hpp index 38c2b3f..ca9b456 100644 --- a/flat_map/__memory.hpp +++ b/flat_map/__memory.hpp @@ -1,21 +1,34 @@ -// Copyright (c) 2021 Kohei Takahashi +// Copyright (c) 2021,2024 Kohei Takahashi // This software is released under the MIT License, see LICENSE. #pragma once #include #include +#include -namespace flat_map::detail +namespace flat_map { -struct fake_allocator +namespace detail { - template - struct rebind { using other = fake_allocator; }; + +template +struct fake_allocator : private AllocatorTuple +{ + template + struct rebind { using other = fake_allocator; }; using value_type = void; + fake_allocator() noexcept = default; + + explicit fake_allocator(AllocatorTuple&& tuple) noexcept : AllocatorTuple{std::move(tuple)} { } + explicit fake_allocator(AllocatorTuple const& tuple) noexcept : AllocatorTuple{tuple} { } + + template decltype(auto) get() noexcept { return std::get(static_cast(*this)); } + template decltype(auto) get() const noexcept { return std::get(static_cast(*this)); } + [[noreturn]] void* allocate(std::size_t) { throw std::bad_alloc(); } constexpr void deallocate(void*, std::size_t) { } @@ -25,3 +38,11 @@ struct fake_allocator }; } // namespace flat_map::detail + +template +auto forward_allocator(Allocators&&... alloc) +{ + return detail::fake_allocator{std::tuple(std::forward(alloc)...)}; +} + +} // namespace flat_map diff --git a/flat_map/tied_sequence.hpp b/flat_map/tied_sequence.hpp index 37a0afe..18ea2dc 100644 --- a/flat_map/tied_sequence.hpp +++ b/flat_map/tied_sequence.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021,2023 Kohei Takahashi +// Copyright (c) 2021,2023-2024 Kohei Takahashi // This software is released under the MIT License, see LICENSE. #pragma once @@ -282,7 +282,7 @@ class tied_sequence public: using value_type = std::tuple; - using allocator_type = detail::fake_allocator; + using allocator_type = detail::fake_allocator>; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = std::tuple; diff --git a/test/test_case/memory.hpp b/test/test_case/memory.hpp index 6c8ffea..c22e363 100644 --- a/test/test_case/memory.hpp +++ b/test/test_case/memory.hpp @@ -12,6 +12,8 @@ struct stateful_allocator : public std::allocator template struct rebind { using other = stateful_allocator; }; + stateful_allocator() : state{"unspecified state"} {} + stateful_allocator(char const* state) : state{state} {} stateful_allocator(stateful_allocator const&) noexcept = default; diff --git a/test/tied_sequence.cpp b/test/tied_sequence.cpp index 86d318f..b57e164 100644 --- a/test/tied_sequence.cpp +++ b/test/tied_sequence.cpp @@ -1098,4 +1098,28 @@ TEST_CASE("allocator", "[allocator]") stateful_allocator{"state2"} }; } + + SECTION("allocator forwarding") + { + [[maybe_unused]] flat_map::tied_sequence>, std::deque>> ts + { + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + }; + } + + SECTION("nested") + { + [[maybe_unused]] flat_map::tied_sequence>, std::deque>>> nts + { + flat_map::forward_allocator( + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + ) + }; + } } From cfc964c7f5e8639a242d5553d5529f7867779d4c Mon Sep 17 00:00:00 2001 From: Kohei Takahashi Date: Thu, 6 Jun 2024 13:15:59 +0900 Subject: [PATCH 3/7] Support get_allocator --- flat_map/__memory.hpp | 3 +++ flat_map/tied_sequence.hpp | 5 ++++- test/tied_sequence.cpp | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/flat_map/__memory.hpp b/flat_map/__memory.hpp index ca9b456..ec575a9 100644 --- a/flat_map/__memory.hpp +++ b/flat_map/__memory.hpp @@ -26,6 +26,9 @@ struct fake_allocator : private AllocatorTuple explicit fake_allocator(AllocatorTuple&& tuple) noexcept : AllocatorTuple{std::move(tuple)} { } explicit fake_allocator(AllocatorTuple const& tuple) noexcept : AllocatorTuple{tuple} { } + template + explicit fake_allocator(std::allocator_arg_t, Allocators&&... allocs) noexcept : AllocatorTuple{std::forward(allocs)...} { } + template decltype(auto) get() noexcept { return std::get(static_cast(*this)); } template decltype(auto) get() const noexcept { return std::get(static_cast(*this)); } diff --git a/flat_map/tied_sequence.hpp b/flat_map/tied_sequence.hpp index 18ea2dc..4b150e5 100644 --- a/flat_map/tied_sequence.hpp +++ b/flat_map/tied_sequence.hpp @@ -419,7 +419,10 @@ class tied_sequence constexpr void assign(std::initializer_list ilist) { *this = ilist; } - constexpr allocator_type get_allocator() const noexcept { return {}; } + constexpr allocator_type get_allocator() const noexcept + { + return std::apply([](auto&... seqs) { return allocator_type{std::allocator_arg, seqs.get_allocator()...}; }, _seq); + } constexpr reference at(size_type pos) { diff --git a/test/tied_sequence.cpp b/test/tied_sequence.cpp index b57e164..ea79227 100644 --- a/test/tied_sequence.cpp +++ b/test/tied_sequence.cpp @@ -1097,6 +1097,10 @@ TEST_CASE("allocator", "[allocator]") stateful_allocator{"state1"}, stateful_allocator{"state2"} }; + + auto alloc = ts.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); } SECTION("allocator forwarding") From 8b5a16daa0625b0ec242a31e2358407d2017efc7 Mon Sep 17 00:00:00 2001 From: Kohei Takahashi Date: Thu, 6 Jun 2024 14:17:51 +0900 Subject: [PATCH 4/7] Support allocator forwarding in ctor --- flat_map/__memory.hpp | 11 ++- flat_map/tied_sequence.hpp | 50 ++++++++-- test/test_case/memory.hpp | 3 + test/tied_sequence.cpp | 193 ++++++++++++++++++++++++++++++++++++- 4 files changed, 241 insertions(+), 16 deletions(-) diff --git a/flat_map/__memory.hpp b/flat_map/__memory.hpp index ec575a9..82c49cf 100644 --- a/flat_map/__memory.hpp +++ b/flat_map/__memory.hpp @@ -22,15 +22,20 @@ struct fake_allocator : private AllocatorTuple using value_type = void; fake_allocator() noexcept = default; + fake_allocator(fake_allocator const&) = default; + fake_allocator(fake_allocator&&) = default; - explicit fake_allocator(AllocatorTuple&& tuple) noexcept : AllocatorTuple{std::move(tuple)} { } explicit fake_allocator(AllocatorTuple const& tuple) noexcept : AllocatorTuple{tuple} { } + explicit fake_allocator(AllocatorTuple&& tuple) noexcept : AllocatorTuple{std::move(tuple)} { } template explicit fake_allocator(std::allocator_arg_t, Allocators&&... allocs) noexcept : AllocatorTuple{std::forward(allocs)...} { } + fake_allocator& operator=(fake_allocator const&) noexcept = default; + fake_allocator& operator=(fake_allocator&&) noexcept = default; + template decltype(auto) get() noexcept { return std::get(static_cast(*this)); } - template decltype(auto) get() const noexcept { return std::get(static_cast(*this)); } + template decltype(auto) get() const noexcept { return std::get(static_cast(*this)); } [[noreturn]] void* allocate(std::size_t) { throw std::bad_alloc(); } @@ -45,7 +50,7 @@ struct fake_allocator : private AllocatorTuple template auto forward_allocator(Allocators&&... alloc) { - return detail::fake_allocator{std::tuple(std::forward(alloc)...)}; + return detail::fake_allocator>{std::allocator_arg, std::forward(alloc)...}; } } // namespace flat_map diff --git a/flat_map/tied_sequence.hpp b/flat_map/tied_sequence.hpp index 4b150e5..9e72d52 100644 --- a/flat_map/tied_sequence.hpp +++ b/flat_map/tied_sequence.hpp @@ -295,6 +295,22 @@ class tied_sequence using const_reverse_iterator = std::reverse_iterator; private: + template + constexpr tied_sequence(std::index_sequence, allocator_type const& alloc) + : _seq{alloc.template get()...} { } + + template + constexpr tied_sequence(std::index_sequence, size_type count, allocator_type const& alloc) + : _seq{Sequences(count, alloc.template get())...} { } + + template + constexpr tied_sequence(std::index_sequence, size_type count, T&& value, allocator_type const& alloc) + : _seq{Sequences(count, std::get(std::forward(value)), alloc.template get())...} { } + + template + constexpr tied_sequence(std::index_sequence, T&& value, allocator_type const& alloc) + : _seq{Sequences(std::get(std::forward(value)), alloc.template get())...} { } + template constexpr tied_sequence(std::index_sequence, size_type count, T&& value, typename Sequences::allocator_type const&... alloc) : _seq{Sequences(count, std::get(std::forward(value)), alloc)...} { } @@ -303,6 +319,10 @@ class tied_sequence constexpr tied_sequence(std::index_sequence, T&& value, typename Sequences::allocator_type const&... alloc) : _seq{Sequences(std::get(std::forward(value)), alloc)...} { } + template + constexpr tied_sequence(std::index_sequence indices, InputIterator first, InputIterator last, allocator_type const& alloc) + : tied_sequence{indices, typename std::iterator_traits::iterator_category{}, first, last, alloc.template get()...} { } + template constexpr tied_sequence(std::index_sequence, std::input_iterator_tag, InputIterator, InputIterator, typename Sequences::allocator_type const&...) { @@ -314,6 +334,10 @@ class tied_sequence constexpr tied_sequence(std::index_sequence, std::forward_iterator_tag, ForwardIterator first, ForwardIterator last, typename Sequences::allocator_type const&... alloc) : _seq{Sequences(detail::unzip(first), detail::unzip(last), alloc)...} { } + template + constexpr tied_sequence(std::index_sequence, std::initializer_list init, allocator_type const& alloc) + : tied_sequence{init, alloc.template get()...} { } + public: constexpr tied_sequence() noexcept((std::is_nothrow_default_constructible_v && ...)) #if FLAT_MAP_WORKAROUND(FLAT_MAP_COMPILER_GCC, < FLAT_MAP_COMPILER_VERSION(10,0,0)) @@ -322,22 +346,24 @@ class tied_sequence = default; #endif - constexpr explicit tied_sequence(allocator_type const&) noexcept(std::is_nothrow_default_constructible_v) : tied_sequence() { } + constexpr explicit tied_sequence(allocator_type const& alloc) noexcept + : tied_sequence(std::index_sequence_for{}, alloc) { } constexpr explicit tied_sequence(typename Sequences::allocator_type const&... alloc) noexcept : _seq{alloc...} { } constexpr tied_sequence(size_type count, value_type const& value) : tied_sequence(count, value, typename Sequences::allocator_type{}...) { } - constexpr tied_sequence(size_type count, value_type const& value, allocator_type const&) - : tied_sequence(count, value) { } + constexpr tied_sequence(size_type count, value_type const& value, allocator_type const& alloc) + : tied_sequence(std::index_sequence_for{}, count, value, alloc) { } constexpr tied_sequence(size_type count, value_type const& value, typename Sequences::allocator_type const&... alloc) : tied_sequence(std::index_sequence_for{}, count, value, alloc...) { } constexpr tied_sequence(size_type count) : tied_sequence(count, typename Sequences::allocator_type{}...) { } - constexpr tied_sequence(size_type count, allocator_type const&) : tied_sequence(count) { } + constexpr tied_sequence(size_type count, allocator_type const& alloc) + : tied_sequence(std::index_sequence_for{}, count, alloc) { } constexpr tied_sequence(size_type count, typename Sequences::allocator_type const&... alloc) : _seq{Sequences(count, alloc)...} { } @@ -347,7 +373,8 @@ class tied_sequence : tied_sequence(first, last, typename Sequences::allocator_type{}...) { } template - constexpr tied_sequence(InputIterator first, InputIterator last, allocator_type const&) : tied_sequence(first, last) { } + constexpr tied_sequence(InputIterator first, InputIterator last, allocator_type const& alloc) + : tied_sequence(std::index_sequence_for{}, first, last, alloc) { } template constexpr tied_sequence(InputIterator first, InputIterator last, typename Sequences::allocator_type const&... alloc) @@ -355,22 +382,25 @@ class tied_sequence constexpr tied_sequence(tied_sequence const&) = default; - constexpr tied_sequence(tied_sequence const& other, allocator_type const&) : tied_sequence(other) { } + constexpr tied_sequence(tied_sequence const& other, allocator_type const& alloc) + : tied_sequence(std::index_sequence_for{}, other._seq, alloc) { } constexpr tied_sequence(tied_sequence const& other, typename Sequences::allocator_type const&... alloc) - : tied_sequence(std::index_sequence_for{}, other, alloc...) { } + : tied_sequence(std::index_sequence_for{}, other._seq, alloc...) { } constexpr tied_sequence(tied_sequence&&) noexcept = default; - constexpr tied_sequence(tied_sequence&& other, allocator_type const&) : tied_sequence(std::move(other)) { } + constexpr tied_sequence(tied_sequence&& other, allocator_type const& alloc) + : tied_sequence(std::index_sequence_for{}, std::move(other._seq), alloc) { } constexpr tied_sequence(tied_sequence&& other, typename Sequences::allocator_type const&... alloc) - : tied_sequence(std::index_sequence_for{}, std::move(other), alloc...) { } + : tied_sequence(std::index_sequence_for{}, std::move(other._seq), alloc...) { } constexpr tied_sequence(std::initializer_list init) : tied_sequence(init, typename Sequences::allocator_type{}...) { } - constexpr tied_sequence(std::initializer_list init, allocator_type const&) : tied_sequence(init) { } + constexpr tied_sequence(std::initializer_list init, allocator_type const& alloc) + : tied_sequence(std::index_sequence_for{}, init, alloc) { } constexpr tied_sequence(std::initializer_list init, typename Sequences::allocator_type const&... alloc) : tied_sequence(init.begin(), init.end(), alloc...) { } diff --git a/test/test_case/memory.hpp b/test/test_case/memory.hpp index c22e363..7b35312 100644 --- a/test/test_case/memory.hpp +++ b/test/test_case/memory.hpp @@ -19,6 +19,9 @@ struct stateful_allocator : public std::allocator stateful_allocator(stateful_allocator const&) noexcept = default; stateful_allocator(stateful_allocator&&) noexcept = default; + stateful_allocator& operator=(stateful_allocator const&) noexcept = default; + stateful_allocator& operator=(stateful_allocator&&) noexcept = default; + template stateful_allocator(stateful_allocator const& other) noexcept : state{other.state} { } diff --git a/test/tied_sequence.cpp b/test/tied_sequence.cpp index ea79227..a148618 100644 --- a/test/tied_sequence.cpp +++ b/test/tied_sequence.cpp @@ -1092,7 +1092,7 @@ TEST_CASE("allocator", "[allocator]") { SECTION("stateful allocator") { - [[maybe_unused]] flat_map::tied_sequence>, std::deque>> ts + flat_map::tied_sequence>, std::deque>> ts { stateful_allocator{"state1"}, stateful_allocator{"state2"} @@ -1105,18 +1105,200 @@ TEST_CASE("allocator", "[allocator]") SECTION("allocator forwarding") { - [[maybe_unused]] flat_map::tied_sequence>, std::deque>> ts + flat_map::tied_sequence>, std::deque>> ts { flat_map::forward_allocator( stateful_allocator{"state1"}, stateful_allocator{"state2"} ) }; + + auto alloc = ts.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); + } + + SECTION("allocator forwarding with count copies") + { + flat_map::tied_sequence>, std::deque>> ts + { + 4, {1, 2}, + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + }; + + REQUIRE(ts.size() == 4); + REQUIRE(ts[0] == std::tuple{1, 2}); + REQUIRE(ts[1] == std::tuple{1, 2}); + REQUIRE(ts[2] == std::tuple{1, 2}); + REQUIRE(ts[3] == std::tuple{1, 2}); + + auto alloc = ts.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); + } + + SECTION("allocator forwarding with count default-initialized") + { + flat_map::tied_sequence>, std::deque>> ts + { + 4, + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + }; + + REQUIRE(ts.size() == 4); + REQUIRE(ts[0] == std::tuple{}); + REQUIRE(ts[1] == std::tuple{}); + REQUIRE(ts[2] == std::tuple{}); + REQUIRE(ts[3] == std::tuple{}); + + auto alloc = ts.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); + } + + SECTION("from sequence") + { + std::vector v = + { + std::tuple{0, 1}, + std::tuple{2, 3}, + std::tuple{4, 5}, + std::tuple{6, 7}, + }; + + flat_map::tied_sequence>, std::deque>> ts + { + v.begin(), v.end(), + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + }; + + REQUIRE(ts.size() == 4); + REQUIRE(ts[0] == std::tuple{0, 1}); + REQUIRE(ts[1] == std::tuple{2, 3}); + REQUIRE(ts[2] == std::tuple{4, 5}); + REQUIRE(ts[3] == std::tuple{6, 7}); + + auto alloc = ts.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); + } + + SECTION("copy ctor") + { + flat_map::tied_sequence>, std::vector>> src + { + 4, {1, 2}, + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + }; + decltype(src) dst = + { + src, + flat_map::forward_allocator( + stateful_allocator{"state3"}, + stateful_allocator{"state4"} + ) + }; + + REQUIRE(src.begin() != dst.begin()); + REQUIRE(src.data() != dst.data()); + REQUIRE(src.size() == 4); + REQUIRE(src[0] == std::tuple{1, 2}); + REQUIRE(src[1] == std::tuple{1, 2}); + REQUIRE(src[2] == std::tuple{1, 2}); + REQUIRE(src[3] == std::tuple{1, 2}); + REQUIRE(dst.size() == 4); + REQUIRE(dst[0] == std::tuple{1, 2}); + REQUIRE(dst[1] == std::tuple{1, 2}); + REQUIRE(dst[2] == std::tuple{1, 2}); + REQUIRE(dst[3] == std::tuple{1, 2}); + + auto alloc = src.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); + + alloc = dst.get_allocator(); + REQUIRE(alloc.get<0>().state == "state3"); + REQUIRE(alloc.get<1>().state == "state4"); + } + + SECTION("move ctor") + { + flat_map::tied_sequence>, std::vector>> src + { + 4, {1, 2}, + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + }; + decltype(src) dst = + { + std::move(src), + flat_map::forward_allocator( + stateful_allocator{"state3"}, + stateful_allocator{"state4"} + ) + }; + + REQUIRE(src.begin() != dst.begin()); + REQUIRE(src.empty()); + REQUIRE(dst.size() == 4); + REQUIRE(dst[0] == std::tuple{1, 2}); + REQUIRE(dst[1] == std::tuple{1, 2}); + REQUIRE(dst[2] == std::tuple{1, 2}); + REQUIRE(dst[3] == std::tuple{1, 2}); + + auto alloc = src.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); + + alloc = dst.get_allocator(); + REQUIRE(alloc.get<0>().state == "state3"); + REQUIRE(alloc.get<1>().state == "state4"); + } + + SECTION("initializer_list") + { + flat_map::tied_sequence>, std::deque>> ts = + { + { + {0, 1}, + {2, 3}, + {4, 5}, + {6, 7}, + }, + flat_map::forward_allocator( + stateful_allocator{"state1"}, + stateful_allocator{"state2"} + ) + }; + + REQUIRE(ts.size() == 4); + REQUIRE(ts[0] == std::tuple{0, 1}); + REQUIRE(ts[1] == std::tuple{2, 3}); + REQUIRE(ts[2] == std::tuple{4, 5}); + REQUIRE(ts[3] == std::tuple{6, 7}); + + auto alloc = ts.get_allocator(); + REQUIRE(alloc.get<0>().state == "state1"); + REQUIRE(alloc.get<1>().state == "state2"); } SECTION("nested") { - [[maybe_unused]] flat_map::tied_sequence>, std::deque>>> nts + flat_map::tied_sequence>, std::deque>>> nts { flat_map::forward_allocator( flat_map::forward_allocator( @@ -1125,5 +1307,10 @@ TEST_CASE("allocator", "[allocator]") ) ) }; + + auto out = nts.get_allocator(); + auto in = out.get<0>(); + REQUIRE(in.get<0>().state == "state1"); + REQUIRE(in.get<1>().state == "state2"); } } From 456195a710ce2577bf1559cca18a97bb32d25d0d Mon Sep 17 00:00:00 2001 From: Kohei Takahashi Date: Thu, 6 Jun 2024 14:22:41 +0900 Subject: [PATCH 5/7] noexcept --- flat_map/__memory.hpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/flat_map/__memory.hpp b/flat_map/__memory.hpp index 82c49cf..6552be4 100644 --- a/flat_map/__memory.hpp +++ b/flat_map/__memory.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace flat_map { @@ -21,18 +22,19 @@ struct fake_allocator : private AllocatorTuple using value_type = void; - fake_allocator() noexcept = default; - fake_allocator(fake_allocator const&) = default; - fake_allocator(fake_allocator&&) = default; + fake_allocator() noexcept(std::is_nothrow_default_constructible_v) = default; - explicit fake_allocator(AllocatorTuple const& tuple) noexcept : AllocatorTuple{tuple} { } - explicit fake_allocator(AllocatorTuple&& tuple) noexcept : AllocatorTuple{std::move(tuple)} { } + fake_allocator(fake_allocator const&) noexcept(std::is_nothrow_copy_constructible_v) = default; + fake_allocator(fake_allocator&&) noexcept(std::is_nothrow_move_constructible_v) = default; + + explicit fake_allocator(AllocatorTuple const& tuple) noexcept(std::is_nothrow_copy_constructible_v) : AllocatorTuple{tuple} { } + explicit fake_allocator(AllocatorTuple&& tuple) noexcept(std::is_nothrow_move_constructible_v) : AllocatorTuple{std::move(tuple)} { } template explicit fake_allocator(std::allocator_arg_t, Allocators&&... allocs) noexcept : AllocatorTuple{std::forward(allocs)...} { } - fake_allocator& operator=(fake_allocator const&) noexcept = default; - fake_allocator& operator=(fake_allocator&&) noexcept = default; + fake_allocator& operator=(fake_allocator const&) noexcept(std::is_nothrow_copy_assignable_v) = default; + fake_allocator& operator=(fake_allocator&&) noexcept(std::is_nothrow_move_assignable_v) = default; template decltype(auto) get() noexcept { return std::get(static_cast(*this)); } template decltype(auto) get() const noexcept { return std::get(static_cast(*this)); } @@ -41,14 +43,14 @@ struct fake_allocator : private AllocatorTuple void* allocate(std::size_t) { throw std::bad_alloc(); } constexpr void deallocate(void*, std::size_t) { } - constexpr bool operator==(fake_allocator) const { return true; } - constexpr bool operator!=(fake_allocator) const { return false; } + constexpr bool operator==(fake_allocator const& other) const noexcept { return static_cast(*this) == static_cast(other); } + constexpr bool operator!=(fake_allocator const& other) const noexcept { return !operator==(other); } }; } // namespace flat_map::detail template -auto forward_allocator(Allocators&&... alloc) +auto forward_allocator(Allocators&&... alloc) noexcept { return detail::fake_allocator>{std::allocator_arg, std::forward(alloc)...}; } From 69effb7937fd50c4820f8c2c37f2ee3b93742630 Mon Sep 17 00:00:00 2001 From: Kohei Takahashi Date: Thu, 6 Jun 2024 14:23:31 +0900 Subject: [PATCH 6/7] Update tied_sequence docs --- docs/tied_sequence.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/tied_sequence.md b/docs/tied_sequence.md index aa53cdd..4f6e231 100644 --- a/docs/tied_sequence.md +++ b/docs/tied_sequence.md @@ -49,7 +49,6 @@ using const_reverse_iterator = std::reverse_iterator; ```cpp constexpr tied_sequence(); -constexpr explicit tied_sequence(allocator_type const&); ``` **Exceptions** @@ -58,6 +57,7 @@ No exception only if for all sequences meet all of - `std::is_nothrow_default_constructible_v == true` ```cpp +constexpr explicit tied_sequence(allocator_type const& alloc) noexcept; constexpr explicit tied_sequence(typename Sequences::allocator_type const&... alloc) noexcept; ``` @@ -65,10 +65,10 @@ Construct sequences form corresponding allocator. ```cpp constexpr tied_sequence(size_type count, value_type const& value); -constexpr tied_sequence(size_type count, value_type const& value, allocator_type const&); +constexpr tied_sequence(size_type count, value_type const& value, allocator_type const& alloc); constexpr tied_sequence(size_type count, value_type const& value, typename Sequences::allocator_type const&... alloc); constexpr tied_sequence(size_type count); -constexpr tied_sequence(size_type count, allocator_type const&); +constexpr tied_sequence(size_type count, allocator_type const& alloc); constexpr tied_sequence(size_type count, typename Sequences::allocator_type const&... alloc); ``` @@ -79,7 +79,7 @@ template constexpr tied_sequence(InputIterator first, InputIterator last); template -constexpr tied_sequence(InputIterator first, InputIterator last, allocator_type const&); +constexpr tied_sequence(InputIterator first, InputIterator last, allocator_type const& alloc); template constexpr tied_sequence(InputIterator first, InputIterator last, typename Sequences::allocator_type const&... alloc); @@ -94,7 +94,7 @@ Construct containers from `[first, last)`. ```cpp constexpr tied_sequence(tied_sequence const& other); -constexpr tied_sequence(tied_sequence const& other, allocator_type const&); +constexpr tied_sequence(tied_sequence const& other, allocator_type const& alloc); constexpr tied_sequence(tied_sequence const& other, typename Sequences::allocator_type const&... alloc); ``` @@ -102,7 +102,7 @@ Copy from other. ```cpp constexpr tied_sequence(tied_sequence&& other); -constexpr tied_sequence(tied_sequence&& other, allocator_type const&); +constexpr tied_sequence(tied_sequence&& other, allocator_type const& alloc); constexpr tied_sequence(tied_sequence&& other, typename Sequences::allocator_type const&... alloc); ``` @@ -111,7 +111,7 @@ Move entire elements from other. ```cpp constexpr tied_sequence(std::initializer_list init); -constexpr tied_sequence(std::initializer_list init, allocator_type const&); +constexpr tied_sequence(std::initializer_list init, allocator_type const& alloc); constexpr tied_sequence(std::initializer_list init, typename Sequences::allocator_type const&... alloc); ``` @@ -594,3 +594,12 @@ constexpr typename tied_sequence::size_type erase_if(tied_sequence ``` Erase every elements which `pred` returned true. + +### forward\_allocator + +```cpp +template +/* unspecified */ forward_allocator(Allocators&&... alloc); +``` + +Forward allocators as `tied_sequence::allocator_type`. From 97ecf28c455298fa7488f6dccd436f9075b3cef5 Mon Sep 17 00:00:00 2001 From: Kohei Takahashi Date: Sun, 9 Jun 2024 17:25:17 +0900 Subject: [PATCH 7/7] doc: Removed redundant back slash --- docs/tied_sequence.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tied_sequence.md b/docs/tied_sequence.md index 4f6e231..0ef59fe 100644 --- a/docs/tied_sequence.md +++ b/docs/tied_sequence.md @@ -595,7 +595,7 @@ constexpr typename tied_sequence::size_type erase_if(tied_sequence Erase every elements which `pred` returned true. -### forward\_allocator +### forward_allocator ```cpp template