From 12f62ca98e853d18fd70fb2c32bc8048d6dd2aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Mon, 15 Jun 2026 23:19:42 +0200 Subject: [PATCH] Unify "hub" with the rest of containers using default "void" allocator parameter (defaults to "new_allocator" which supports overalignment in all std standards). --- include/boost/container/container_fwd.hpp | 4 + include/boost/container/hub.hpp | 112 +++++++++++----------- include/boost/container/new_allocator.hpp | 3 + test/hub_new_extended_alignment_test.cpp | 48 ++-------- test/hub_utility.hpp | 5 +- 5 files changed, 78 insertions(+), 94 deletions(-) diff --git a/include/boost/container/container_fwd.hpp b/include/boost/container/container_fwd.hpp index 06e11f6..22db883 100644 --- a/include/boost/container/container_fwd.hpp +++ b/include/boost/container/container_fwd.hpp @@ -147,6 +147,10 @@ template class slist; +template +class hub; + template ,class Allocator = void diff --git a/include/boost/container/hub.hpp b/include/boost/container/hub.hpp index 710df8a..27ec344 100644 --- a/include/boost/container/hub.hpp +++ b/include/boost/container/hub.hpp @@ -12,6 +12,9 @@ #include #include + +// container +#include #include #include //from_range_t / from_range @@ -20,15 +23,19 @@ #include #include #include +#include //default allocator when Allocator = void #include +#include //dtl::addressof (avoids ) +#include //portable (over)aligned nothrow alloc +#include //forward declares std::allocator #include #include +#include //movelib::unique_ptr (avoids ) #include #include #include #include #include -#include #include #include #include @@ -50,9 +57,9 @@ #endif #if !defined(BOOST_CONTAINER_HUB_NO_RANGES) - +// (std::input_iterator, std::iter_value_t/iter_reference_t) is already +//included unconditionally below; only (std::convertible_to) is extra. #include -#include #endif /* Software prefetch hint accepting a (possibly fancy) pointer: convert to a raw @@ -82,7 +89,8 @@ namespace container { #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED -template> +//Default argument for Allocator is provided by container_fwd.hpp +template class hub; template @@ -709,41 +717,17 @@ struct buffer T* data = nullptr; private: -#if defined(__cpp_aligned_new) && __cpp_aligned_new >= 201606L - using aligned_new_required = std::integral_constant< - bool, (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)>; - + //Portable, nothrow, (over)aligned allocation: this is the same primitive + //boost::container::new_allocator relies on for overalignment, so there is no + //need to special-case __cpp_aligned_new here. The nothrow null return lets + //sort() fall back to a leaner algorithm when this (possibly large) scratch + //buffer cannot be allocated. void allocate_data(std::size_t n) { - data = static_cast(allocate(n * sizeof(T), aligned_new_required{})); + data = static_cast(dtl::aligned_allocate(alignof(T), n * sizeof(T))); } - static void* allocate(std::size_t m, std::false_type) - { - return ::operator new[](m, std::nothrow); - } - - static void* allocate(std::size_t m, std::true_type) - { - return ::operator new[](m, std::align_val_t{alignof(T)}, std::nothrow); - } - - void deallocate_data() { deallocate(data, aligned_new_required{}); } - - static void deallocate(void* p, std::false_type) { ::operator delete[](p); } - - static void deallocate(void* p, std::true_type) - { - ::operator delete[](p, std::align_val_t{alignof(T)}); - } -#else - void allocate_data(std::size_t n) - { - data = static_cast(::operator new[](n * sizeof(T), std::nothrow)); - } - - void deallocate_data() { ::operator delete[](data); } -#endif + void deallocate_data() { dtl::aligned_deallocate(data); } }; template @@ -761,7 +745,7 @@ struct nodtor_deleter }; template -using nodtor_unique_ptr = std::unique_ptr>; +using nodtor_unique_ptr = boost::movelib::unique_ptr>; template struct type_identity { using type = T; }; @@ -929,7 +913,7 @@ struct block_typedefs //! \c trim_capacity or on container destruction. New blocks are allocated only //! when every block is full or when the user issues a \c reserve operation. //! -//! \c hub is a model of \c Container, \c ReversibleContainer, +//! \c hub is a model of \c Container, \c ReversibleContainer, //! \c AllocatorAwareContainer and \c SequenceContainer, with the following //! exceptions: operators \c == and \c != are not provided, and positional //! insertion of the form \c insert(position,\ ...) or \c emplace(position,\ ...) @@ -937,15 +921,27 @@ struct block_typedefs //! \c LegacyBidirectionalIterator. //! //! \tparam T The cv-unqualified object type of the elements stored in the hub. -//! \tparam Allocator An allocator whose value type is \c T. +//! \tparam Allocator An allocator whose value type is \c T. If \c void (the +//! default), \c boost::container::new_allocator is used. //! //! Exception safety: Except when explicitly noted, all non-const member //! functions (and free functions taking \c hub by non-const reference) provide //! the basic exception guarantee, whereas all const member functions (and free //! functions taking \c hub by const reference) provide the strong guarantee. -template -class hub: hub_detail::block_typedefs::block_allocator +#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED +template +class hub +#else +template +class hub: hub_detail::block_typedefs< + typename real_allocator::type>::block_allocator +#endif { +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + //A void allocator argument selects boost::container::new_allocator, as in + //the rest of the library; from here on Allocator is the resolved allocator. + typedef typename real_allocator::type Allocator; +#endif static_assert( !std::is_const::value && !std::is_volatile::value && !std::is_function::value && !std::is_reference::value && @@ -2024,7 +2020,7 @@ private: size_type i = 0; container::for_each(*this, [&] (value_type& x) { - p[i] = {std::addressof(x), i}; + p[i] = {dtl::addressof(x), i}; ++i; }); @@ -2160,21 +2156,29 @@ private: #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES) -template< - typename InputIterator, - typename Allocator = std::allocator< - typename std::iterator_traits::value_type> -> -hub(InputIterator, InputIterator, Allocator = Allocator()) - -> hub< - typename std::iterator_traits::value_type, Allocator>; +template +hub(InputIterator, InputIterator) + -> hub::value_type>; + +template +hub(InputIterator, InputIterator, Allocator) + -> hub::value_type, Allocator>; + +//The (initializer_list) case is covered by the implicit guide of the +//initializer_list constructor (the allocator defaults to void); an explicit +//guide is only needed to deduce a user-supplied allocator, since the resolved +//in-class allocator type is a non-deduced context. +template +hub(std::initializer_list, Allocator) + -> hub; #if !defined(BOOST_CONTAINER_HUB_NO_RANGES) -template< - hub_detail::input_range_like R, - typename Allocator = std::allocator > -> -hub(from_range_t, R&&, Allocator = Allocator()) +template +hub(from_range_t, R&&) + -> hub >; + +template +hub(from_range_t, R&&, Allocator) -> hub, Allocator>; #endif #endif diff --git a/include/boost/container/new_allocator.hpp b/include/boost/container/new_allocator.hpp index 615d9b9..3455975 100644 --- a/include/boost/container/new_allocator.hpp +++ b/include/boost/container/new_allocator.hpp @@ -104,6 +104,9 @@ class new_allocator //! This class is a reduced STL-compatible allocator that allocates memory using operator new +//! or compatible calls. This allocator is stateless and can be used with any type. It supports +//! overaligned types, even if the compiler does not support overaligned operator new. +//! It is a drop-in replacement for std::allocator template class new_allocator { diff --git a/test/hub_new_extended_alignment_test.cpp b/test/hub_new_extended_alignment_test.cpp index dfc8ccf..f377111 100644 --- a/test/hub_new_extended_alignment_test.cpp +++ b/test/hub_new_extended_alignment_test.cpp @@ -12,51 +12,23 @@ int main() { return 0; } #else -#if !defined(__cpp_aligned_new) || __cpp_aligned_new < 201606L -#include - -BOOST_PRAGMA_MESSAGE("Test skipped because aligned new is not available.") - -int main() -{ -} -#else -#include #include #include +#include #include -#include -template -struct aligned_new_allocator -{ - using value_type = T; - - aligned_new_allocator() = default; - template - aligned_new_allocator(const aligned_new_allocator&) {} - - T* allocate(std::size_t n) - { - return static_cast( - ::operator new(n * sizeof(T), std::align_val_t{alignof(T)})); - } - - void deallocate(T* p, std::size_t) - { - ::operator delete(p, std::align_val_t{alignof(T)}); - } - - bool operator==(const aligned_new_allocator&) const { return true; } - bool operator!=(const aligned_new_allocator&) const { return false; } -}; +/* boost::container::new_allocator (the default allocator) supports + * overalignment portably, so a plain hub with the default allocator stores + * new-extended-aligned types correctly: no custom allocator nor an + * __cpp_aligned_new guard is required. + */ #if defined(BOOST_MSVC) #pragma warning(push) #pragma warning(disable:4324) /* structure padded due to alignment specifier */ #endif -struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) +struct alignas(4 * alignof(std::max_align_t)) new_extended_aligned_object { new_extended_aligned_object(int n_): n{n_} { check_alignment(); } @@ -84,9 +56,8 @@ new_extended_aligned_object #pragma warning(pop) /* C4324 */ #endif -using new_extended_alignment_hub = boost::container::hub< - new_extended_aligned_object, - aligned_new_allocator>; +using new_extended_alignment_hub = + boost::container::hub; /* The only internal sort function of hub that allocates auxiliary memory * for T is transfer_sort: this function, however, is never called when T has @@ -119,6 +90,5 @@ int main() return boost::report_errors(); } -#endif #endif diff --git a/test/hub_utility.hpp b/test/hub_utility.hpp index 2a078c3..8be4b34 100644 --- a/test/hub_utility.hpp +++ b/test/hub_utility.hpp @@ -53,9 +53,12 @@ template< > struct rebind_value_type, U> { + //Use the container's resolved allocator_type rather than the Allocator + //template argument, which may be void (the default that selects new_allocator). using type = Hub< U, - typename boost::container::allocator_traits:: + typename boost::container::allocator_traits< + typename Hub::allocator_type>:: template portable_rebind_alloc::type>; };