From 606912183f7834441ec46698d012658e146407b8 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 6 Jun 2026 12:05:04 +0200 Subject: [PATCH 01/11] added from_range ctors and insert_range --- .../boost/unordered/concurrent_flat_map.hpp | 123 +++++++ .../boost/unordered/concurrent_flat_set.hpp | 118 ++++++ .../boost/unordered/concurrent_node_map.hpp | 123 +++++++ .../boost/unordered/concurrent_node_set.hpp | 119 ++++++ .../boost/unordered/detail/implementation.hpp | 42 ++- .../boost/unordered/detail/ranges_support.hpp | 101 ++++++ .../boost/unordered/unordered_flat_map.hpp | 118 +++++- .../boost/unordered/unordered_flat_set.hpp | 115 +++++- include/boost/unordered/unordered_map.hpp | 339 +++++++++++++++++- .../boost/unordered/unordered_node_map.hpp | 118 +++++- .../boost/unordered/unordered_node_set.hpp | 115 +++++- include/boost/unordered/unordered_set.hpp | 334 ++++++++++++++++- test/cfoa/constructor_tests.cpp | 102 +++++- test/cfoa/insert_tests.cpp | 41 ++- test/unordered/constructor_tests.cpp | 207 +++++++++++ test/unordered/insert_tests.cpp | 22 ++ 16 files changed, 2123 insertions(+), 14 deletions(-) create mode 100644 include/boost/unordered/detail/ranges_support.hpp diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index 7ba2286a0a..dcee99130c 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,21 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_map(FromRangeT&&, R&& rg, + size_type n = detail::foa::default_bucket_count, + const hasher& hf = hasher(), const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()) + : table_(n, hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + concurrent_flat_map(concurrent_flat_map const& rhs) : table_(rhs.table_, boost::allocator_select_on_container_copy_construction( @@ -121,6 +137,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_map( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : concurrent_flat_map( + fr, std::forward(rg), 0, hasher(), key_equal(), a) + { + } +#endif + explicit concurrent_flat_map(allocator_type const& a) : table_(detail::foa::default_bucket_count, hasher(), key_equal(), a) { @@ -171,6 +200,29 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_map( + FromRangeT&& fr, R&& rg, size_type n, const allocator_type& a) + : concurrent_flat_map( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_map(FromRangeT&& fr, R&& rg, size_type n, + const hasher& hf, const allocator_type& a) + : concurrent_flat_map(fr, std::forward(rg), n, hf, key_equal(), a) + { + } +#endif + concurrent_flat_map( std::initializer_list il, const allocator_type& a) : concurrent_flat_map( @@ -430,6 +482,20 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + size_type insert_range(R&& rg) + { + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) { + if (table_.emplace(*first++)) ++count_elements; + } + return count_elements; + } +#endif + size_type insert(std::initializer_list ilist) { return this->insert(ilist.begin(), ilist.end()); @@ -980,6 +1046,27 @@ namespace boost { boost::unordered::detail::iter_val_t, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator< + boost::unordered::detail::range_to_alloc_t >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + concurrent_flat_map(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_flat_map, + boost::unordered::detail::range_mapped_t, Hash, Pred, + Allocator>; +#endif + template >, class Pred = std::equal_to >, @@ -1027,6 +1114,42 @@ namespace boost { std::equal_to >, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + concurrent_flat_map(FromRangeT&&, R&&, std::size_t, Allocator) + -> concurrent_flat_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template > > + concurrent_flat_map(FromRangeT&&, R&&, Allocator) + -> concurrent_flat_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + concurrent_flat_map( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> concurrent_flat_map, + boost::unordered::detail::range_mapped_t, Hash, + std::equal_to >, + Allocator>; +#endif + template > > concurrent_flat_map(std::initializer_list >, std::size_t, diff --git a/include/boost/unordered/concurrent_flat_set.hpp b/include/boost/unordered/concurrent_flat_set.hpp index 162c4c2838..2c23c13740 100644 --- a/include/boost/unordered/concurrent_flat_set.hpp +++ b/include/boost/unordered/concurrent_flat_set.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -99,6 +100,21 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_set(FromRangeT&&, R&& rg, + size_type n = detail::foa::default_bucket_count, + const hasher& hf = hasher(), const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()) + : table_(n, hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + concurrent_flat_set(concurrent_flat_set const& rhs) : table_(rhs.table_, boost::allocator_select_on_container_copy_construction( @@ -118,6 +134,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_set( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : concurrent_flat_set( + fr, std::forward(rg), 0, hasher(), key_equal(), a) + { + } +#endif + explicit concurrent_flat_set(allocator_type const& a) : table_(detail::foa::default_bucket_count, hasher(), key_equal(), a) { @@ -168,6 +197,29 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_set( + FromRangeT&& fr, R&& rg, size_type n, const allocator_type& a) + : concurrent_flat_set( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_flat_set(FromRangeT&& fr, R&& rg, size_type n, + const hasher& hf, const allocator_type& a) + : concurrent_flat_set(fr, std::forward(rg), n, hf, key_equal(), a) + { + } +#endif + concurrent_flat_set( std::initializer_list il, const allocator_type& a) : concurrent_flat_set( @@ -436,6 +488,20 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + size_type insert_range(R&& rg) + { + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) { + if (table_.emplace(*first++)) ++count_elements; + } + return count_elements; + } +#endif + size_type insert(std::initializer_list ilist) { return this->insert(ilist.begin(), ilist.end()); @@ -853,6 +919,25 @@ namespace boost { typename std::iterator_traits::value_type, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + concurrent_flat_set(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_flat_set, + Hash, Pred, Allocator>; +#endif + template , class Pred = std::equal_to, class Allocator = std::allocator, class = std::enable_if_t >, @@ -894,6 +979,39 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + concurrent_flat_set(FromRangeT&&, R&&, std::size_t, Allocator) + -> concurrent_flat_set, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + concurrent_flat_set( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> concurrent_flat_set, + Hash, std::equal_to >, + Allocator>; + + template > > + concurrent_flat_set(FromRangeT&&, R&&, Allocator) + -> concurrent_flat_set, + boost::hash >, + std::equal_to >, + Allocator>; +#endif + template > > concurrent_flat_set(std::initializer_list, std::size_t, Allocator) diff --git a/include/boost/unordered/concurrent_node_map.hpp b/include/boost/unordered/concurrent_node_map.hpp index 50f667bc36..08ace1a888 100644 --- a/include/boost/unordered/concurrent_node_map.hpp +++ b/include/boost/unordered/concurrent_node_map.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,21 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_map(FromRangeT&&, R&& rg, + size_type n = detail::foa::default_bucket_count, + const hasher& hf = hasher(), const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()) + : table_(n, hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + concurrent_node_map(concurrent_node_map const& rhs) : table_(rhs.table_, boost::allocator_select_on_container_copy_construction( @@ -129,6 +145,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_map( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : concurrent_node_map( + fr, std::forward(rg), 0, hasher(), key_equal(), a) + { + } +#endif + explicit concurrent_node_map(allocator_type const& a) : table_(detail::foa::default_bucket_count, hasher(), key_equal(), a) { @@ -179,6 +208,29 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_map( + FromRangeT&& fr, R&& rg, size_type n, const allocator_type& a) + : concurrent_node_map( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_map(FromRangeT&& fr, R&& rg, size_type n, + const hasher& hf, const allocator_type& a) + : concurrent_node_map(fr, std::forward(rg), n, hf, key_equal(), a) + { + } +#endif + concurrent_node_map( std::initializer_list il, const allocator_type& a) : concurrent_node_map( @@ -438,6 +490,20 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + size_type insert_range(R&& rg) + { + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) { + if (table_.emplace(*first++)) ++count_elements; + } + return count_elements; + } +#endif + size_type insert(std::initializer_list ilist) { return this->insert(ilist.begin(), ilist.end()); @@ -1128,6 +1194,27 @@ namespace boost { boost::unordered::detail::iter_val_t, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator< + boost::unordered::detail::range_to_alloc_t >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + concurrent_node_map(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_node_map, + boost::unordered::detail::range_mapped_t, Hash, Pred, + Allocator>; +#endif + template >, class Pred = std::equal_to >, @@ -1175,6 +1262,42 @@ namespace boost { std::equal_to >, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + concurrent_node_map(FromRangeT&&, R&&, std::size_t, Allocator) + -> concurrent_node_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template > > + concurrent_node_map(FromRangeT&&, R&&, Allocator) + -> concurrent_node_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + concurrent_node_map( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> concurrent_node_map, + boost::unordered::detail::range_mapped_t, Hash, + std::equal_to >, + Allocator>; +#endif + template > > concurrent_node_map(std::initializer_list >, std::size_t, diff --git a/include/boost/unordered/concurrent_node_set.hpp b/include/boost/unordered/concurrent_node_set.hpp index 3cdcd947f6..b5c7582765 100644 --- a/include/boost/unordered/concurrent_node_set.hpp +++ b/include/boost/unordered/concurrent_node_set.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,21 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_set(FromRangeT&&, R&& rg, + size_type n = detail::foa::default_bucket_count, + const hasher& hf = hasher(), const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()) + : table_(n, hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + concurrent_node_set(concurrent_node_set const& rhs) : table_(rhs.table_, boost::allocator_select_on_container_copy_construction( @@ -126,6 +142,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_set( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : concurrent_node_set( + fr, std::forward(rg), 0, hasher(), key_equal(), a) + { + } +#endif + explicit concurrent_node_set(allocator_type const& a) : table_(detail::foa::default_bucket_count, hasher(), key_equal(), a) { @@ -176,6 +205,29 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_set( + FromRangeT&& fr, R&& rg, size_type n, const allocator_type& a) + : concurrent_node_set( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + concurrent_node_set(FromRangeT&& fr, R&& rg, size_type n, + const hasher& hf, const allocator_type& a) + : concurrent_node_set(fr, std::forward(rg), n, hf, key_equal(), a) + { + } +#endif + concurrent_node_set( std::initializer_list il, const allocator_type& a) : concurrent_node_set( @@ -444,6 +496,20 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + size_type insert_range(R&& rg) + { + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) { + if (table_.emplace(*first++)) ++count_elements; + } + return count_elements; + } +#endif + size_type insert(std::initializer_list ilist) { return this->insert(ilist.begin(), ilist.end()); @@ -1002,6 +1068,26 @@ namespace boost { typename std::iterator_traits::value_type, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + concurrent_node_set(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_node_set, + Hash, Pred, Allocator>; +#endif + + template , class Pred = std::equal_to, class Allocator = std::allocator, class = std::enable_if_t >, @@ -1043,6 +1129,39 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + concurrent_node_set(FromRangeT&&, R&&, std::size_t, Allocator) + -> concurrent_node_set, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + concurrent_node_set( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> concurrent_node_set, + Hash, std::equal_to >, + Allocator>; + + template > > + concurrent_node_set(FromRangeT&&, R&&, Allocator) + -> concurrent_node_set, + boost::hash >, + std::equal_to >, + Allocator>; +#endif + template > > concurrent_node_set(std::initializer_list, std::size_t, Allocator) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index b6ce49fe5f..16e36493e3 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,29 @@ namespace boost { boost::unordered::detail::insert_size(i, j), num_buckets); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + inline std::size_t insert_size(R&& rg) + { + if constexpr (std::ranges::sized_range) + return std::ranges::size(std::forward(rg)); + else if constexpr (std::ranges::forward_range) + return std::ranges::distance(std::forward(rg)); + else + return 1; + } + + template + inline std::size_t initial_size(R&& rg, + std::size_t num_buckets = + boost::unordered::detail::default_bucket_count) + { + return (std::max)( + boost::unordered::detail::insert_size(std::forward(rg)), + num_buckets); + } +#endif + ////////////////////////////////////////////////////////////////////////// // compressed @@ -2155,8 +2179,8 @@ namespace boost { // if hash function throws, or inserting > 1 element, basic exception // safety strong otherwise - template - void insert_range_unique(no_key, InputIt i, InputIt j) + template + void insert_range_unique(no_key, InputIt i, Sentinel j) { hasher const& hf = this->hash_function(); node_allocator_type alloc = this->node_alloc(); @@ -2543,14 +2567,20 @@ namespace boost { // if hash function throws, or inserting > 1 element, basic exception // safety. Strong otherwise - template + template typename boost::unordered::detail::enable_if_forward::type - insert_range_equiv(I i, I j) + insert_range_equiv(I i, S j) { if (i == j) return; +#if !defined(BOOST_UNORDERED_NO_RANGES) + std::size_t distance = static_cast( + std::ranges::distance(i, j)); +#else std::size_t distance = static_cast(std::distance(i, j)); +#endif + if (distance == 1) { emplace_equiv(boost::unordered::detail::func::construct_node( this->node_alloc(), *i)); @@ -2566,9 +2596,9 @@ namespace boost { } } - template + template typename boost::unordered::detail::disable_if_forward::type - insert_range_equiv(I i, I j) + insert_range_equiv(I i, S j) { for (; i != j; ++i) { emplace_equiv(boost::unordered::detail::func::construct_node( diff --git a/include/boost/unordered/detail/ranges_support.hpp b/include/boost/unordered/detail/ranges_support.hpp new file mode 100644 index 0000000000..696d2bc357 --- /dev/null +++ b/include/boost/unordered/detail/ranges_support.hpp @@ -0,0 +1,101 @@ +/* Copyright 2026 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) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_RANGES_SUPPORT_HPP +#define BOOST_UNORDERED_DETAIL_RANGES_SUPPORT_HPP + +#include +#include + +#if defined(BOOST_NO_CXX20_HDR_CONCEPTS) || defined(BOOST_NO_CXX20_HDR_RANGES) +#define BOOST_UNORDERED_NO_RANGES +#elif BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 170100) && \ + defined(BOOST_LIBSTDCXX_VERSION) +/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109647 + * https://github.com/llvm/llvm-project/issues/49620 + */ +#define BOOST_UNORDERED_NO_RANGES +#endif + +#if !defined(BOOST_UNORDERED_NO_RANGES) +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace unordered { + +struct from_range_t { explicit from_range_t() = default; }; +inline constexpr from_range_t from_range{}; + +namespace detail { + +/* Code below picks std::from_range_t if it exists, + * boost::unordered::from_range_t otherwise. Technique explained at + https://bannalia.blogspot.com/2016/09/compile-time-checking-existence-of.html + */ + +struct from_range_t_hook{}; + +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +namespace std { + +template<> struct hash< ::boost::unordered::detail::from_range_t_hook> +{ + using from_range_t_type = decltype([] { + using namespace ::boost::unordered; + return from_range_t{}; + }()); + + /* make standard happy */ + std::size_t operator()( + const ::boost::unordered::detail::from_range_t_hook&) const; +}; + +} /* namespace std */ + +namespace boost { +namespace unordered { +namespace detail { + +template +concept convertible_to_from_range_t = + std::is_convertible_v || + std::is_convertible_v::from_range_t_type>; + +template +concept container_compatible_range = + std::ranges::input_range && + std::convertible_to, T>; + +template +using range_key_t = + std::remove_cvref_t< + std::tuple_element_t<0, std::ranges::range_value_t>>; + +template +using range_mapped_t = + std::remove_cvref_t< + std::tuple_element_t<1, std::ranges::range_value_t>>; + +template +using range_to_alloc_t = + std::pair, range_mapped_t>; + +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif /* !defined(BOOST_UNORDERED_NO_RANGES) */ +#endif diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index e478314afc..18d07ea79c 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2024-2026 Joaquin M Lopez Munoz // Copyright (C) 2026 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 +15,7 @@ #include #include #include +#include #include #include #include @@ -108,6 +109,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_map( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : unordered_flat_map( + fr, std::forward(rg), size_type(0), hasher(), key_equal(), a) + { + } +#endif + explicit unordered_flat_map(allocator_type const& a) : unordered_flat_map(0, a) { @@ -136,6 +150,41 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_map(FromRangeT&&, R&& rg, size_type n = 0, + hasher const& h = hasher(), key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_flat_map(n, h, pred, a) + { + this->insert_range(std::forward(rg)); + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_map( + FromRangeT&& fr, R&& rg, size_type n, allocator_type const& a) + : unordered_flat_map( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_map(FromRangeT&& fr, R&& rg, size_type n, + hasher const& h, allocator_type const& a) + : unordered_flat_map(fr, std::forward(rg), n, h, key_equal(), a) + { + } +#endif + unordered_flat_map(unordered_flat_map const& other) : table_(other.table_) { } @@ -278,6 +327,16 @@ namespace boost { } } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + BOOST_FORCEINLINE void insert_range(R&& rg) + { + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) table_.emplace(*first++); + } +#endif + void insert(std::initializer_list ilist) { this->insert(ilist.begin(), ilist.end()); @@ -756,6 +815,27 @@ namespace boost { boost::unordered::detail::iter_val_t, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator< + boost::unordered::detail::range_to_alloc_t >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_flat_map(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_flat_map, + boost::unordered::detail::range_mapped_t, Hash, Pred, + Allocator>; +#endif + template >, class Pred = std::equal_to >, @@ -800,6 +880,42 @@ namespace boost { std::equal_to >, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_flat_map(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_flat_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template > > + unordered_flat_map(FromRangeT&&, R&&, Allocator) + -> unordered_flat_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_flat_map( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_flat_map, + boost::unordered::detail::range_mapped_t, Hash, + std::equal_to >, + Allocator>; +#endif + template > > unordered_flat_map(std::initializer_list >, std::size_t, diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp index 629750342c..ffeaa43f28 100644 --- a/include/boost/unordered/unordered_flat_set.hpp +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2024-2026 Joaquin M Lopez Munoz // Copyright (C) 2026 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 +15,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_set( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : unordered_flat_set( + fr, std::forward(rg), size_type(0), hasher(), key_equal(), a) + { + } +#endif + explicit unordered_flat_set(allocator_type const& a) : unordered_flat_set(0, a) { @@ -132,6 +146,41 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_set(FromRangeT&&, R&& rg, size_type n = 0, + hasher const& h = hasher(), key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_flat_set(n, h, pred, a) + { + this->insert_range(std::forward(rg)); + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_set( + FromRangeT&& fr, R&& rg, size_type n, allocator_type const& a) + : unordered_flat_set( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_flat_set(FromRangeT&& fr, R&& rg, size_type n, + hasher const& h, allocator_type const& a) + : unordered_flat_set(fr, std::forward(rg), n, h, key_equal(), a) + { + } +#endif + unordered_flat_set(unordered_flat_set const& other) : table_(other.table_) { } @@ -289,6 +338,16 @@ namespace boost { } } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + void insert_range(R&& rg) + { + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) table_.emplace(*first++); + } +#endif + void insert(std::initializer_list ilist) { this->insert(ilist.begin(), ilist.end()); @@ -574,6 +633,25 @@ namespace boost { typename std::iterator_traits::value_type, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_flat_set(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_flat_set, + Hash, Pred, Allocator>; +#endif + template , class Pred = std::equal_to, class Allocator = std::allocator, class = std::enable_if_t >, @@ -605,6 +683,29 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_flat_set(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_flat_set, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_flat_set( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_flat_set, + Hash, std::equal_to >, + Allocator>; +#endif + template > > unordered_flat_set(std::initializer_list, std::size_t, Allocator) @@ -626,6 +727,18 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_flat_set(FromRangeT&&, R&&, Allocator) + -> unordered_flat_set, + boost::hash >, + std::equal_to >, + Allocator>; +#endif + template > > unordered_flat_set(std::initializer_list, Allocator) diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 6b7418b033..29b96679db 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -1,7 +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. +// Copyright (C) 2024-2026 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) @@ -16,6 +16,7 @@ #endif #include +#include #include #include #include @@ -92,6 +93,17 @@ namespace boost { const hasher& = hasher(), const key_equal& = key_equal(), const allocator_type& = allocator_type()); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map(FromRangeT&&, R&&, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); +#endif + unordered_map(unordered_map const&); unordered_map(unordered_map&& other) @@ -126,6 +138,27 @@ namespace boost { unordered_map( InputIt, InputIt, size_type, const hasher&, const allocator_type&); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map(FromRangeT&&, R&&, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map(FromRangeT&&, R&&, size_type, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map( + FromRangeT&&, R&&, size_type, const hasher&, const allocator_type&); +#endif + unordered_map(std::initializer_list, const allocator_type&); unordered_map( @@ -248,6 +281,11 @@ namespace boost { template void insert(InputIt, InputIt); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > R> + void insert_range(R&&); +#endif + void insert(std::initializer_list); // extract @@ -622,6 +660,27 @@ namespace boost { boost::unordered::detail::iter_val_t, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator< + boost::unordered::detail::range_to_alloc_t >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_map(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_map, + boost::unordered::detail::range_mapped_t, Hash, Pred, + Allocator>; +#endif + template >, class Pred = std::equal_to >, @@ -664,6 +723,42 @@ namespace boost { std::equal_to >, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_map(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template > > + unordered_map(FromRangeT&&, R&&, Allocator) + -> unordered_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_map( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_map, + boost::unordered::detail::range_mapped_t, Hash, + std::equal_to >, + Allocator>; +#endif + template > > unordered_map(std::initializer_list >, std::size_t, @@ -742,6 +837,17 @@ namespace boost { const hasher& = hasher(), const key_equal& = key_equal(), const allocator_type& = allocator_type()); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap(FromRangeT&&, R&&, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); +#endif + unordered_multimap(unordered_multimap const&); unordered_multimap(unordered_multimap&& other) @@ -777,6 +883,27 @@ namespace boost { unordered_multimap( InputIt, InputIt, size_type, const hasher&, const allocator_type&); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap(FromRangeT&&, R&&, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap(FromRangeT&&, R&&, size_type, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap( + FromRangeT&&, R&&, size_type, const hasher&, const allocator_type&); +#endif + unordered_multimap( std::initializer_list, const allocator_type&); @@ -894,6 +1021,11 @@ namespace boost { template void insert(InputIt, InputIt); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > R> + void insert_range(R&&); +#endif + void insert(std::initializer_list); // extract @@ -1141,6 +1273,26 @@ namespace boost { boost::unordered::detail::iter_val_t, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator< + boost::unordered::detail::range_to_alloc_t >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_multimap(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_multimap, + boost::unordered::detail::range_mapped_t, Hash, Pred, + Allocator>; +#endif template >, class Pred = std::equal_to >, @@ -1184,6 +1336,42 @@ namespace boost { std::equal_to >, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_multimap(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_multimap, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template > > + unordered_multimap(FromRangeT&&, R&&, Allocator) + -> unordered_multimap, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_multimap( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_multimap, + boost::unordered::detail::range_mapped_t, Hash, + std::equal_to >, + Allocator>; +#endif + template > > unordered_multimap(std::initializer_list >, std::size_t, @@ -1231,6 +1419,23 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map::unordered_map(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const key_equal& eql, + const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_map::unordered_map(unordered_map const& other) : table_(other.table_, @@ -1303,6 +1508,7 @@ namespace boost { this->insert(f, l); } + template template unordered_map::unordered_map( @@ -1323,6 +1529,51 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map::unordered_map( + FromRangeT&&, R&& rg, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size( + std::forward(rg), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map::unordered_map( + FromRangeT&&, R&& rg, size_type n, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_map::unordered_map(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, key_equal(), a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_map::unordered_map( std::initializer_list list, const allocator_type& a) @@ -1395,6 +1646,20 @@ namespace boost { } } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template > R> + void unordered_map::insert_range(R&& rg) + { + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + if (first != last) { + table_.insert_range_unique( + table::extractor::extract(*first), first, last); + } + } +#endif + template void unordered_map::insert( std::initializer_list list) @@ -1757,6 +2022,23 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap::unordered_multimap(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const key_equal& eql, + const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_multimap::unordered_multimap( unordered_multimap const& other) @@ -1851,6 +2133,51 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap::unordered_multimap( + FromRangeT&&, R&& rg, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size( + std::forward(rg), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap::unordered_multimap( + FromRangeT&&, R&& rg, size_type n, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range > R + > + unordered_multimap::unordered_multimap(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, key_equal(), a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_multimap::unordered_multimap( std::initializer_list list, const allocator_type& a) @@ -1921,6 +2248,16 @@ namespace boost { table_.insert_range_equiv(first, last); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template > R> + void unordered_multimap::insert_range(R&& rg) + { + table_.insert_range_equiv(std::ranges::begin(rg), std::ranges::end(rg)); + } +#endif + + template void unordered_multimap::insert( std::initializer_list list) diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index 73dbdc841b..a8b05cbd43 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2024-2026 Joaquin M Lopez Munoz // Copyright (C) 2026 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 +16,7 @@ #include #include #include +#include #include #include #include @@ -115,6 +116,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_map( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : unordered_node_map( + fr, std::forward(rg), size_type(0), hasher(), key_equal(), a) + { + } +#endif + explicit unordered_node_map(allocator_type const& a) : unordered_node_map(0, a) { @@ -143,6 +157,41 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_map(FromRangeT&&, R&& rg, size_type n = 0, + hasher const& h = hasher(), key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_node_map(n, h, pred, a) + { + this->insert_range(std::forward(rg)); + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_map( + FromRangeT&& fr, R&& rg, size_type n, allocator_type const& a) + : unordered_node_map( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_map(FromRangeT&& fr, R&& rg, size_type n, + hasher const& h, allocator_type const& a) + : unordered_node_map(fr, std::forward(rg), n, h, key_equal(), a) + { + } +#endif + unordered_node_map(unordered_node_map const& other) : table_(other.table_) { } @@ -285,6 +334,16 @@ namespace boost { } } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + BOOST_FORCEINLINE void insert_range(R&& rg) + { + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) table_.emplace(*first++); + } +#endif + void insert(std::initializer_list ilist) { this->insert(ilist.begin(), ilist.end()); @@ -830,6 +889,27 @@ namespace boost { boost::unordered::detail::iter_val_t, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator< + boost::unordered::detail::range_to_alloc_t >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_node_map(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_node_map, + boost::unordered::detail::range_mapped_t, Hash, Pred, + Allocator>; +#endif + template >, class Pred = std::equal_to >, @@ -874,6 +954,42 @@ namespace boost { std::equal_to >, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_node_map(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_node_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template > > + unordered_node_map(FromRangeT&&, R&&, Allocator) + -> unordered_node_map, + boost::unordered::detail::range_mapped_t, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_node_map( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_node_map, + boost::unordered::detail::range_mapped_t, Hash, + std::equal_to >, + Allocator>; +#endif + template > > unordered_node_map(std::initializer_list >, std::size_t, diff --git a/include/boost/unordered/unordered_node_set.hpp b/include/boost/unordered/unordered_node_set.hpp index 48a2e212d6..200468590c 100644 --- a/include/boost/unordered/unordered_node_set.hpp +++ b/include/boost/unordered/unordered_node_set.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas -// Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2024-2026 Joaquin M Lopez Munoz // Copyright (C) 2026 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) @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,19 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_set( + FromRangeT&& fr, R&& rg, allocator_type const& a) + : unordered_node_set( + fr, std::forward(rg), size_type(0), hasher(), key_equal(), a) + { + } +#endif + explicit unordered_node_set(allocator_type const& a) : unordered_node_set(0, a) { @@ -141,6 +155,41 @@ namespace boost { { } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_set(FromRangeT&&, R&& rg, size_type n = 0, + hasher const& h = hasher(), key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_node_set(n, h, pred, a) + { + this->insert_range(std::forward(rg)); + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_set( + FromRangeT&& fr, R&& rg, size_type n, allocator_type const& a) + : unordered_node_set( + fr, std::forward(rg), n, hasher(), key_equal(), a) + { + } + + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_node_set(FromRangeT&& fr, R&& rg, size_type n, + hasher const& h, allocator_type const& a) + : unordered_node_set(fr, std::forward(rg), n, h, key_equal(), a) + { + } +#endif + unordered_node_set(unordered_node_set const& other) : table_(other.table_) { } @@ -298,6 +347,16 @@ namespace boost { } } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + void insert_range(R&& rg) + { + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + while (first != last) table_.emplace(*first++); + } +#endif + void insert(std::initializer_list ilist) { this->insert(ilist.begin(), ilist.end()); @@ -650,6 +709,25 @@ namespace boost { typename std::iterator_traits::value_type, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_node_set(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::foa::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_node_set, + Hash, Pred, Allocator>; +#endif + template , class Pred = std::equal_to, class Allocator = std::allocator, class = std::enable_if_t >, @@ -681,6 +759,29 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_node_set(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_node_set, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_node_set( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_node_set, + Hash, std::equal_to >, + Allocator>; +#endif + template > > unordered_node_set(std::initializer_list, std::size_t, Allocator) @@ -702,6 +803,18 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_node_set(FromRangeT&&, R&&, Allocator) + -> unordered_node_set, + boost::hash >, + std::equal_to >, + Allocator>; +#endif + template > > unordered_node_set(std::initializer_list, Allocator) diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index eae846ddba..4821eb8bda 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -1,7 +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. +// Copyright (C) 2024-2026 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) @@ -15,6 +15,7 @@ #pragma once #endif +#include #include #include #include @@ -88,6 +89,17 @@ namespace boost { const hasher& = hasher(), const key_equal& = key_equal(), const allocator_type& = allocator_type()); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set(FromRangeT&&, R&&, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); +#endif + unordered_set(unordered_set const&); unordered_set(unordered_set&& other) @@ -122,6 +134,27 @@ namespace boost { unordered_set( InputIt, InputIt, size_type, const hasher&, const allocator_type&); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set(FromRangeT&&, R&&, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set(FromRangeT&&, R&&, size_type, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set( + FromRangeT&&, R&&, size_type, const hasher&, const allocator_type&); +#endif + unordered_set(std::initializer_list, const allocator_type&); unordered_set( @@ -246,6 +279,11 @@ namespace boost { template void insert(InputIt, InputIt); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + void insert_range(R&&); +#endif + void insert(std::initializer_list); // extract @@ -478,6 +516,25 @@ namespace boost { -> unordered_set::value_type, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_set(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_set, + Hash, Pred, Allocator>; +#endif + template , class Pred = std::equal_to, class Allocator = std::allocator, class = std::enable_if_t >, @@ -507,6 +564,29 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_set(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_set, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_set( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_set, + Hash, std::equal_to >, + Allocator>; +#endif + template > > unordered_set(std::initializer_list, std::size_t, Allocator) @@ -527,6 +607,18 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_set(FromRangeT&&, R&&, Allocator) + -> unordered_set, + boost::hash >, + std::equal_to >, + Allocator>; +#endif + template > > unordered_set(std::initializer_list, Allocator) @@ -585,6 +677,18 @@ namespace boost { const hasher& = hasher(), const key_equal& = key_equal(), const allocator_type& = allocator_type()); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset(FromRangeT&&, R&&, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); +#endif + + unordered_multiset(unordered_multiset const&); unordered_multiset(unordered_multiset&& other) @@ -620,6 +724,27 @@ namespace boost { unordered_multiset( InputIt, InputIt, size_type, const hasher&, const allocator_type&); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset(FromRangeT&&, R&&, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset(FromRangeT&&, R&&, size_type, const allocator_type&); + + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset( + FromRangeT&&, R&&, size_type, const hasher&, const allocator_type&); +#endif + unordered_multiset( std::initializer_list, const allocator_type&); @@ -720,6 +845,11 @@ namespace boost { template void insert(InputIt, InputIt); +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R> + void insert_range(R&&); +#endif + void insert(std::initializer_list); // extract @@ -942,6 +1072,25 @@ namespace boost { typename std::iterator_traits::value_type, Hash, Pred, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template < + boost::unordered::detail::convertible_to_from_range_t FromRangeT, + std::ranges::input_range R, + class Hash = + boost::hash >, + class Pred = + std::equal_to >, + class Allocator = std::allocator >, + class = std::enable_if_t >, + class = std::enable_if_t >, + class = std::enable_if_t > > + unordered_multiset(FromRangeT&&, R&&, + std::size_t = boost::unordered::detail::default_bucket_count, + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_multiset, + Hash, Pred, Allocator>; +#endif + template , class Pred = std::equal_to, class Allocator = std::allocator, class = std::enable_if_t >, @@ -973,6 +1122,29 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_multiset(FromRangeT&&, R&&, std::size_t, Allocator) + -> unordered_multiset, + boost::hash >, + std::equal_to >, + Allocator>; + + template >, + class = std::enable_if_t > > + unordered_multiset( + FromRangeT&&, R&&, std::size_t, Hash, Allocator) + -> unordered_multiset, + Hash, std::equal_to >, + Allocator>; +#endif + template > > unordered_multiset(std::initializer_list, std::size_t, Allocator) @@ -994,6 +1166,18 @@ namespace boost { std::equal_to::value_type>, Allocator>; +#if !defined(BOOST_UNORDERED_NO_RANGES) + template > > + unordered_multiset(FromRangeT&&, R&&, Allocator) + -> unordered_multiset, + boost::hash >, + std::equal_to >, + Allocator>; +#endif + template > > unordered_multiset(std::initializer_list, Allocator) @@ -1023,6 +1207,23 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set::unordered_set(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const key_equal& eql, + const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_set::unordered_set(unordered_set const& other) : table_(other.table_, @@ -1115,6 +1316,51 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set::unordered_set( + FromRangeT&&, R&& rg, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size( + std::forward(rg), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set::unordered_set( + FromRangeT&&, R&& rg, size_type n, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_set::unordered_set(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, key_equal(), a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_set::unordered_set( std::initializer_list list, const allocator_type& a) @@ -1187,6 +1433,20 @@ namespace boost { } } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template R> + void unordered_set::insert_range(R&& rg) + { + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + if (first != last) { + table_.insert_range_unique( + table::extractor::extract(*first), first, last); + } + } +#endif + template void unordered_set::insert( std::initializer_list list) @@ -1420,6 +1680,23 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset::unordered_multiset(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const key_equal& eql, + const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, eql, a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_multiset::unordered_multiset( unordered_multiset const& other) @@ -1513,6 +1790,51 @@ namespace boost { this->insert(f, l); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset::unordered_multiset( + FromRangeT&&, R&& rg, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size( + std::forward(rg), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset::unordered_multiset( + FromRangeT&&, R&& rg, size_type n, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hasher(), key_equal(), a) + { + this->insert_range(std::forward(rg)); + } + + template + template < + detail::convertible_to_from_range_t FromRangeT, + detail::container_compatible_range R + > + unordered_multiset::unordered_multiset(FromRangeT&&, R&& rg, + size_type n, const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(std::forward(rg), n), + hf, key_equal(), a) + { + this->insert_range(std::forward(rg)); + } +#endif + template unordered_multiset::unordered_multiset( std::initializer_list list, const allocator_type& a) @@ -1582,6 +1904,16 @@ namespace boost { table_.insert_range_equiv(first, last); } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + template R> + void unordered_multiset::insert_range(R&& rg) + { + table_.insert_range_equiv( + std::ranges::begin(rg), std::ranges::end(rg)); + } +#endif + template void unordered_multiset::insert( std::initializer_list list) diff --git a/test/cfoa/constructor_tests.cpp b/test/cfoa/constructor_tests.cpp index 2d8c5f95fc..2cf39f0bcf 100644 --- a/test/cfoa/constructor_tests.cpp +++ b/test/cfoa/constructor_tests.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2023 Christian Mazakas -// Copyright (C) 2023-2024 Joaquin M Lopez Munoz +// Copyright (C) 2023-2026 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) @@ -10,6 +10,8 @@ #include #include +#include + test::seed_t initialize_seed(4122023); using test::default_generator; @@ -702,6 +704,97 @@ namespace { } } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template + void range_with_all_params(std::pair p) + { + using value_type = typename X::value_type; + static constexpr auto value_type_cardinality = + value_cardinality::value; + using allocator_type = typename X::allocator_type; + + auto init_list = p.second; + auto sentineled_rg = + std::list{init_list} | std::views::take(init_list.size()); + + { + raii::reset_counts(); + + X x( + boost::unordered::from_range, + init_list, 0, hasher(1), key_equal(2), allocator_type(3)); + + BOOST_TEST_EQ(x.size(), 11u); + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST_EQ(raii::default_constructor, 0u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size() / 2u); + BOOST_TEST_EQ( + raii::move_constructor, 0u); + } + check_raii_counts(); + + { + raii::reset_counts(); + + X x(boost::unordered::from_range, sentineled_rg, allocator_type(3)); + + BOOST_TEST_EQ(x.size(), 11u); + BOOST_TEST_EQ(x.hash_function(), hasher()); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST_EQ(raii::default_constructor, 0u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size() / 2u); + BOOST_TEST_EQ( + raii::move_constructor, 0u); + } + check_raii_counts(); + + { + raii::reset_counts(); + + X x(boost::unordered::from_range, init_list, 0, allocator_type(3)); + + BOOST_TEST_EQ(x.size(), 11u); + BOOST_TEST_EQ(x.hash_function(), hasher()); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST_EQ(raii::default_constructor, 0u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size() / 2u); + BOOST_TEST_EQ( + raii::move_constructor, 0u); + } + check_raii_counts(); + + { + raii::reset_counts(); + + X x( + boost::unordered::from_range, + sentineled_rg, 0, hasher(1), allocator_type(3)); + + BOOST_TEST_EQ(x.size(), 11u); + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal()); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST_EQ(raii::default_constructor, 0u); + BOOST_TEST_EQ( + raii::copy_constructor, value_type_cardinality * init_list.size() / 2u); + BOOST_TEST_EQ( + raii::move_constructor, 0u); + } + check_raii_counts(); + } +#endif + template void initializer_list_with_all_params(std::pair p) { @@ -1040,6 +1133,13 @@ UNORDERED_TEST( explicit_allocator, ((test_map)(test_node_map)(test_set)(test_node_set))) +#if !defined(BOOST_UNORDERED_NO_RANGES) +UNORDERED_TEST( + range_with_all_params, + ((test_map_and_init_list)(test_node_map_and_init_list) + (test_set_and_init_list)(test_node_set_and_init_list))) +#endif + UNORDERED_TEST( initializer_list_with_all_params, ((test_map_and_init_list)(test_node_map_and_init_list) diff --git a/test/cfoa/insert_tests.cpp b/test/cfoa/insert_tests.cpp index 985f9a4039..4569d2838e 100644 --- a/test/cfoa/insert_tests.cpp +++ b/test/cfoa/insert_tests.cpp @@ -175,6 +175,39 @@ namespace { } } iterator_range_inserter; +#if !defined(BOOST_UNORDERED_NO_RANGES) + struct range_inserter_type + { + template void operator()(std::vector& values, X& x) + { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + std::vector values2; + values2.reserve(values.size()); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); + } + + auto sz = x.size(); + std::atomic num_inserts{0}; + std::atomic num_attempted_inserts{0}; + thread_runner(values2, [&x, &num_inserts, &num_attempted_inserts](boost::span s) { + num_inserts += x.insert_range(s | std::views::take(s.size() / 2)); + num_inserts += x.insert_range(s); + num_attempted_inserts += s.size() + s.size() / 2; + }); + BOOST_TEST_EQ(x.size(), sz + num_inserts); + + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * num_attempted_inserts); + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_EQ(raii::copy_assignment, 0u); + BOOST_TEST_EQ(raii::move_assignment, 0u); + } + } range_inserter; +#endif + struct lvalue_insert_or_assign_copy_assign_type { template void operator()(std::vector& values, X& x) @@ -1265,13 +1298,19 @@ UNORDERED_TEST( ((map_and_init_list)(node_map_and_init_list) (set_and_init_list)(node_set_and_init_list))) +#if !defined(BOOST_UNORDERED_NO_RANGES) +#define RANGE_INSERTER (range_inserter) +#else +#define RANGE_INSERTER +#endif + UNORDERED_TEST( insert, ((map)(fancy_map)(node_map)(fancy_node_map) (set)(fancy_set)(node_set)(fancy_node_set)) ((value_type_generator_factory)(init_type_generator_factory)) ((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter) - (norehash_lvalue_inserter)(norehash_rvalue_inserter) + RANGE_INSERTER (norehash_lvalue_inserter)(norehash_rvalue_inserter) (lvalue_insert_or_cvisit)(lvalue_insert_or_visit) (rvalue_insert_or_cvisit)(rvalue_insert_or_visit) (iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit)) diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 7eaa99bc93..3b3609b2e1 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -1,6 +1,7 @@ // Copyright 2006-2010 Daniel James. // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2026 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) @@ -14,6 +15,7 @@ #include "../helpers/tracker.hpp" #include "../objects/test.hpp" +#include #include namespace constructor_tests { @@ -337,6 +339,211 @@ namespace constructor_tests { // std::vector expected(vec.begin(), vec.begin() + 3); +#if !defined(BOOST_UNORDERED_NO_RANGES) + UNORDERED_SUB_TEST("Range construct 1") + { + test::check_instances check_; + + { + T x(boost::unordered::from_range, list); + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + T x(boost::unordered::from_range, std::vector{vec[0], vec[1], vec[2]}); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST_GT(x.bucket_count(), 0u); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Range construct 2") + { + test::check_instances check_; + + { + T x(boost::unordered::from_range, list, 1000); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 1000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + T x( + boost::unordered::from_range, + std::list{vec[0], vec[1], vec[2]} | std::views::take(3), 1000); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 1000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Range construct 3") + { + { + test::check_instances check_; + + T x(boost::unordered::from_range, list, 10, hf1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + test::check_instances check_; + + T x( + boost::unordered::from_range, std::vector{vec[0], vec[1], vec[2]}, + 10, hf1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Range construct 4") + { + { + test::check_instances check_; + + T x(boost::unordered::from_range, list, 10, hf1, eq1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + test::check_instances check_; + + T x( + boost::unordered::from_range, + std::list{vec[0], vec[1], vec[2]} | std::views::take(3), + 10, hf1, eq1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Range construct 5") + { + { + test::check_instances check_; + + T x(boost::unordered::from_range, list, 10, hf1, eq1, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + test::check_instances check_; + + T x( + boost::unordered::from_range, + std::vector{vec[0], vec[1], vec[2]}, 10, hf1, eq1, al1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Range construct 6") + { + { + test::check_instances check_; + + T x(boost::unordered::from_range, list, 10, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + test::check_instances check_; + + T x( + boost::unordered::from_range, + std::list{vec[0], vec[1], vec[2]} | std::views::take(3), 10, al1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Range construct 7") + { + { + test::check_instances check_; + + T x(boost::unordered::from_range, list, 10, hf1, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + test::check_instances check_; + + T x( + boost::unordered::from_range, + std::vector{vec[0], vec[1], vec[2]}, 10, hf1, al1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Range construct 8") + { + test::check_instances check_; + + { + T x(boost::unordered::from_range, list, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + T x( + boost::unordered::from_range, + std::list{vec[0], vec[1], vec[2]} | std::views::take(3), al1); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } + } +#endif + UNORDERED_SUB_TEST("Initializer list construct 1") { test::check_instances check_; diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index c56d37c105..a0a137ccdc 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -1,6 +1,7 @@ // Copyright 2006-2010 Daniel James. // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2026 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) @@ -405,6 +406,27 @@ namespace insert_tests { test::check_equivalent_keys(x); } } + +#if !defined(BOOST_UNORDERED_NO_RANGES) + UNORDERED_SUB_TEST("insert_range tests") + { + test::check_instances check_; + + X x; + + test::random_values v(1000, generator); + x.insert_range(v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + + x.clear(); + x.insert_range(v | std::views::take(v.size())); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } +#endif } template From cc9bd48c8f279aa9bb3211db8ad11a2989b4ff9f Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 6 Jun 2026 15:56:48 +0200 Subject: [PATCH 02/11] worked around GCC 10-11 bug when piping a temporary range --- test/cfoa/constructor_tests.cpp | 4 ++-- test/unordered/constructor_tests.cpp | 20 +++++++------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/test/cfoa/constructor_tests.cpp b/test/cfoa/constructor_tests.cpp index 2cf39f0bcf..64075f2a08 100644 --- a/test/cfoa/constructor_tests.cpp +++ b/test/cfoa/constructor_tests.cpp @@ -714,8 +714,8 @@ namespace { using allocator_type = typename X::allocator_type; auto init_list = p.second; - auto sentineled_rg = - std::list{init_list} | std::views::take(init_list.size()); + auto init_std_list = std::list{init_list}; + auto sentineled_rg = init_std_list | std::views::take(init_list.size()); { raii::reset_counts(); diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 3b3609b2e1..16b91c3175 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -340,6 +340,9 @@ namespace constructor_tests { std::vector expected(vec.begin(), vec.begin() + 3); #if !defined(BOOST_UNORDERED_NO_RANGES) + auto std_list = std::list{vec[0], vec[1], vec[2]}; + auto sentineled_rg = std_list | std::views::take(3); + UNORDERED_SUB_TEST("Range construct 1") { test::check_instances check_; @@ -377,9 +380,7 @@ namespace constructor_tests { } { - T x( - boost::unordered::from_range, - std::list{vec[0], vec[1], vec[2]} | std::views::take(3), 1000); + T x(boost::unordered::from_range, sentineled_rg, 1000); BOOST_TEST_NOT(x.empty()); BOOST_TEST(x.bucket_count() >= 1000); BOOST_TEST(test::equivalent(x.hash_function(), hf)); @@ -433,10 +434,7 @@ namespace constructor_tests { { test::check_instances check_; - T x( - boost::unordered::from_range, - std::list{vec[0], vec[1], vec[2]} | std::views::take(3), - 10, hf1, eq1); + T x(boost::unordered::from_range, sentineled_rg, 10, hf1, eq1); BOOST_TEST_NOT(x.empty()); BOOST_TEST(x.bucket_count() >= 10); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); @@ -488,9 +486,7 @@ namespace constructor_tests { { test::check_instances check_; - T x( - boost::unordered::from_range, - std::list{vec[0], vec[1], vec[2]} | std::views::take(3), 10, al1); + T x(boost::unordered::from_range, sentineled_rg, 10, al1); BOOST_TEST_NOT(x.empty()); BOOST_TEST(x.bucket_count() >= 10); BOOST_TEST(test::equivalent(x.get_allocator(), al1)); @@ -535,9 +531,7 @@ namespace constructor_tests { } { - T x( - boost::unordered::from_range, - std::list{vec[0], vec[1], vec[2]} | std::views::take(3), al1); + T x(boost::unordered::from_range, sentineled_rg, al1); BOOST_TEST(test::equivalent(x.get_allocator(), al1)); test::check_container(x, expected); } From a6c36a3c5e22f6465841da7d6b8234e29663378f Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 6 Jun 2026 19:22:42 +0200 Subject: [PATCH 03/11] added insert_range_(or|and)_[c]visit --- .../boost/unordered/concurrent_flat_map.hpp | 66 ++++++ .../boost/unordered/concurrent_flat_set.hpp | 66 ++++++ .../boost/unordered/concurrent_node_map.hpp | 66 ++++++ .../boost/unordered/concurrent_node_set.hpp | 66 ++++++ test/cfoa/insert_tests.cpp | 189 +++++++++++++++++- 5 files changed, 451 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index dcee99130c..002ac6666d 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -551,6 +551,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_visit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_visit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_visit(std::initializer_list ilist, F f) { @@ -584,6 +599,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_cvisit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_cvisit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_cvisit(std::initializer_list ilist, F f) { @@ -621,6 +651,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_visit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_visit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_visit( std::initializer_list ilist, F1 f1, F2 f2) @@ -661,6 +709,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_cvisit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_cvisit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_cvisit( std::initializer_list ilist, F1 f1, F2 f2) diff --git a/include/boost/unordered/concurrent_flat_set.hpp b/include/boost/unordered/concurrent_flat_set.hpp index 2c23c13740..3499d43789 100644 --- a/include/boost/unordered/concurrent_flat_set.hpp +++ b/include/boost/unordered/concurrent_flat_set.hpp @@ -542,6 +542,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_visit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_visit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_visit(std::initializer_list ilist, F f) { @@ -584,6 +599,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_cvisit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_cvisit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_cvisit(std::initializer_list ilist, F f) { @@ -632,6 +662,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_visit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_visit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_visit(std::initializer_list ilist, F1 f1, F2 f2) { @@ -682,6 +730,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_cvisit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_cvisit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_cvisit( std::initializer_list ilist, F1 f1, F2 f2) diff --git a/include/boost/unordered/concurrent_node_map.hpp b/include/boost/unordered/concurrent_node_map.hpp index 08ace1a888..1cfbf5dbdf 100644 --- a/include/boost/unordered/concurrent_node_map.hpp +++ b/include/boost/unordered/concurrent_node_map.hpp @@ -578,6 +578,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_visit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_visit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_visit(std::initializer_list ilist, F f) { @@ -632,6 +647,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_cvisit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_cvisit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_cvisit(std::initializer_list ilist, F f) { @@ -690,6 +720,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_visit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_visit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_visit( std::initializer_list ilist, F1 f1, F2 f2) @@ -752,6 +800,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_cvisit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_cvisit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_cvisit( std::initializer_list ilist, F1 f1, F2 f2) diff --git a/include/boost/unordered/concurrent_node_set.hpp b/include/boost/unordered/concurrent_node_set.hpp index b5c7582765..fbfa4d1d91 100644 --- a/include/boost/unordered/concurrent_node_set.hpp +++ b/include/boost/unordered/concurrent_node_set.hpp @@ -569,6 +569,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_visit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_visit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_visit(std::initializer_list ilist, F f) { @@ -632,6 +647,21 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template R, class F> + size_type insert_range_or_cvisit(R&& rg, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_or_cvisit(*first, f); + } + return count_elements; + } +#endif + template size_type insert_or_cvisit(std::initializer_list ilist, F f) { @@ -701,6 +731,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_visit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_visit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_visit( std::initializer_list ilist, F1 f1, F2 f2) @@ -774,6 +822,24 @@ namespace boost { return count_elements; } +#if !defined(BOOST_UNORDERED_NO_RANGES) + template< + detail::container_compatible_range R, class F1, class F2 + > + size_type insert_range_and_cvisit(R&& rg, F1 f1, F2 f2) + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1) + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2) + size_type count_elements = 0; + auto first = std::ranges::begin(rg); + auto last = std::ranges::end(rg); + for (; first != last; ++first, ++count_elements) { + table_.emplace_and_cvisit(*first, f1, f2); + } + return count_elements; + } +#endif + template size_type insert_and_cvisit( std::initializer_list ilist, F1 f1, F2 f2) diff --git a/test/cfoa/insert_tests.cpp b/test/cfoa/insert_tests.cpp index 4569d2838e..784ce002a5 100644 --- a/test/cfoa/insert_tests.cpp +++ b/test/cfoa/insert_tests.cpp @@ -831,6 +831,41 @@ namespace { } } iterator_range_insert_or_cvisit; +#if !defined(BOOST_UNORDERED_NO_RANGES) + struct insert_range_or_cvisit_type + { + template void operator()(std::vector& values, X& x) + { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + std::vector values2; + values2.reserve(values.size()); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); + } + + std::atomic num_invokes{0}; + thread_runner( + values2, [&x, &num_invokes](boost::span s) { + BOOST_TEST_EQ(x.insert_range_or_cvisit(s, + [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }), + s.size()); + }); + + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values2.size()); + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_GT(raii::move_constructor, 0u); + } + } insert_range_or_cvisit; +#endif + struct iterator_range_insert_and_cvisit_type { template void operator()(std::vector& values, X& x) @@ -885,6 +920,55 @@ namespace { } } iterator_range_insert_and_cvisit; +#if !defined(BOOST_UNORDERED_NO_RANGES) + struct insert_range_and_cvisit_type + { + template void operator()(std::vector& values, X& x) + { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + + std::vector values2; + values2.reserve(values.size()); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); + } + + std::atomic num_inserts{0}; + std::atomic num_invokes{0}; + thread_runner(values2, + [&x, &num_inserts, &num_invokes](boost::span s) { + BOOST_TEST_EQ(x.insert_range_and_cvisit( + s, + [&num_inserts](arg_type& v) { + (void)v; + ++num_inserts; + }, + [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }), + s.size()); + }); + + BOOST_TEST_EQ(num_inserts, x.size()); + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values2.size()); + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_GT(raii::move_constructor, 0u); + } + } insert_range_and_cvisit; +#endif + struct iterator_range_insert_or_visit_type { template void operator()(std::vector& values, X& x) @@ -932,6 +1016,48 @@ namespace { } } iterator_range_insert_or_visit; +#if !defined(BOOST_UNORDERED_NO_RANGES) + struct insert_range_or_visit_type + { + template void operator()(std::vector& values, X& x) + { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + + std::vector values2; + values2.reserve(values.size()); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); + } + + std::atomic num_invokes{0}; + thread_runner( + values2, [&x, &num_invokes](boost::span s) { + BOOST_TEST_EQ(x.insert_range_or_visit(s, + [&num_invokes](arg_type& v) { + (void)v; + ++num_invokes; + }), + s.size()); + }); + + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values2.size()); + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_GT(raii::move_constructor, 0u); + } + } insert_range_or_visit; +#endif + struct iterator_range_insert_and_visit_type { template void operator()(std::vector& values, X& x) @@ -986,6 +1112,55 @@ namespace { } } iterator_range_insert_and_visit; +#if !defined(BOOT_UNORDERED_NO_RANGES) + struct insert_range_and_visit_type + { + template void operator()(std::vector& values, X& x) + { + static constexpr auto value_type_cardinality = + value_cardinality::value; + + // concurrent_flat_set visit is always const access + using arg_type = typename std::conditional< + std::is_same::value, + typename X::value_type const, + typename X::value_type + >::type; + + std::vector values2; + values2.reserve(values.size()); + for (auto const& v : values) { + values2.push_back(raii_convertible(v)); + } + + std::atomic num_inserts{0}; + std::atomic num_invokes{0}; + thread_runner(values2, + [&x, &num_inserts, &num_invokes](boost::span s) { + BOOST_TEST_EQ(x.insert_range_and_visit( + s, + [&num_inserts](arg_type& v) { + (void)v; + ++num_inserts; + }, + [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }), + s.size()); + }); + + BOOST_TEST_EQ(num_inserts, x.size()); + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ( + raii::default_constructor, value_type_cardinality * values2.size()); + BOOST_TEST_EQ(raii::copy_constructor, 0u); + BOOST_TEST_GT(raii::move_constructor, 0u); + } + } insert_range_and_visit; +#endif + struct non_copyable_function { non_copyable_function() = default; @@ -1300,8 +1475,16 @@ UNORDERED_TEST( #if !defined(BOOST_UNORDERED_NO_RANGES) #define RANGE_INSERTER (range_inserter) +#define INSERT_RANGE_OR_CVISIT (insert_range_or_cvisit) +#define INSERT_RANGE_OR_VISIT (insert_range_or_visit) +#define INSERT_RANGE_AND_CVISIT (insert_range_and_cvisit) +#define INSERT_RANGE_AND_VISIT (insert_range_and_visit) #else #define RANGE_INSERTER +#define INSERT_RANGE_OR_CVISIT +#define INSERT_RANGE_OR_VISIT +#define INSERT_RANGE_AND_CVISIT +#define INSERT_RANGE_AND_VISIT #endif UNORDERED_TEST( @@ -1313,7 +1496,8 @@ UNORDERED_TEST( RANGE_INSERTER (norehash_lvalue_inserter)(norehash_rvalue_inserter) (lvalue_insert_or_cvisit)(lvalue_insert_or_visit) (rvalue_insert_or_cvisit)(rvalue_insert_or_visit) - (iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit)) + (iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit) + INSERT_RANGE_OR_CVISIT INSERT_RANGE_OR_VISIT ) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( @@ -1323,7 +1507,8 @@ UNORDERED_TEST( ((value_type_generator_factory)(init_type_generator_factory)) ((lvalue_insert_and_cvisit)(lvalue_insert_and_visit) (rvalue_insert_and_cvisit)(rvalue_insert_and_visit) - (iterator_range_insert_and_cvisit)(iterator_range_insert_and_visit)) + (iterator_range_insert_and_cvisit)(iterator_range_insert_and_visit) + INSERT_RANGE_AND_CVISIT INSERT_RANGE_AND_VISIT ) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST( From 41c364c29c4503da6988da20a3a85db3f1ca5ccd Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 6 Jun 2026 21:48:21 +0200 Subject: [PATCH 04/11] typo --- test/cfoa/insert_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cfoa/insert_tests.cpp b/test/cfoa/insert_tests.cpp index 784ce002a5..dfe0673407 100644 --- a/test/cfoa/insert_tests.cpp +++ b/test/cfoa/insert_tests.cpp @@ -1112,7 +1112,7 @@ namespace { } } iterator_range_insert_and_visit; -#if !defined(BOOT_UNORDERED_NO_RANGES) +#if !defined(BOOST_UNORDERED_NO_RANGES) struct insert_range_and_visit_type { template void operator()(std::vector& values, X& x) From ff2461835773fca0a3136ad7830d4684f634e4eb Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 7 Jun 2026 09:58:33 +0200 Subject: [PATCH 05/11] tried to avoid MinGW32 compilation OOM in cfoa/insert_tests.cpp --- test/Jamfile.v2 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 31265e4587..30e344d484 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -379,7 +379,8 @@ run cfoa/insert_tests.cpp : $(CPP11) multi msvc:/bigobj gcc:on - gcc:space + gcc:off + gcc:off clang:on clang:space : cfoa_insert_tests ; From 4a0bc242d7119b191cd459171d89ef858d133d11 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 7 Jun 2026 11:22:23 +0200 Subject: [PATCH 06/11] s/off/-g0 --- test/Jamfile.v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 30e344d484..7ccaa1d01a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -380,7 +380,7 @@ run cfoa/insert_tests.cpp msvc:/bigobj gcc:on gcc:off - gcc:off + gcc:-g0 clang:on clang:space : cfoa_insert_tests ; From 127e2b9f43c2e8d466cf1822f358a23731af3bc8 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 7 Jun 2026 12:21:49 +0200 Subject: [PATCH 07/11] tested CTAD deduction guides --- test/unordered/deduction_tests.cpp | 198 ++++++++++++++++++++++++++++- 1 file changed, 195 insertions(+), 3 deletions(-) diff --git a/test/unordered/deduction_tests.cpp b/test/unordered/deduction_tests.cpp index 7d3996e33e..b44c9e146a 100644 --- a/test/unordered/deduction_tests.cpp +++ b/test/unordered/deduction_tests.cpp @@ -1,19 +1,26 @@ // Copyright 2017-2018 Daniel James. +// Copyright 2026 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) -#include #include -#include #include -#include #if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES +#include +#include +#include #include #include +#include +#include #include +#include +#include +#include +#include struct hash_equals { @@ -87,6 +94,44 @@ template