[WIP] libstdc++: Add allocate_at_least (P0401) [PR118030]
Jonathan Wakely
jwakely@redhat.com
Fri Mar 6 13:43:18 GMT 2026
On Fri, 6 Mar 2026 at 13:41, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Fri, 6 Mar 2026 at 13:37, Jonathan Wakely <jwakely@redhat.com> wrote:
> >
> > On Fri, 6 Mar 2026 at 12:44, Nathan Myers <ncm@cantrip.org> wrote:
> > >
> > > [Work-in-progress, posting to verify direction.]
> > >
> > > Implement P0401R6, "Providing size feedback in the Allocator
> > > interface".
> > >
> > > libstdc++-v3/ChangeLog:
> > > PR libstdc++/118030
> > > * include/bits/alloc_traits.h (allocate_at_least, allocation_result,
> > > __at_least_result, __complete_type_allocator): Define.
> > > * include/bits/allocator.h (allocate_at_least): Define.
> > > * include/std/memory (__glibcxx_want_allocate_at_least): Define.
> > > * include/bits/version.def (allocate_at_least): Add.
> > > * include/bits/version.h: Regenerate.
> > > * testsuite/20_util/allocator/allocate_at_least.cc: New test.
> > > * testsuite/20_util/allocator/allocate_at_least_neg.cc: New test.
> > > ---
> > > libstdc++-v3/include/bits/alloc_traits.h | 27 +++++++++++++++
> > > libstdc++-v3/include/bits/allocator.h | 6 ++++
> > > libstdc++-v3/include/bits/version.def | 8 +++++
> > > libstdc++-v3/include/bits/version.h | 10 ++++++
> > > libstdc++-v3/include/std/memory | 1 +
> > > .../20_util/allocator/allocate_at_least.cc | 34 +++++++++++++++++++
> > > .../allocator/allocate_at_least_neg.cc | 22 ++++++++++++
> > > 7 files changed, 108 insertions(+)
> > > create mode 100644 libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
> > > create mode 100644 libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc
> > >
> > > diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
> > > index c34143a3526..943bf03cc5a 100644
> > > --- a/libstdc++-v3/include/bits/alloc_traits.h
> > > +++ b/libstdc++-v3/include/bits/alloc_traits.h
> > > @@ -548,6 +548,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > };
> > > #pragma GCC diagnostic pop
> > >
> > > +#ifdef __glibcxx_lib_allocate_at_least // C++23
> > > + template <typename _Pointer>
> > > + struct allocation_result
> > > + {
> > > + _Pointer ptr;
> > > + size_t count;
> >
> > The working draft has a SizeType parameter too:
> >
> > template<class Pointer, class SizeType = size_t>
> > struct allocation_result {
> > Pointer ptr;
> > SizeType count;
> > };
> >
> >
> > > + };
> > > +
> > > + template <typename _Alloc>
> > > + using __at_least_result =
> > > + allocation_result<typename allocator_traits<_Alloc>::pointer>;
> > > +
> > > + template <typename _Alloc>
> > > + concept __complete_type_allocator =
> > > + requires { sizeof(typename allocator_traits<_Alloc>::value_type); };
> > > +
> > > + template <__complete_type_allocator _Alloc>
> > > + [[nodiscard]] constexpr __at_least_result<_Alloc>
> > > + allocate_at_least(_Alloc& __a, size_t __n)
> > > + {
> > > + if constexpr (requires { __a.allocate_at_least(__n); })
> > > + return __a.allocate_at_least(__n);
> > > + else
> > > + return { __a.allocate(__n), __n };
> > > + }
>
> And this was moved to be a member function of allocator_traits by some
> LWG issue.
Actually it was part of
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2652r2.html
not an LWG issue.
>
> Make sure you're implementing the working draft, not just the original
> proposal from 2021.
>
>
> > > +#endif
> > > +
> > > #if _GLIBCXX_HOSTED
> > > /**
> > > * @brief Partial specialization for `std::allocator`
> > > diff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/include/bits/allocator.h
> > > index 9f9526bd5b0..9c246b77d93 100644
> > > --- a/libstdc++-v3/include/bits/allocator.h
> > > +++ b/libstdc++-v3/include/bits/allocator.h
> > > @@ -203,6 +203,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > return __allocator_base<_Tp>::allocate(__n, 0);
> > > }
> > >
> > > +#ifdef __glibcxx_allocate_at_least // C++23
> > > + [[nodiscard]] constexpr allocation_result<_Tp*>
> > > + allocate_at_least(size_t __n)
> > > + { return { allocate(__n), __n }; }
> > > +#endif
> > > +
> > > [[__gnu__::__always_inline__]]
> > > constexpr void
> > > deallocate(_Tp* __p, size_t __n)
> > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> > > index dbe95b8b79f..ecd3315d8a3 100644
> > > --- a/libstdc++-v3/include/bits/version.def
> > > +++ b/libstdc++-v3/include/bits/version.def
> > > @@ -88,6 +88,14 @@ ftms = {
> > > };
> > > };
> > >
> > > +ftms = {
> > > + name = allocate_at_least;
> > > + values = {
> > > + v = 202106;
> > > + cxxmin = 23;
> > > + };
> > > +};
> > > +
> > > ftms = {
> > > name = is_null_pointer;
> > > values = {
> > > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> > > index eee99847490..f0d9a0010ab 100644
> > > --- a/libstdc++-v3/include/bits/version.h
> > > +++ b/libstdc++-v3/include/bits/version.h
> > > @@ -80,6 +80,16 @@
> > > #endif /* !defined(__cpp_lib_allocator_traits_is_always_equal) */
> > > #undef __glibcxx_want_allocator_traits_is_always_equal
> > >
> > > +#if !defined(__cpp_lib_allocate_at_least)
> > > +# if (__cplusplus >= 202100L)
> > > +# define __glibcxx_allocate_at_least 202106L
> > > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_allocate_at_least)
> > > +# define __cpp_lib_allocate_at_least 202106L
> > > +# endif
> > > +# endif
> > > +#endif /* !defined(__cpp_lib_allocate_at_least) */
> > > +#undef __glibcxx_want_allocate_at_least
> > > +
> > > #if !defined(__cpp_lib_is_null_pointer)
> > > # if (__cplusplus >= 201103L)
> > > # define __glibcxx_is_null_pointer 201309L
> > > diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
> > > index c9c9224e599..cbe9f5ad200 100644
> > > --- a/libstdc++-v3/include/std/memory
> > > +++ b/libstdc++-v3/include/std/memory
> > > @@ -125,6 +125,7 @@
> > > #define __glibcxx_want_to_address
> > > #define __glibcxx_want_transparent_operators
> > > #define __glibcxx_want_smart_ptr_owner_equality
> > > +#define __glibcxx_want_allocate_at_least
> > > #include <bits/version.h>
> > >
> > > #if __cplusplus >= 201103L && __cplusplus <= 202002L && _GLIBCXX_HOSTED
> > > diff --git a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
> > > new file mode 100644
> > > index 00000000000..317e7518c35
> > > --- /dev/null
> > > +++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
> > > @@ -0,0 +1,34 @@
> > > +// { dg-do run { target c++23 } }
> > > +
> > > +#include <memory>
> > > +#include <testsuite_hooks.h>
> > > +
> > > +template <typename T>
> > > + struct A : std::allocator<T>
> > > + {
> > > + using Base = std::allocator<T>;
> > > +
> > > + std::allocation_result<T*>
> > > + allocate_at_least(std::size_t n)
> > > + { return { Base::allocate(2*n), 2*n }; }
> > > + };
> > > +
> > > +int main()
> > > +{
> > > + std::allocator<char> native;
> > > + auto a1 = native.allocate_at_least(100);
> > > + static_assert(std::is_same_v<decltype(a1), std::allocation_result<char*>>);
> > > +
> > > + auto a2 = std::allocate_at_least<char*>(native, 100);
> > > + static_assert(std::is_same_v<decltype(a2), std::allocation_result<char*>>);
> > > +
> > > + A<char> custom;
> > > + auto a3 = std::allocate_at_least<char*>(custom, 100);
> > > + static_assert(std::is_same_v<decltype(a3), std::allocation_result<char*>>);
> > > + VERIFY(a1.count == 100);
> > > + VERIFY(a2.count == 100);
> > > + VERIFY(a3.count == 200);
> > > + custom.deallocate(a3.ptr, a3.count);
> > > + native.deallocate(a2.ptr, a2.count);
> > > + native.deallocate(a1.ptr, a1.count);
> > > +}
> > > diff --git a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc
> > > new file mode 100644
> > > index 00000000000..a377c758b47
> > > --- /dev/null
> > > +++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc
> > > @@ -0,0 +1,22 @@
> > > +// { dg-do compile { target c++23 } }
> > > +
> > > +#include <memory>
> > > +#include <testsuite_hooks.h>
> > > +
> > > +template <typename T>
> > > + struct A : std::allocator<T>
> > > + {
> > > + using Base = std::allocator<T>;
> > > +
> > > + std::allocation_result<T*>
> > > + allocate_at_least(std::size_t n)
> > > + { return { Base::allocate(2*n), 2*n }; }
> > > + };
> > > +
> > > +struct incomplete;
> > > +
> > > +int main()
> > > +{
> > > + A<incomplete> custom;
> > > + auto a3 = std::allocate_at_least<char*>(custom, 100); // { dg-error "" }
> > > +}
> > > --
> > > 2.52.0
> > >
More information about the Libstdc++
mailing list