diff --git a/.appveyor.yml b/.appveyor.yml index 7ffc2fc6..524fc10e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -31,47 +31,83 @@ environment: B2_VARIANT: debug,release matrix: - - FLAVOR: Visual Studio 2015 + - FLAVOR: VS2015 (32 bit) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 B2_TOOLSET: msvc-14.0 + B2_ADDRESS_MODEL: 32 - - FLAVOR: Visual Studio 2017, C++14 + - FLAVOR: VS2015 (64 bit) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + B2_TOOLSET: msvc-14.0 + B2_ADDRESS_MODEL: 64 + + - FLAVOR: VS2017 (32 bit, C++14, Debug) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 B2_CXXSTD: 14 B2_TOOLSET: msvc-14.1 + B2_ADDRESS_MODEL: 32 + B2_VARIANT: debug - - FLAVOR: Visual Studio 2017, C++17 + - FLAVOR: VS2017 (32 bit, C++17, Release) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 B2_CXXSTD: 17 B2_TOOLSET: msvc-14.1 + B2_ADDRESS_MODEL: 32 + B2_VARIANT: release - - FLAVOR: Visual Studio 2017, C++latest + - FLAVOR: VS2017 (32 bit, C++latest, Debug) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 B2_CXXSTD: latest B2_TOOLSET: msvc-14.1 + B2_ADDRESS_MODEL: 32 + B2_VARIANT: debug - - FLAVOR: cygwin (32-bit, C++11) + - FLAVOR: VS2017 (64 bit, C++14, Release) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_CXXSTD: 14 + B2_TOOLSET: msvc-14.1 + B2_ADDRESS_MODEL: 64 + B2_VARIANT: release + + - FLAVOR: VS2017 (64 bit, C++17, Debug) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_CXXSTD: 17 + B2_TOOLSET: msvc-14.1 + B2_ADDRESS_MODEL: 64 + B2_VARIANT: debug + + - FLAVOR: VS2017 (64 bit, C++latest, Release) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_CXXSTD: latest + B2_TOOLSET: msvc-14.1 + B2_ADDRESS_MODEL: 64 + B2_VARIANT: release + + - FLAVOR: Cygwin (32 bit, C++11, Release) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 ADDPATH: C:\cygwin\bin; B2_ADDRESS_MODEL: 32 B2_CXXSTD: 11 B2_TOOLSET: gcc + B2_VARIANT: release - - FLAVOR: cygwin (32-bit, C++14) + - FLAVOR: Cygwin (32 bit, C++14, Debug) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 ADDPATH: C:\cygwin\bin; B2_ADDRESS_MODEL: 32 B2_CXXSTD: 14 B2_TOOLSET: gcc + B2_VARIANT: debug - - FLAVOR: cygwin (32-bit, C++1z) + - FLAVOR: Cygwin (32 bit, C++1z, Release) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 ADDPATH: C:\cygwin\bin; B2_ADDRESS_MODEL: 32 B2_CXXSTD: 1z B2_TOOLSET: gcc + B2_VARIANT: release - - FLAVOR: cygwin (64-bit, latest, C++11) + - FLAVOR: Cygwin (64 bit, C++11, Release) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 ADDPATH: C:\cygwin64\bin; B2_ADDRESS_MODEL: 64 @@ -80,74 +116,83 @@ environment: B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" B2_VARIANT: release - - FLAVOR: cygwin (64-bit, latest, C++14) + - FLAVOR: Cygwin (64 bit, C++14, Debug) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 ADDPATH: C:\cygwin64\bin; B2_ADDRESS_MODEL: 64 B2_CXXSTD: 14 B2_TOOLSET: gcc B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" - B2_VARIANT: release + B2_VARIANT: debug - - FLAVOR: cygwin (64-bit, latest, C++1z) + - FLAVOR: Cygwin (64 bit, C++17, Release) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 ADDPATH: C:\cygwin64\bin; B2_ADDRESS_MODEL: 64 - B2_CXXSTD: 1z + B2_CXXSTD: 17 B2_TOOLSET: gcc B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" B2_VARIANT: release - - FLAVOR: mingw-w64, 32 bit, C++11 + - FLAVOR: Cygwin (64 bit, C++2a, Debug) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + ADDPATH: C:\cygwin64\bin; + B2_ADDRESS_MODEL: 64 + B2_CXXSTD: 2a + B2_TOOLSET: gcc + B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" + B2_VARIANT: debug + + - FLAVOR: MinGW-w64 (32 bit, C++11) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin; B2_CXXSTD: 11 B2_TOOLSET: gcc B2_ADDRESS_MODEL: 32 - - FLAVOR: mingw-w64, 32 bit, C++14 + - FLAVOR: MinGW-w64 (32 bit, C++14) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin; B2_CXXSTD: 14 B2_TOOLSET: gcc B2_ADDRESS_MODEL: 32 - - FLAVOR: mingw-w64, 32 bit, C++17 + - FLAVOR: MinGW-w64 (32 bit, C++17) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin; B2_CXXSTD: 17 B2_TOOLSET: gcc B2_ADDRESS_MODEL: 32 - - FLAVOR: mingw-w64, 32 bit, C++2a + - FLAVOR: MinGW-w64 (32 bit, C++2a) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin; B2_CXXSTD: 2a B2_TOOLSET: gcc B2_ADDRESS_MODEL: 32 - - FLAVOR: mingw-w64, 64 bit, C++11 + - FLAVOR: MinGW-w64 (64 bit, C++11) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin; B2_CXXSTD: 11 B2_TOOLSET: gcc B2_ADDRESS_MODEL: 64 - - FLAVOR: mingw-w64, 64 bit, C++14 + - FLAVOR: MinGW-w64 (64 bit, C++14) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin; B2_CXXSTD: 14 B2_TOOLSET: gcc B2_ADDRESS_MODEL: 64 - - FLAVOR: mingw-w64, 64 bit, C++17 + - FLAVOR: MinGW-w64 (64 bit, C++17) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin; B2_CXXSTD: 17 B2_TOOLSET: gcc B2_ADDRESS_MODEL: 64 - - FLAVOR: mingw-w64, 64 bit, C++2a + - FLAVOR: MinGW-w64 (64 bit, C++2a) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin; B2_CXXSTD: 2a diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index e727de80..8ab7b639 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -6,6 +6,10 @@ :github-pr-url: https://github.com/boostorg/unordered/pull :cpp: C++ +== Release 1.86.0 + +* Added container `pmr` aliases when header `` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type. + == Release 1.85.0 * 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. diff --git a/doc/unordered/concurrent_flat_map.adoc b/doc/unordered/concurrent_flat_map.adoc index b921d8df..6f87390c 100644 --- a/doc/unordered/concurrent_flat_map.adoc +++ b/doc/unordered/concurrent_flat_map.adoc @@ -299,6 +299,17 @@ namespace boost { template typename concurrent_flat_map::size_type xref:#concurrent_flat_map_erase_if_2[erase_if](concurrent_flat_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using concurrent_flat_map = + boost::concurrent_flat_map>>; + } } ----- diff --git a/doc/unordered/concurrent_flat_set.adoc b/doc/unordered/concurrent_flat_set.adoc index b9436708..e7713352 100644 --- a/doc/unordered/concurrent_flat_set.adoc +++ b/doc/unordered/concurrent_flat_set.adoc @@ -254,6 +254,16 @@ namespace boost { template typename concurrent_flat_set::size_type xref:#concurrent_flat_set_erase_if_2[erase_if](concurrent_flat_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using concurrent_flat_set = + boost::concurrent_flat_set>; + } } ----- diff --git a/doc/unordered/unordered_flat_map.adoc b/doc/unordered/unordered_flat_map.adoc index fd9e3888..07f90d0e 100644 --- a/doc/unordered/unordered_flat_map.adoc +++ b/doc/unordered/unordered_flat_map.adoc @@ -285,6 +285,17 @@ namespace boost { template typename unordered_flat_map::size_type xref:#unordered_flat_map_erase_if[erase_if](unordered_flat_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_flat_map = + boost::unordered_flat_map>>; + } } ----- diff --git a/doc/unordered/unordered_flat_set.adoc b/doc/unordered/unordered_flat_set.adoc index c52b9159..48ca0837 100644 --- a/doc/unordered/unordered_flat_set.adoc +++ b/doc/unordered/unordered_flat_set.adoc @@ -238,6 +238,16 @@ namespace boost { template typename unordered_flat_set::size_type xref:#unordered_flat_set_erase_if[erase_if](unordered_flat_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_flat_set = + boost::unordered_flat_set>; + } } ----- diff --git a/doc/unordered/unordered_map.adoc b/doc/unordered/unordered_map.adoc index 015cb90b..b165eb4a 100644 --- a/doc/unordered/unordered_map.adoc +++ b/doc/unordered/unordered_map.adoc @@ -290,6 +290,17 @@ namespace boost { template typename unordered_map::size_type xref:#unordered_map_erase_if[erase_if](unordered_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_map = + boost::unordered_map>>; + } } ----- diff --git a/doc/unordered/unordered_multimap.adoc b/doc/unordered/unordered_multimap.adoc index 6c50b857..2c6d50b5 100644 --- a/doc/unordered/unordered_multimap.adoc +++ b/doc/unordered/unordered_multimap.adoc @@ -257,6 +257,17 @@ namespace boost { template typename unordered_multimap::size_type xref:#unordered_multimap_erase_if[erase_if](unordered_multimap& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_multimap = + boost::unordered_multimap>>; + } } ----- diff --git a/doc/unordered/unordered_multiset.adoc b/doc/unordered/unordered_multiset.adoc index 7fd265bc..510d4fdf 100644 --- a/doc/unordered/unordered_multiset.adoc +++ b/doc/unordered/unordered_multiset.adoc @@ -248,6 +248,16 @@ namespace boost { template typename unordered_multiset::size_type xref:#unordered_multiset_erase_if[erase_if](unordered_multiset& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_multiset = + boost::unordered_multiset>; + } } ----- diff --git a/doc/unordered/unordered_node_map.adoc b/doc/unordered/unordered_node_map.adoc index 4179d17a..8056bb64 100644 --- a/doc/unordered/unordered_node_map.adoc +++ b/doc/unordered/unordered_node_map.adoc @@ -6,7 +6,7 @@ `boost::unordered_node_map` — A node-based, open-addressing unordered associative container that associates unique keys with another value. `boost::unordered_node_map` uses an open-addressing layout like `boost::unordered_flat_map`, but, -being node-based, it provides pointer/iterator stability and node handling functionalities. +being node-based, it provides pointer stability and node handling functionalities. Its performance lies between those of `boost::unordered_map` and `boost::unordered_flat_map`. As a result of its using open addressing, the interface of `boost::unordered_node_map` deviates in @@ -288,6 +288,17 @@ namespace boost { template typename unordered_node_map::size_type xref:#unordered_node_map_erase_if[erase_if](unordered_node_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_node_map = + boost::unordered_node_map>>; + } } ----- diff --git a/doc/unordered/unordered_node_set.adoc b/doc/unordered/unordered_node_set.adoc index cf5e8282..f5b5fdf2 100644 --- a/doc/unordered/unordered_node_set.adoc +++ b/doc/unordered/unordered_node_set.adoc @@ -6,7 +6,7 @@ `boost::unordered_node_set` — A node-based, open-addressing unordered associative container that stores unique values. `boost::unordered_node_set` uses an open-addressing layout like `boost::unordered_flat_set`, but, -being node-based, it provides pointer/iterator stability and node handling functionalities. +being node-based, it provides pointer stability and node handling functionalities. Its performance lies between those of `boost::unordered_set` and `boost::unordered_flat_set`. As a result of its using open addressing, the interface of `boost::unordered_node_set` deviates in @@ -242,6 +242,16 @@ namespace boost { template typename unordered_node_set::size_type xref:#unordered_node_set_erase_if[erase_if](unordered_node_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_node_set = + boost::unordered_node_set>; + } } ----- diff --git a/doc/unordered/unordered_set.adoc b/doc/unordered/unordered_set.adoc index 14b51eb9..2a3f5217 100644 --- a/doc/unordered/unordered_set.adoc +++ b/doc/unordered/unordered_set.adoc @@ -249,6 +249,16 @@ namespace boost { template typename unordered_set::size_type xref:#unordered_set_erase_if[erase_if](unordered_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace unordered::pmr { + template, + class Pred = std::equal_to> + using unordered_set = + boost::unordered_set>; + } } ----- diff --git a/include/boost/unordered/concurrent_flat_map_fwd.hpp b/include/boost/unordered/concurrent_flat_map_fwd.hpp index 99fa130c..e66f3674 100644 --- a/include/boost/unordered/concurrent_flat_map_fwd.hpp +++ b/include/boost/unordered/concurrent_flat_map_fwd.hpp @@ -1,6 +1,7 @@ /* Fast open-addressing concurrent hashmap. * * Copyright 2023 Christian Mazakas. + * Copyright 2024 Braden Ganetsky. * 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) @@ -11,11 +12,16 @@ #ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP #define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP +#include #include #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { @@ -43,6 +49,15 @@ namespace boost { typename concurrent_flat_map::size_type erase_if( concurrent_flat_map& c, Predicate pred); +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_flat_map = boost::unordered::concurrent_flat_map > >; + } // namespace pmr +#endif + } // namespace unordered using boost::unordered::concurrent_flat_map; diff --git a/include/boost/unordered/concurrent_flat_set_fwd.hpp b/include/boost/unordered/concurrent_flat_set_fwd.hpp index 03bcc5b4..1133d9c9 100644 --- a/include/boost/unordered/concurrent_flat_set_fwd.hpp +++ b/include/boost/unordered/concurrent_flat_set_fwd.hpp @@ -2,6 +2,7 @@ * * Copyright 2023 Christian Mazakas. * Copyright 2023 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 * http://www.boost.org/LICENSE_1_0.txt) @@ -12,11 +13,16 @@ #ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP #define BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP +#include #include #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { @@ -44,6 +50,15 @@ namespace boost { typename concurrent_flat_set::size_type erase_if( concurrent_flat_set& c, Predicate pred); +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_flat_set = boost::unordered::concurrent_flat_set >; + } // namespace pmr +#endif + } // namespace unordered using boost::unordered::concurrent_flat_set; diff --git a/include/boost/unordered/detail/fca.hpp b/include/boost/unordered/detail/fca.hpp index 66c8c250..1f733db2 100644 --- a/include/boost/unordered/detail/fca.hpp +++ b/include/boost/unordered/detail/fca.hpp @@ -659,12 +659,7 @@ namespace boost { std::swap(buckets, other.buckets); std::swap(groups, other.groups); - bool b = boost::allocator_propagate_on_container_swap< - allocator_type>::type::value; - if (b) { - boost::core::invoke_swap( - get_node_allocator(), other.get_node_allocator()); - } + swap_allocator_if_pocs(other); } node_allocator_type const& get_node_allocator() const @@ -876,6 +871,27 @@ namespace boost { pbg->prev->next = pbg->next; pbg->prev = pbg->next = group_pointer(); } + + void swap_allocator_if_pocs(grouped_bucket_array& other) + { + using allocator_pocs = + typename boost::allocator_propagate_on_container_swap< + allocator_type>::type; + swap_allocator_if_pocs( + other, std::integral_constant()); + } + + void swap_allocator_if_pocs( + grouped_bucket_array& other, std::true_type /* propagate */) + { + boost::core::invoke_swap( + get_node_allocator(), other.get_node_allocator()); + } + + void swap_allocator_if_pocs( + grouped_bucket_array&, std::false_type /* don't propagate */) + { + } }; } // namespace detail } // namespace unordered diff --git a/include/boost/unordered/unordered_flat_map_fwd.hpp b/include/boost/unordered/unordered_flat_map_fwd.hpp index 5551d1a6..f758f83a 100644 --- a/include/boost/unordered/unordered_flat_map_fwd.hpp +++ b/include/boost/unordered/unordered_flat_map_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // 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) @@ -15,6 +16,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -36,6 +41,16 @@ namespace boost { void swap(unordered_flat_map& lhs, unordered_flat_map& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_flat_map = + boost::unordered::unordered_flat_map > >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_flat_map; diff --git a/include/boost/unordered/unordered_flat_set_fwd.hpp b/include/boost/unordered/unordered_flat_set_fwd.hpp index 02435bab..9214939f 100644 --- a/include/boost/unordered/unordered_flat_set_fwd.hpp +++ b/include/boost/unordered/unordered_flat_set_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // 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) @@ -15,6 +16,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -36,6 +41,15 @@ namespace boost { void swap(unordered_flat_set& lhs, unordered_flat_set& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_flat_set = boost::unordered::unordered_flat_set >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_flat_set; diff --git a/include/boost/unordered/unordered_map_fwd.hpp b/include/boost/unordered/unordered_map_fwd.hpp index b374965e..375889a1 100644 --- a/include/boost/unordered/unordered_map_fwd.hpp +++ b/include/boost/unordered/unordered_map_fwd.hpp @@ -1,6 +1,7 @@ // Copyright (C) 2008-2011 Daniel James. // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // 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) @@ -16,6 +17,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -58,6 +63,20 @@ namespace boost { template class node_handle_map; template struct insert_return_type_map; + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class P = std::equal_to > + using unordered_map = boost::unordered::unordered_map > >; + + template , + class P = std::equal_to > + using unordered_multimap = boost::unordered::unordered_multimap > >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_map; diff --git a/include/boost/unordered/unordered_node_map_fwd.hpp b/include/boost/unordered/unordered_node_map_fwd.hpp index ec598168..95ac162d 100644 --- a/include/boost/unordered/unordered_node_map_fwd.hpp +++ b/include/boost/unordered/unordered_node_map_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // 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) @@ -15,6 +16,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -36,6 +41,16 @@ namespace boost { void swap(unordered_node_map& lhs, unordered_node_map& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_node_map = + boost::unordered::unordered_node_map > >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_node_map; diff --git a/include/boost/unordered/unordered_node_set_fwd.hpp b/include/boost/unordered/unordered_node_set_fwd.hpp index 977d032f..75215f2a 100644 --- a/include/boost/unordered/unordered_node_set_fwd.hpp +++ b/include/boost/unordered/unordered_node_set_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // 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) @@ -15,6 +16,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -36,6 +41,15 @@ namespace boost { void swap(unordered_node_set& lhs, unordered_node_set& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_node_set = boost::unordered::unordered_node_set >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_node_set; diff --git a/include/boost/unordered/unordered_set_fwd.hpp b/include/boost/unordered/unordered_set_fwd.hpp index b74c6136..aea95d88 100644 --- a/include/boost/unordered/unordered_set_fwd.hpp +++ b/include/boost/unordered/unordered_set_fwd.hpp @@ -1,6 +1,7 @@ // Copyright (C) 2008-2011 Daniel James. // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // 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) @@ -16,6 +17,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , class P = std::equal_to, @@ -56,6 +61,18 @@ namespace boost { template class node_handle_set; template struct insert_return_type_set; + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , class P = std::equal_to > + using unordered_set = boost::unordered::unordered_set >; + + template , class P = std::equal_to > + using unordered_multiset = boost::unordered::unordered_multiset >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_multiset; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6f2cc812..3575a562 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -122,6 +122,7 @@ local FCA_TESTS = transparent_tests unnecessary_copy_tests fancy_pointer_noleak + pmr_allocator_tests ; for local test in $(FCA_TESTS) @@ -229,6 +230,7 @@ local FOA_TESTS = uses_allocator hash_is_avalanching_test fancy_pointer_noleak + pmr_allocator_tests ; for local test in $(FOA_TESTS) @@ -338,6 +340,7 @@ local CFOA_TESTS = rw_spinlock_test8 reentrancy_check_test explicit_alloc_ctor_tests + pmr_allocator_tests ; for local test in $(CFOA_TESTS) diff --git a/test/cfoa/helpers.hpp b/test/cfoa/helpers.hpp index 4514b0a7..ef58f3fe 100644 --- a/test/cfoa/helpers.hpp +++ b/test/cfoa/helpers.hpp @@ -8,6 +8,8 @@ #define BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP #include "../helpers/generators.hpp" +#include "../helpers/helpers.hpp" +#include "../helpers/pmr.hpp" #include "../helpers/test.hpp" #include "common_helpers.hpp" diff --git a/test/cfoa/pmr_allocator_tests.cpp b/test/cfoa/pmr_allocator_tests.cpp new file mode 100644 index 00000000..0c0df338 --- /dev/null +++ b/test/cfoa/pmr_allocator_tests.cpp @@ -0,0 +1,8 @@ +// Copyright (C) 2024 Braden Ganetsky +// 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) + +#define BOOST_UNORDERED_CFOA_TESTS +#include +#include +#include "../unordered/pmr_allocator_tests.cpp" diff --git a/test/helpers/helpers.hpp b/test/helpers/helpers.hpp index 146dea4d..d421efab 100644 --- a/test/helpers/helpers.hpp +++ b/test/helpers/helpers.hpp @@ -51,6 +51,11 @@ namespace test { static_cast::difference_type>(x)); return it; } + + template + using is_map = + std::integral_constant::value>; } #endif diff --git a/test/helpers/pmr.hpp b/test/helpers/pmr.hpp new file mode 100644 index 00000000..cfa10dce --- /dev/null +++ b/test/helpers/pmr.hpp @@ -0,0 +1,55 @@ + +// Copyright 2024 Braden Ganetsky. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_TEST_PMR_HEADER +#define BOOST_UNORDERED_TEST_PMR_HEADER + +#include + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + +#include + +namespace test { + class counted_new_delete_resource : public std::pmr::memory_resource + { + public: + using std::pmr::memory_resource::memory_resource; + ~counted_new_delete_resource() override {} + + std::size_t count() const { return _count; } + + private: + void* do_allocate(std::size_t bytes, std::size_t alignment) override + { + _count += bytes; + return ::operator new(bytes, std::align_val_t(alignment)); + } + + void do_deallocate( + void* p, std::size_t bytes, std::size_t alignment) override + { + _count -= bytes; +#if __cpp_sized_deallocation + ::operator delete(p, bytes, std::align_val_t(alignment)); +#else + ::operator delete(p, std::align_val_t(alignment)); +#endif + } + + bool do_is_equal( + const std::pmr::memory_resource& other) const noexcept override + { + return this == &other; + } + + private: + std::size_t _count = 0; + }; +} // namespace test + +#endif // !defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE) + +#endif // !defined(BOOST_UNORDERED_TEST_PMR_HEADER) diff --git a/test/unordered/pmr_allocator_tests.cpp b/test/unordered/pmr_allocator_tests.cpp new file mode 100644 index 00000000..44e1ca06 --- /dev/null +++ b/test/unordered/pmr_allocator_tests.cpp @@ -0,0 +1,253 @@ +// +// Copyright 2024 Braden Ganetsky. +// 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) + +#include "../helpers/helpers.hpp" +#include "../helpers/pmr.hpp" +#include "../helpers/test.hpp" +#include "../helpers/unordered.hpp" +#include +#include + +#ifdef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + +BOOST_PRAGMA_MESSAGE( + "Test skipped because C++17 header is not available.") + +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ + __MAC_OS_X_VERSION_MIN_REQUIRED < 140000 + +BOOST_PRAGMA_MESSAGE( + "Test skipped because __MAC_OS_X_VERSION_MIN_REQUIRED < 140000"); + +#else + +namespace pmr_allocator_tests { + + using pmr_string = std::basic_string, + std::pmr::polymorphic_allocator >; + +#if defined(BOOST_UNORDERED_CFOA_TESTS) + static boost::unordered::pmr::concurrent_flat_map* + test_string_flat_map; + static boost::unordered::pmr::concurrent_flat_map* + test_pmr_string_flat_map; + static boost::unordered::pmr::concurrent_flat_set* + test_string_flat_set; + static boost::unordered::pmr::concurrent_flat_set* + test_pmr_string_flat_set; +#define PMR_ALLOCATOR_TESTS_ARGS \ + ((test_string_flat_map)(test_pmr_string_flat_map)(test_string_flat_set)(test_pmr_string_flat_set)) +#elif defined(BOOST_UNORDERED_FOA_TESTS) + static boost::unordered::pmr::unordered_flat_map* + test_string_flat_map; + static boost::unordered::pmr::unordered_flat_map* + test_pmr_string_flat_map; + static boost::unordered::pmr::unordered_node_map* + test_string_node_map; + static boost::unordered::pmr::unordered_node_map* + test_pmr_string_node_map; + static boost::unordered::pmr::unordered_flat_set* + test_string_flat_set; + static boost::unordered::pmr::unordered_flat_set* + test_pmr_string_flat_set; + static boost::unordered::pmr::unordered_node_set* + test_string_node_set; + static boost::unordered::pmr::unordered_node_set* + test_pmr_string_node_set; +#define PMR_ALLOCATOR_TESTS_ARGS \ + ((test_string_flat_map)(test_pmr_string_flat_map)(test_string_node_map)(test_pmr_string_node_map)(test_string_flat_set)(test_pmr_string_flat_set)(test_string_node_set)(test_pmr_string_node_set)) +#else + static boost::unordered::pmr::unordered_map* + test_string_map; + static boost::unordered::pmr::unordered_map* + test_pmr_string_map; + static boost::unordered::pmr::unordered_multimap* + test_string_multimap; + static boost::unordered::pmr::unordered_multimap* + test_pmr_string_multimap; + static boost::unordered::pmr::unordered_set* test_string_set; + static boost::unordered::pmr::unordered_set* test_pmr_string_set; + static boost::unordered::pmr::unordered_multiset* + test_string_multiset; + static boost::unordered::pmr::unordered_multiset* + test_pmr_string_multiset; +#define PMR_ALLOCATOR_TESTS_ARGS \ + ((test_string_map)(test_pmr_string_map)(test_string_multimap)(test_pmr_string_multimap)(test_string_set)(test_pmr_string_set)(test_string_multiset)(test_pmr_string_multiset)) +#endif + + template + typename std::enable_if::value, std::size_t>::type + emplace_strings(X& x) + { + std::string_view sv = + "this is a string that's longer than the SBO threshold"; + x.emplace(sv); + // Return how many chars were allocated using a pmr allocator + return std::is_same::value ? sv.size() + 1 + : 0; + } + + template + typename std::enable_if::value, std::size_t>::type + emplace_strings(X& x) + { + std::string_view key = + "this is a string that's longer than the SBO threshold"; + std::string_view value = + "this is another long string that's longer than the SBO threshold"; + x.emplace(key, value); + // Return how many chars were allocated using a pmr allocator + return std::is_same::value + ? key.size() + value.size() + 2 + : 0; + } + + void validate_resource( + pmr_string const& str, test::counted_new_delete_resource const& resource) + { + BOOST_TEST_EQ(str.get_allocator().resource(), &resource); + } + + void validate_resource( + std::string const&, test::counted_new_delete_resource const&) + { + // Pass through + } + + template + typename std::enable_if::value>::type validate_resource( + X& x, test::counted_new_delete_resource const& resource) + { +#if defined(BOOST_UNORDERED_CFOA_TESTS) + x.cvisit_all( + [&resource](auto& element) { validate_resource(element, resource); }); +#else + for (auto& element : x) { + validate_resource(element, resource); + } +#endif + } + + template + typename std::enable_if::value>::type validate_resource( + X& x, test::counted_new_delete_resource const& resource) + { +#if defined(BOOST_UNORDERED_CFOA_TESTS) + x.cvisit_all([&resource](auto& element) { + validate_resource(element.first, resource); + validate_resource(element.second, resource); + }); +#else + for (auto& element : x) { + validate_resource(element.first, resource); + validate_resource(element.second, resource); + } +#endif + } + + template static void pmr_emplace_erase(X*) + { + using container = X; + using allocator_type = typename container::allocator_type; + + test::counted_new_delete_resource resource; + + { + allocator_type alloc(&resource); + container x(alloc); + + std::size_t num_chars = emplace_strings(x); + BOOST_TEST_EQ(x.size(), 1); + std::size_t count_after_emplace = resource.count(); + BOOST_TEST_GT(count_after_emplace, + num_chars + sizeof(typename container::value_type)); + + validate_resource(x, resource); + + x.clear(); + BOOST_TEST_LE(resource.count(), count_after_emplace); + } + + BOOST_TEST_EQ(resource.count(), 0); + } + + // clang-format off + + UNORDERED_TEST( + pmr_emplace_erase, + PMR_ALLOCATOR_TESTS_ARGS + ) + + // clang-format on + + enum operation + { + copy_op, + move_op, + swap_op, + }; + + template void do_operation(X& x1, X& x2, operation op) + { + switch (op) { + case copy_op: + x2 = x1; + return; + case move_op: + x2 = std::move(x1); + return; + case swap_op: + x1.swap(x1); // Swapping with non-equal non-pocs allocators is UB + return; + default: + BOOST_TEST(false); + } + } + + template static void pmr_no_propagate_on_operation(X*, operation op) + { + using container = X; + using allocator_type = typename container::allocator_type; + BOOST_STATIC_ASSERT( + !std::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value); + BOOST_STATIC_ASSERT( + !std::allocator_traits< + allocator_type>::propagate_on_container_move_assignment::value); + BOOST_STATIC_ASSERT(!std::allocator_traits< + allocator_type>::propagate_on_container_swap::value); + + test::counted_new_delete_resource resource1; + test::counted_new_delete_resource resource2; + + allocator_type alloc1(&resource1); + allocator_type alloc2(&resource2); + + container x1(alloc1); + container x2(alloc2); + bool allocators_not_equal = x1.get_allocator() != x2.get_allocator(); + BOOST_TEST(allocators_not_equal); + + emplace_strings(x1); + do_operation(x1, x2, op); + allocators_not_equal = x1.get_allocator() != x2.get_allocator(); + BOOST_TEST(allocators_not_equal); + } + + // clang-format off + + UNORDERED_TEST( + pmr_no_propagate_on_operation, + PMR_ALLOCATOR_TESTS_ARGS + ((copy_op)(move_op)(swap_op)) + ) + + // clang-format on + +} // namespace pmr_allocator_tests + +#endif + +RUN_TESTS()