From 15cfef696763907f5a930e29b80440bb45e15237 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 17 Feb 2024 18:58:48 +0100 Subject: [PATCH] Fixed support for allocators with `explicit` copy constructors (#234) * added tests for explicit allocators * made explicit_alloc_ctor_tests work by adhering to the principle that classes templated with Allocator should accept exactly Allocator objects * removed TMP machinery older compilers choke about * initialized variables * updated release notes * fixed PR number --- doc/unordered/changes.adoc | 1 + doc/unordered/copyright.adoc | 2 +- include/boost/unordered/detail/fca.hpp | 14 +- .../unordered/detail/foa/concurrent_table.hpp | 13 +- include/boost/unordered/detail/foa/core.hpp | 13 +- .../boost/unordered/detail/implementation.hpp | 10 +- include/boost/unordered/unordered_map.hpp | 17 +- include/boost/unordered/unordered_set.hpp | 16 +- test/Jamfile.v2 | 3 + test/cfoa/explicit_alloc_ctor_tests.cpp | 6 + test/unordered/explicit_alloc_ctor_tests.cpp | 152 ++++++++++++++++++ 11 files changed, 215 insertions(+), 32 deletions(-) create mode 100644 test/cfoa/explicit_alloc_ctor_tests.cpp create mode 100644 test/unordered/explicit_alloc_ctor_tests.cpp diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index 9a7d49e8..57c4620e 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -10,6 +10,7 @@ * Optimized `emplace()` for a `value_type` or `init_type` (if applicable) argument to bypass creating an intermediate object. The argument is already the same type as the would-be intermediate object. * Optimized `emplace()` for `k,v` arguments on map containers to delay constructing the object until it is certain that an element should be inserted. This optimization happens when the map's `key_type` is move constructible or when the `k` argument is a `key_type`. +* Fixed support for allocators with `explicit` copy constructors ({github-pr-url}/234[PR#234^]). == Release 1.84.0 - Major update diff --git a/doc/unordered/copyright.adoc b/doc/unordered/copyright.adoc index 3a14ba72..dd03fe7c 100644 --- a/doc/unordered/copyright.adoc +++ b/doc/unordered/copyright.adoc @@ -11,7 +11,7 @@ Copyright (C) 2005-2008 Daniel James Copyright (C) 2022-2023 Christian Mazakas -Copyright (C) 2022-2023 Joaquín M López Muñoz +Copyright (C) 2022-2024 Joaquín M López Muñoz Copyright (C) 2022-2023 Peter Dimov diff --git a/include/boost/unordered/detail/fca.hpp b/include/boost/unordered/detail/fca.hpp index 6461bc0f..66c8c250 100644 --- a/include/boost/unordered/detail/fca.hpp +++ b/include/boost/unordered/detail/fca.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2023 Joaquin M Lopez Munoz. +// Copyright (C) 2022-2024 Joaquin M Lopez Munoz. // Copyright (C) 2022 Christian Mazakas // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -518,7 +518,8 @@ namespace boost { } grouped_bucket_array(size_type n, const Allocator& al) - : empty_value(empty_init_t(), al), + : empty_value( + empty_init_t(), node_allocator_type(al)), size_index_(0), size_(0), buckets(), groups() { if (n == 0) { @@ -678,12 +679,17 @@ namespace boost { bucket_allocator_type get_bucket_allocator() const { - return this->get_node_allocator(); + return bucket_allocator_type(this->get_node_allocator()); } group_allocator_type get_group_allocator() const { - return this->get_node_allocator(); + return group_allocator_type(this->get_node_allocator()); + } + + Allocator get_allocator() const + { + return Allocator(this->get_node_allocator()); } size_type buckets_len() const noexcept { return size_ + 1; } diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 305ada28..218a78a4 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -1,6 +1,6 @@ /* Fast open-addressing concurrent hash table. * - * Copyright 2023 Joaquin M Lopez Munoz. + * Copyright 2023-2024 Joaquin M Lopez Munoz. * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -259,6 +259,7 @@ struct concurrent_table_arrays:table_arrays typename boost::allocator_pointer::type; using super=table_arrays; + using allocator_type=typename super::allocator_type; concurrent_table_arrays(const super& arrays,group_access_pointer pga): super{arrays},group_accesses_{pga}{} @@ -267,12 +268,11 @@ struct concurrent_table_arrays:table_arrays return boost::to_address(group_accesses_); } - static concurrent_table_arrays new_( - group_access_allocator_type al,std::size_t n) + static concurrent_table_arrays new_(allocator_type al,std::size_t n) { super x{super::new_(al,n)}; BOOST_TRY{ - return new_group_access(al,x); + return new_group_access(group_access_allocator_type(al),x); } BOOST_CATCH(...){ super::delete_(al,x); @@ -322,10 +322,9 @@ struct concurrent_table_arrays:table_arrays return arrays; } - static void delete_( - group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept + static void delete_(allocator_type al,concurrent_table_arrays& arrays)noexcept { - delete_group_access(al,arrays); + delete_group_access(group_access_allocator_type(al),arrays); super::delete_(al,arrays); } diff --git a/include/boost/unordered/detail/foa/core.hpp b/include/boost/unordered/detail/foa/core.hpp index 28766e2d..9955ca1d 100644 --- a/include/boost/unordered/detail/foa/core.hpp +++ b/include/boost/unordered/detail/foa/core.hpp @@ -1,6 +1,6 @@ /* Common base for Boost.Unordered open-addressing tables. * - * Copyright 2022-2023 Joaquin M Lopez Munoz. + * Copyright 2022-2024 Joaquin M Lopez Munoz. * Copyright 2023 Christian Mazakas. * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. @@ -977,7 +977,12 @@ struct arrays_holder arrays_holder(arrays_holder const&); arrays_holder& operator=(arrays_holder const&)=delete; - ~arrays_holder(){if(!released_)arrays_.delete_(al_,arrays_);} + ~arrays_holder() + { + if(!released_){ + arrays_.delete_(typename Arrays::allocator_type(al_),arrays_); + } + } const Arrays& release() { @@ -1974,7 +1979,7 @@ private: arrays_type new_arrays(std::size_t n)const { - return arrays_type::new_(al(),n); + return arrays_type::new_(typename arrays_type::allocator_type(al()),n); } arrays_type new_arrays_for_growth()const @@ -1995,7 +2000,7 @@ private: void delete_arrays(arrays_type& arrays_)noexcept { - arrays_type::delete_(al(),arrays_); + arrays_type::delete_(typename arrays_type::allocator_type(al()),arrays_); } arrays_holder_type make_arrays(std::size_t n)const diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index a239d448..01e41746 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1,6 +1,6 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2016 Daniel James -// Copyright (C) 2022-2023 Joaquin M Lopez Munoz. +// Copyright (C) 2022-2024 Joaquin M Lopez Munoz. // Copyright (C) 2022-2023 Christian Mazakas // Copyright (C) 2024 Braden Ganetsky // @@ -1366,14 +1366,14 @@ namespace boost { } table(std::size_t num_buckets, hasher const& hf, key_equal const& eq, - node_allocator_type const& a) + value_allocator const& a) : functions(hf, eq), size_(0), mlf_(1.0f), max_load_(0), buckets_(num_buckets, a) { recalculate_max_load(); } - table(table const& x, node_allocator_type const& a) + table(table const& x, value_allocator const& a) : functions(x), size_(0), mlf_(x.mlf_), max_load_(0), buckets_(x.size_, a) { @@ -1388,7 +1388,7 @@ namespace boost { x.max_load_ = 0; } - table(table& x, node_allocator_type const& a, + table(table& x, value_allocator const& a, boost::unordered::detail::move_tag m) : functions(x, m), size_(0), mlf_(x.mlf_), max_load_(0), buckets_(x.bucket_count(), a) @@ -2722,7 +2722,7 @@ namespace boost { inline void table::rehash_impl(std::size_t num_buckets) { bucket_array_type new_buckets( - num_buckets, buckets_.get_node_allocator()); + num_buckets, buckets_.get_allocator()); BOOST_TRY { diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 34f8ae53..ef3eb206 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -1,6 +1,7 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2011 Daniel James. // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz. // 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) @@ -158,7 +159,7 @@ namespace boost { allocator_type get_allocator() const noexcept { - return table_.node_alloc(); + return allocator_type(table_.node_alloc()); } // // iterators @@ -254,12 +255,15 @@ namespace boost { node_type extract(const_iterator position) { return node_type( - table_.extract_by_iterator_unique(position), table_.node_alloc()); + table_.extract_by_iterator_unique(position), + allocator_type(table_.node_alloc())); } node_type extract(const key_type& k) { - return node_type(table_.extract_by_key_impl(k), table_.node_alloc()); + return node_type( + table_.extract_by_key_impl(k), + allocator_type(table_.node_alloc())); } template @@ -268,8 +272,9 @@ namespace boost { node_type>::type extract(Key&& k) { - return node_type(table_.extract_by_key_impl(std::forward(k)), - table_.node_alloc()); + return node_type( + table_.extract_by_key_impl(std::forward(k)), + allocator_type(table_.node_alloc())); } insert_return_type insert(node_type&& np) @@ -806,7 +811,7 @@ namespace boost { allocator_type get_allocator() const noexcept { - return table_.node_alloc(); + return allocator_type(table_.node_alloc()); } // iterators diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 680b6565..d891a418 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -1,6 +1,7 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2011 Daniel James. // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz. // 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) @@ -154,7 +155,7 @@ namespace boost { allocator_type get_allocator() const noexcept { - return table_.node_alloc(); + return allocator_type(table_.node_alloc()); } // iterators @@ -252,12 +253,15 @@ namespace boost { node_type extract(const_iterator position) { return node_type( - table_.extract_by_iterator_unique(position), table_.node_alloc()); + table_.extract_by_iterator_unique(position), + allocator_type(table_.node_alloc())); } node_type extract(const key_type& k) { - return node_type(table_.extract_by_key_impl(k), table_.node_alloc()); + return node_type( + table_.extract_by_key_impl(k), + allocator_type(table_.node_alloc())); } template @@ -266,7 +270,9 @@ namespace boost { node_type>::type extract(const Key& k) { - return node_type(table_.extract_by_key_impl(k), table_.node_alloc()); + return node_type( + table_.extract_by_key_impl(k), + allocator_type(table_.node_alloc())); } insert_return_type insert(node_type&& np) @@ -647,7 +653,7 @@ namespace boost { allocator_type get_allocator() const noexcept { - return table_.node_alloc(); + return allocator_type(table_.node_alloc()); } // iterators diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f1ab8d81..fc452148 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -92,6 +92,7 @@ local FCA_TESTS = erase_equiv_tests erase_if erase_tests + explicit_alloc_ctor_tests extract_tests find_tests fwd_map_test @@ -205,6 +206,7 @@ local FOA_TESTS = emplace_smf_tests emplace_tests erase_tests + explicit_alloc_ctor_tests merge_tests find_tests at_tests @@ -327,6 +329,7 @@ local CFOA_TESTS = rw_spinlock_test7 rw_spinlock_test8 reentrancy_check_test + explicit_alloc_ctor_tests ; for local test in $(CFOA_TESTS) diff --git a/test/cfoa/explicit_alloc_ctor_tests.cpp b/test/cfoa/explicit_alloc_ctor_tests.cpp new file mode 100644 index 00000000..7f87a060 --- /dev/null +++ b/test/cfoa/explicit_alloc_ctor_tests.cpp @@ -0,0 +1,6 @@ +// Copyright 2024 Joaquin M Lopez Muoz. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_UNORDERED_CFOA_TESTS +#include "../unordered/explicit_alloc_ctor_tests.cpp" diff --git a/test/unordered/explicit_alloc_ctor_tests.cpp b/test/unordered/explicit_alloc_ctor_tests.cpp new file mode 100644 index 00000000..9b6431d9 --- /dev/null +++ b/test/unordered/explicit_alloc_ctor_tests.cpp @@ -0,0 +1,152 @@ +// Copyright 2024 Joaquin M Lopez Muoz. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt) + +#ifdef BOOST_UNORDERED_CFOA_TESTS +#include +#include +#else +#include "../helpers/unordered.hpp" +#endif + +#include "../helpers/test.hpp" +#include +#include +#include + +template struct explicit_allocator +{ + typedef T value_type; + + explicit_allocator() {} + + template + explicit explicit_allocator(const explicit_allocator&) {} + + template + bool operator==(explicit_allocator const &) const noexcept + { + return true; + } + + template + bool operator!=(explicit_allocator const &) const noexcept + { + return false; + } + + T* allocate(std::size_t n) + { + return std::allocator().allocate(n); + } + + void deallocate(T* p, std::size_t n) + { + std::allocator().deallocate(p, n); + } +}; + +template struct has_extract: +std::false_type {}; + +template +struct has_extract< + Container, boost::void_t +>: std::true_type {}; + +template void test_explicit_alloc_ctor_extract( + Container&, std::false_type) +{ +} + +template void test_explicit_alloc_ctor_extract( + Container& c, std::true_type) +{ + auto n = c.extract(c.begin()); + c.insert(std::move(n)); + n = c.extract(typename Container::key_type()); + c.insert(std::move(n)); +} + +template void test_explicit_alloc_ctor_extract(Container& c) +{ + test_explicit_alloc_ctor_extract(c, has_extract()); +} + +template void test_explicit_alloc_ctor() +{ + using key_type = typename Container::key_type; + using value_type = typename Container::value_type; + using hasher = typename Container::hasher; + using allocator_type = typename Container::allocator_type; + + allocator_type al; + std::initializer_list il{}; + hasher h; + std::vector v; + Container c, + c2(0), + c3(v.begin(), v.end()), + c4(c3), + c5(std::move(c4)), + c6(v.begin(), v.end(), al), + c7(al), + c8(c7, al), + c9(std::move(c8), al), + c10(il), + c11(0, al), + c12(0, h, al), + c13(v.begin(), v.end(), 0, al), + c14(v.begin(), v.end(), 0, h, al), + c15(il, al), + c16(il, 0, al), + c17(il, 0, h, al); + value_type x{}; + key_type k{}; + + c = c2; + c = std::move(c2); + c = il; + c.swap(c3); + c.insert(x); +#ifdef BOOST_UNORDERED_CFOA_TESTS + (void) c.visit(k, [](const value_type&) {}); +#else + (void) c.find(k); +#endif + test_explicit_alloc_ctor_extract(c); +} + +UNORDERED_AUTO_TEST (explicit_alloc_ctor) { +#if defined(BOOST_UNORDERED_CFOA_TESTS) + test_explicit_alloc_ctor, std::equal_to, + explicit_allocator > > >(); + test_explicit_alloc_ctor, std::equal_to, explicit_allocator > >(); +#elif defined(BOOST_UNORDERED_FOA_TESTS) + test_explicit_alloc_ctor, std::equal_to, + explicit_allocator > > >(); + test_explicit_alloc_ctor, std::equal_to, explicit_allocator > >(); + test_explicit_alloc_ctor, std::equal_to, + explicit_allocator > > >(); + test_explicit_alloc_ctor, std::equal_to, explicit_allocator > >(); +#else + test_explicit_alloc_ctor, std::equal_to, + explicit_allocator > > >(); + test_explicit_alloc_ctor, std::equal_to, + explicit_allocator > > >(); + test_explicit_alloc_ctor, std::equal_to, explicit_allocator > >(); + test_explicit_alloc_ctor, std::equal_to, explicit_allocator > >(); +#endif +} + +RUN_TESTS()