On Fri, Jul 25, 2025 at 10:37 AM Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> > > On 7/23/25 11:25, 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.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.groshei...@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(*)[]> > > Should be: > > requires is_convertible_v<element_type(*)[], _OElementType(*)[]> > > I'll generalize the test_ctor in generic.cc to accept two Accessors, > then we can test this with the same rigour. > Good catch. I am not sure about the generalization at this point, as: * conversion between different accessor categories will be that common * if the explicitness will match between accessors. So I am just fine having a single test in aligned accessor now, but if you find it easier to express this in test_ctor, then no object to doing that. Regards, Tomasz > > > + 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)); > > + } > > + 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 > >