[PATCH v4 2/2] libstdc++: Implement aligned_accessor from mdspan [PR120994]

Jonathan Wakely jwakely@redhat.com
Wed Aug 20 17:26:48 GMT 2025


On 04/08/25 12:59 +0200, Luc Grosheintz 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_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               | 54 +++++++++++++++++++
> libstdc++-v3/src/c++23/std.cc.in              |  9 ++--
> .../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 | 31 +++++++++++
> .../testsuite/23_containers/mdspan/version.cc |  8 +++
> 9 files changed, 198 insertions(+), 3 deletions(-)
> 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 691d4836262..9ac283347ce 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
>@@ -1055,6 +1060,55 @@ _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");

Please rephrase this in terms of "must", otherwise it's sometimes
unclear whether "assertion failed: A is too small for E" means that
the assertions was "it's too small" and that failed, or that the
assertion was something else and it failed _because_ it's too small.

In this case it's probably not confusing, but I think it's good to
consistently use the "must" form in our assertions where possible.

So either remove the string literal entirely (because the C++
expression used as the condition is pretty clear already) or change it
to something like:
"ByteAlignment must not be less than alignof(ElementType)".

I have a slight preference for just removing the string literal in
this case.

>+
>+      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<element_type(*)[], _OElementType(*)[]>
>+	constexpr
>+	operator default_accessor<_OElementType>() const noexcept
>+	{ return {}; }
>+
>+      constexpr reference
>+      access(data_handle_type __p, size_t __i) const noexcept
>+      { 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
>+      { 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_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 66009ad89c0..31cb13ed431 100644
>--- a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
>+++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc
>@@ -97,10 +97,36 @@ 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>());
>+static_assert(test_ctor<OverAlignedAccessorTrait<2>::type,
>+			std::default_accessor, false>());
>+static_assert(test_ctor<OverAlignedAccessorTrait<2>::type,
>+			OverAlignedAccessorTrait<4>::type>());
>+static_assert(test_ctor<std::default_accessor,
>+			OverAlignedAccessorTrait<2>::type>());
>+static_assert(!std::is_constructible_v<std::aligned_accessor<char, 4>,
>+				       std::aligned_accessor<char, 2>>);
>+#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)
>@@ -136,5 +162,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