[PATCH v3 3/3] libstdc++: Implement aligned_accessor from mdspan [PR120994]

Luc Grosheintz luc.grosheintz@gmail.com
Wed Jul 23 11:33:07 GMT 2025



On 7/23/25 13:20, Tomasz Kaminski wrote:
> On Wed, Jul 23, 2025 at 11:34 AM Luc Grosheintz <luc.grosheintz@gmail.com>
> wrote:
> 
>> This commit completes the implementation of P2897R7 by implementing and
>> testing the template class aligned_accessor.
>>
>>          PR libstdc++/120994
>>
>> libstdc++-v3/ChangeLog:
>>
>>          * include/bits/version.def (aligned_accessor): Add.
>>          * include/bits/version.h: Regenerate.
>>          * include/std/mdspan (aligned_accessor): New class.
>>          * src/c++23/std.cc.in (aligned_accessor): Add.
>>          * testsuite/23_containers/mdspan/accessors/generic.cc: Add tests
>>          for aligned_accessor.
>>          * testsuite/23_containers/mdspan/accessors/aligned.cc: New test.
>>          * testsuite/23_containers/mdspan/accessors/aligned_neg.cc: New
>> test.
>>          * testsuite/23_containers/mdspan/version.cc: Add test for
>>          __cpp_lib_aligned_accessor.
>>
>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
>> ---
>>   libstdc++-v3/include/bits/version.def         | 10 +++
>>   libstdc++-v3/include/bits/version.h           | 10 +++
>>   libstdc++-v3/include/std/mdspan               | 68 +++++++++++++++++++
>>   libstdc++-v3/src/c++23/std.cc.in              |  9 ++-
>>   .../23_containers/mdspan/accessors/aligned.cc | 43 ++++++++++++
>>   .../mdspan/accessors/aligned_neg.cc           | 33 +++++++++
>>   .../accessors/debug/aligned_access_neg.cc     | 23 +++++++
>>   .../accessors/debug/aligned_offset_neg.cc     | 23 +++++++
>>   .../23_containers/mdspan/accessors/generic.cc | 23 +++++++
>>   .../testsuite/23_containers/mdspan/version.cc |  8 +++
>>   10 files changed, 247 insertions(+), 3 deletions(-)
>>   create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc
>>   create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc
>>   create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc
>>   create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc
>>
>> diff --git a/libstdc++-v3/include/bits/version.def
>> b/libstdc++-v3/include/bits/version.def
>> index 56ad9ee9d4b..9d5122fed17 100644
>> --- a/libstdc++-v3/include/bits/version.def
>> +++ b/libstdc++-v3/include/bits/version.def
>> @@ -1025,6 +1025,16 @@ ftms = {
>>     };
>>   };
>>
>> +ftms = {
>> +  name = aligned_accessor;
>> +  values = {
>> +    v = 202411;
>> +    cxxmin = 26;
>> +    extra_cond = "__glibcxx_assume_aligned "
>> +    "&& __glibcxx_is_sufficiently_aligned";
>> +  };
>> +};
>> +
>>   ftms = {
>>     name = ssize;
>>     values = {
>> diff --git a/libstdc++-v3/include/bits/version.h
>> b/libstdc++-v3/include/bits/version.h
>> index 51805d292f0..6feeaa4d15a 100644
>> --- a/libstdc++-v3/include/bits/version.h
>> +++ b/libstdc++-v3/include/bits/version.h
>> @@ -1149,6 +1149,16 @@
>>   #endif /* !defined(__cpp_lib_mdspan) && defined(__glibcxx_want_mdspan) */
>>   #undef __glibcxx_want_mdspan
>>
>> +#if !defined(__cpp_lib_aligned_accessor)
>> +# if (__cplusplus >  202302L) && (__glibcxx_assume_aligned &&
>> __glibcxx_is_sufficiently_aligned)
>> +#  define __glibcxx_aligned_accessor 202411L
>> +#  if defined(__glibcxx_want_all) ||
>> defined(__glibcxx_want_aligned_accessor)
>> +#   define __cpp_lib_aligned_accessor 202411L
>> +#  endif
>> +# endif
>> +#endif /* !defined(__cpp_lib_aligned_accessor) &&
>> defined(__glibcxx_want_aligned_accessor) */
>> +#undef __glibcxx_want_aligned_accessor
>> +
>>   #if !defined(__cpp_lib_ssize)
>>   # if (__cplusplus >= 202002L)
>>   #  define __glibcxx_ssize 201902L
>> diff --git a/libstdc++-v3/include/std/mdspan
>> b/libstdc++-v3/include/std/mdspan
>> index 7d17f32a836..4b2221bd070 100644
>> --- a/libstdc++-v3/include/std/mdspan
>> +++ b/libstdc++-v3/include/std/mdspan
>> @@ -39,7 +39,12 @@
>>   #include <limits>
>>   #include <utility>
>>
>> +#if __cplusplus > 202302L
>> +#include <bits/align.h>
>> +#endif
>> +
>>   #define __glibcxx_want_mdspan
>> +#define __glibcxx_want_aligned_accessor
>>   #include <bits/version.h>
>>
>>   #ifdef __glibcxx_mdspan
>> @@ -1062,6 +1067,69 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>         { return __p + __i; }
>>       };
>>
>> +#ifdef __glibcxx_aligned_accessor
>> +  template<typename _ElementType, size_t _ByteAlignment>
>> +    struct aligned_accessor
>> +    {
>> +      static_assert(has_single_bit(_ByteAlignment),
>> +       "ByteAlignment must be a power of two");
>> +      static_assert(_ByteAlignment >= alignof(_ElementType),
>> +       "ByteAlignment is too small for ElementType");
>> +
>> +      using offset_policy = default_accessor<_ElementType>;
>> +      using element_type = _ElementType;
>> +      using reference = element_type&;
>> +      using data_handle_type = element_type*;
>> +
>> +      static constexpr size_t byte_alignment = _ByteAlignment;
>> +
>> +      constexpr
>> +      aligned_accessor() noexcept = default;
>> +
>> +      template<typename _OElementType, size_t _OByteAlignment>
>> +       requires (is_convertible_v<_OElementType(*)[], element_type(*)[]>
>> +           && _OByteAlignment >= byte_alignment)
>> +       constexpr
>> +       aligned_accessor(aligned_accessor<_OElementType, _OByteAlignment>)
>> +       noexcept
>> +       { }
>> +
>> +      template<typename _OElementType>
>> +       requires is_convertible_v<_OElementType(*)[], element_type(*)[]>
>> +       constexpr explicit
>> +       aligned_accessor(default_accessor<_OElementType>) noexcept
>> +       { }
>> +
>> +      template<typename _OElementType>
>> +       requires is_convertible_v<_OElementType(*)[], element_type(*)[]>
>> +       constexpr
>> +       operator default_accessor<_OElementType>() const noexcept
>> +       { return {}; }
>> +
>> +      constexpr reference
>> +      access(data_handle_type __p, size_t __i) const noexcept
>> +      {
>> +       if !consteval
>> +         {
>> +           _GLIBCXX_DEBUG_ASSERT(
>> +             std::is_sufficiently_aligned<_ByteAlignment>(__p));
>> +         }
>>
> I think we discussed this before, and agreed on removing this check
> here and in the next function, let me know if I missed some part of the
> conversation.
> I have checked that the test passes with them removed.
> Sorry for missing that on v2.

Sorry, that's strange. This was one of the important parts of the
v1 review. I'll create v4 with only two patches 1/3 and 3/3.

> 
>> +       return std::assume_aligned<byte_alignment>(__p)[__i];
>> +      }
>> +
>> +      constexpr typename offset_policy::data_handle_type
>> +      offset(data_handle_type __p, size_t __i) const noexcept
>> +      {
>> +       if !consteval
>> +         {
>> +           _GLIBCXX_DEBUG_ASSERT(
>> +             std::is_sufficiently_aligned<_ByteAlignment>(__p));
>> +         }
>> +       return std::assume_aligned<byte_alignment>(__p) + __i;
>> +      }
>> +    };
>> +#endif
>> +
>>     namespace __mdspan
>>     {
>>       template<typename _Extents, typename _IndexType, size_t _Nm>
>> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
>> std.cc.in
>> index 24411476ba0..40d2fa03c5a 100644
>> --- a/libstdc++-v3/src/c++23/std.cc.in
>> +++ b/libstdc++-v3/src/c++23/std.cc.in
>> @@ -1858,10 +1858,13 @@ export namespace std
>>     using std::layout_right;
>>     using std::layout_stride;
>>     using std::default_accessor;
>> +#if __glibcxx_aligned_accessor
>> +  using std::aligned_accessor;
>> +#endif
>>     using std::mdspan;
>> -  // FIXME layout_left_padded, layout_right_padded, aligned_accessor,
>> -  // strided_slice, submdspan_mapping_result, full_extent_t, full_extent,
>> -  // submdspan_extents, mdsubspan
>> +  // FIXME layout_left_padded, layout_right_padded, strided_slice,
>> +  // submdspan_mapping_result, full_extent_t, full_extent,
>> submdspan_extents,
>> +  // mdsubspan
>>   }
>>   #endif
>>
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc
>> new file mode 100644
>> index 00000000000..f84092f6d89
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned.cc
>> @@ -0,0 +1,43 @@
>> +// { dg-do compile { target c++26 } }
>> +#include <mdspan>
>> +
>> +#include <testsuite_hooks.h>
>> +
>> +constexpr bool
>> +test_from_other()
>> +{
>> +  std::aligned_accessor<double, 4*sizeof(double)> a4;
>> +  [[maybe_unused]] std::aligned_accessor<double, 2*sizeof(double)> a2(a4);
>> +  static_assert(std::is_nothrow_convertible_v<std::aligned_accessor<char,
>> 4>,
>> +                                             std::aligned_accessor<char,
>> 2>>);
>> +  static_assert(!std::is_constructible_v<std::aligned_accessor<char, 4>,
>> +                                        std::aligned_accessor<char, 2>>);
>> +  return true;
>> +}
>> +static_assert(test_from_other());
>> +
>> +constexpr bool
>> +test_from_default()
>> +{
>> +  std::default_accessor<double> ad;
>> +  [[maybe_unused]] std::aligned_accessor<double, 4*sizeof(double)> a4(ad);
>> +  static_assert(!std::is_convertible_v<std::default_accessor<char>,
>> +                                      std::aligned_accessor<char, 1>>);
>> +  static_assert(!std::is_convertible_v<std::default_accessor<char>,
>> +                                      std::aligned_accessor<char, 2>>);
>> +  static_assert(std::is_nothrow_constructible_v<
>> +      std::aligned_accessor<char, 4>, std::default_accessor<char>>);
>> +  return true;
>> +}
>> +static_assert(test_from_default());
>> +
>> +constexpr bool
>> +test_to_default()
>> +{
>> +  std::aligned_accessor<double, 4*sizeof(double)> a4;
>> +  [[maybe_unused]] std::default_accessor<double> ad = a4;
>> +  static_assert(std::is_nothrow_convertible_v<std::aligned_accessor<char,
>> 2>,
>> +
>>   std::default_accessor<char>>);
>> +  return true;
>> +}
>> +static_assert(test_to_default());
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc
>> new file mode 100644
>> index 00000000000..46d80bf4083
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc
>> @@ -0,0 +1,33 @@
>> +// { dg-do compile { target c++26 } }
>> +#include<mdspan>
>> +
>> +#include <cstdint>
>> +
>> +std::aligned_accessor<uint32_t, 0> a;          // { dg-error "required
>> from here" }
>> +std::aligned_accessor<uint32_t, 7> b;          // { dg-error "required
>> from here" }
>> +std::aligned_accessor<uint32_t, size_t(-1)> c; // { dg-error "required
>> from here" }
>> +
>> +std::aligned_accessor<uint32_t, 2> d;          // { dg-error "required
>> from here" }
>> +
>> +std::aligned_accessor<int[2], 32> e;           // { dg-error "required
>> from here" }
>> +
>> +class Abstract
>> +{
>> +  virtual void
>> +  foo() const = 0;
>> +};
>> +
>> +class Derived : public Abstract
>> +{
>> +  void
>> +  foo() const override
>> +  { }
>> +};
>> +
>> +std::aligned_accessor<Derived, alignof(int)> f_ok;
>> +std::aligned_accessor<Abstract, alignof(int)> f_err; // { dg-error
>> "required from here" }
>> +
>> +// { dg-prune-output "ByteAlignment must be a power of two" }
>> +// { dg-prune-output "ByteAlignment is too small for ElementType" }
>> +// { dg-prune-output "ElementType must not be an array type" }
>> +// { dg-prune-output "ElementType must not be an abstract" }
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc
>> new file mode 100644
>> index 00000000000..3511cef1c3a
>> --- /dev/null
>> +++
>> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc
>> @@ -0,0 +1,23 @@
>> +// { dg-do run { target c++26 xfail *-*-* } }
>> +// { dg-require-debug-mode "" }
>> +
>> +#include <mdspan>
>> +#include <array>
>> +
>> +void
>> +test_unaligned_access()
>> +{
>> +  constexpr size_t N = 4;
>> +  alignas(N) std::array<char, 128> buffer{};
>> +  auto* unaligned = buffer.data() + 1;
>> +  auto a = std::aligned_accessor<char, N>{};
>> +
>> +  [[maybe_unused]] char x = a.access(unaligned, 0);
>> +}
>> +
>> +int
>> +main()
>> +{
>> +  test_unaligned_access();
>> +  return 0;
>> +};
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc
>> new file mode 100644
>> index 00000000000..319da5ffef3
>> --- /dev/null
>> +++
>> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc
>> @@ -0,0 +1,23 @@
>> +// { dg-do run { target c++26 xfail *-*-* } }
>> +// { dg-require-debug-mode "" }
>> +
>> +#include <mdspan>
>> +#include <array>
>> +
>> +void
>> +test_unaligned_offset()
>> +{
>> +  constexpr size_t N = 4;
>> +  alignas(N) std::array<char, 128> buffer{};
>> +  auto* unaligned = buffer.data() + 1;
>> +  auto a = std::aligned_accessor<char, N>{};
>> +
>> +  [[maybe_unused]] char* x = a.offset(unaligned, 0);
>> +}
>> +
>> +int
>> +main()
>> +{
>> +  test_unaligned_offset();
>> +  return 0;
>> +};
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
>> index c3350353aae..d420fe885aa 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
>> @@ -82,10 +82,28 @@ template<template<typename T> typename Accessor>
>>
>>   static_assert(test_properties<std::default_accessor>());
>>
>> +#ifdef __glibcxx_aligned_accessor
>> +template<size_t Mult>
>> +struct OverAlignedAccessorTrait
>> +{
>> +  template<typename T>
>> +    using type = std::aligned_accessor<T, Mult*alignof(T)>;
>> +};
>> +
>> +static_assert(test_properties<OverAlignedAccessorTrait<1>::type>());
>> +static_assert(test_properties<OverAlignedAccessorTrait<2>::type>());
>> +#endif
>> +
>>   template<typename A>
>>     constexpr size_t
>>     accessor_alignment = alignof(typename A::element_type);
>>
>> +#ifdef __glibcxx_aligned_accessor
>> +template<typename T, size_t N>
>> +  constexpr size_t
>> +  accessor_alignment<std::aligned_accessor<T, N>> = N;
>> +#endif
>> +
>>   template<typename Accessor>
>>     constexpr void
>>     test_access(Accessor accessor)
>> @@ -121,5 +139,10 @@ main()
>>   {
>>     test_all<std::default_accessor<double>>();
>>     static_assert(test_all<std::default_accessor<double>>());
>> +
>> +#ifdef __glibcxx_aligned_accessor
>> +  test_all<typename OverAlignedAccessorTrait<4>::type<double>>();
>> +  static_assert(test_all<typename
>> OverAlignedAccessorTrait<4>::type<double>>());
>> +#endif
>>     return 0;
>>   }
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
>> index 752060262a0..18826005971 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/version.cc
>> @@ -10,3 +10,11 @@
>>   #elif __cplusplus > 202302L && __cpp_lib_mdspan != 202406L
>>   #error "Feature test macro __cpp_lib_mdspan has the wrong value for C++26"
>>   #endif
>> +
>> +#if __cplusplus > 202302L
>> +#ifndef __cpp_lib_aligned_accessor
>> +#error "Feature test macro __cpp_lib_aligned_accessor is missing for
>> <mdspan>"
>> +#elif __cpp_lib_aligned_accessor != 202411L
>> +#error "Feature test macro __cpp_lib_aligned_accessor has the wrong value"
>> +#endif
>> +#endif
>> --
>> 2.50.0
>>
>>
> 



More information about the Libstdc++ mailing list