[PATCH v3 5/5] libstdc++: Implement mdspan and tests [PR107761].
Tomasz Kaminski
tkaminsk@redhat.com
Mon Jul 7 13:36:15 GMT 2025
I used TEST_MDSPAN_LAYOUT_LIKE_H as header guard.
On Mon, Jul 7, 2025 at 10:58 AM Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>
>
> On Fri, Jul 4, 2025 at 10:37 AM Luc Grosheintz <luc.grosheintz@gmail.com>
> wrote:
>
>> Implements the class mdspan as described in N4950, i.e. without P3029.
>> It also adds tests for mdspan. This commit completes the implementation
>> of P0009, i.e. the C++23 part <mdspan>.
>>
>> PR libstdc++/107761
>>
>> libstdc++-v3/ChangeLog:
>>
>> * include/std/mdspan (mdspan): New class.
>> * src/c++23/std.cc.in (mdspan): Add.
>> * testsuite/23_containers/mdspan/class_mandate_neg.cc: New test.
>> * testsuite/23_containers/mdspan/mdspan.cc: New test.
>> * testsuite/23_containers/mdspan/layout_like.h: Add class
>> LayoutLike which models a user-defined layout.
>>
>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
>> ---
>>
> Only two small comments:
> - use of header guard instead of pragma
> - use of md.empty() instead of md.extents(0)
> I will do these changes locally.
>
>> libstdc++-v3/include/std/mdspan | 284 +++++++++
>> libstdc++-v3/src/c++23/std.cc.in | 3 +-
>> .../23_containers/mdspan/class_mandate_neg.cc | 41 ++
>> .../23_containers/mdspan/layout_like.h | 80 +++
>> .../testsuite/23_containers/mdspan/mdspan.cc | 603 ++++++++++++++++++
>> .../23_containers/mdspan/out_of_bounds_neg.cc | 24 +
>> 6 files changed, 1034 insertions(+), 1 deletion(-)
>> create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
>> create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
>>
>> diff --git a/libstdc++-v3/include/std/mdspan
>> b/libstdc++-v3/include/std/mdspan
>> index 7e970c2b905..f64804e2a42 100644
>> --- a/libstdc++-v3/include/std/mdspan
>> +++ b/libstdc++-v3/include/std/mdspan
>> @@ -1057,6 +1057,290 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> { return __p + __i; }
>> };
>>
>> + namespace __mdspan
>> + {
>> + template<typename _Extents, typename _IndexType, size_t _Nm>
>> + constexpr bool
>> + __is_multi_index(const _Extents& __exts, span<_IndexType, _Nm>
>> __indices)
>> + {
>> + static_assert(__exts.rank() == _Nm);
>> + for (size_t __i = 0; __i < __exts.rank(); ++__i)
>> + if (__indices[__i] >= __exts.extent(__i))
>> + return false;
>> + return true;
>> + }
>> + }
>> +
>> + template<typename _ElementType, typename _Extents,
>> + typename _LayoutPolicy = layout_right,
>> + typename _AccessorPolicy = default_accessor<_ElementType>>
>> + class mdspan
>> + {
>> + static_assert(!is_array_v<_ElementType>,
>> + "ElementType must not be an array type");
>> + static_assert(!is_abstract_v<_ElementType>,
>> + "ElementType must not be an abstract class type");
>> + static_assert(__mdspan::__is_extents<_Extents>,
>> + "Extents must be a specialization of std::extents");
>> + static_assert(is_same_v<_ElementType,
>> + typename _AccessorPolicy::element_type>);
>> +
>> + public:
>> + using extents_type = _Extents;
>> + using layout_type = _LayoutPolicy;
>> + using accessor_type = _AccessorPolicy;
>> + using mapping_type = typename layout_type::template
>> mapping<extents_type>;
>> + using element_type = _ElementType;
>> + using value_type = remove_cv_t<element_type>;
>> + using index_type = typename extents_type::index_type;
>> + using size_type = typename extents_type::size_type;
>> + using rank_type = typename extents_type::rank_type;
>> + using data_handle_type = typename accessor_type::data_handle_type;
>> + using reference = typename accessor_type::reference;
>> +
>> + static constexpr rank_type
>> + rank() noexcept { return extents_type::rank(); }
>> +
>> + static constexpr rank_type
>> + rank_dynamic() noexcept { return extents_type::rank_dynamic(); }
>> +
>> + static constexpr size_t
>> + static_extent(rank_type __r) noexcept
>> + { return extents_type::static_extent(__r); }
>> +
>> + constexpr index_type
>> + extent(rank_type __r) const noexcept { return
>> extents().extent(__r); }
>> +
>> + constexpr
>> + mdspan()
>> + requires (rank_dynamic() > 0)
>> + && is_default_constructible_v<data_handle_type>
>> + && is_default_constructible_v<mapping_type>
>> + && is_default_constructible_v<accessor_type>
>> + : _M_accessor(), _M_mapping(), _M_handle()
>> + { }
>> +
>> + constexpr
>> + mdspan(const mdspan& __other) = default;
>> +
>> + constexpr
>> + mdspan(mdspan&& __other) = default;
>> +
>> + template<__mdspan::__valid_index_type<index_type>... _OIndexTypes>
>> + requires (sizeof...(_OIndexTypes) == rank()
>> + || sizeof...(_OIndexTypes) == rank_dynamic())
>> + && is_constructible_v<mapping_type, extents_type>
>> + && is_default_constructible_v<accessor_type>
>> + constexpr explicit
>> + mdspan(data_handle_type __handle, _OIndexTypes... __exts)
>> + : _M_accessor(),
>> +
>> _M_mapping(_Extents(static_cast<index_type>(std::move(__exts))...)),
>> + _M_handle(std::move(__handle))
>> + { }
>> +
>> + template<__mdspan::__valid_index_type<index_type> _OIndexType,
>> + size_t _Nm>
>> + requires (_Nm == rank() || _Nm == rank_dynamic())
>> + && is_constructible_v<mapping_type, extents_type>
>> + && is_default_constructible_v<accessor_type>
>> + constexpr explicit(_Nm != rank_dynamic())
>> + mdspan(data_handle_type __handle, span<_OIndexType, _Nm> __exts)
>> + : _M_accessor(), _M_mapping(extents_type(__exts)),
>> + _M_handle(std::move(__handle))
>> + { }
>> +
>> + template<__mdspan::__valid_index_type<index_type> _OIndexType,
>> + size_t _Nm>
>> + requires (_Nm == rank() || _Nm == rank_dynamic())
>> + && is_constructible_v<mapping_type, extents_type>
>> + && is_default_constructible_v<accessor_type>
>> + constexpr explicit(_Nm != rank_dynamic())
>> + mdspan(data_handle_type __handle, const array<_OIndexType, _Nm>&
>> __exts)
>> + : _M_accessor(), _M_mapping(extents_type(__exts)),
>> + _M_handle(std::move(__handle))
>> + { }
>> +
>> + constexpr
>> + mdspan(data_handle_type __handle, const extents_type& __exts)
>> + requires is_constructible_v<mapping_type, const extents_type&>
>> + && is_default_constructible_v<accessor_type>
>> + : _M_accessor(), _M_mapping(__exts), _M_handle(std::move(__handle))
>> + { }
>> +
>> + constexpr
>> + mdspan(data_handle_type __handle, const mapping_type& __mapping)
>> + requires is_default_constructible_v<accessor_type>
>> + : _M_accessor(), _M_mapping(__mapping),
>> _M_handle(std::move(__handle))
>> + { }
>> +
>> + constexpr
>> + mdspan(data_handle_type __handle, const mapping_type& __mapping,
>> + const accessor_type& __accessor)
>> + : _M_accessor(__accessor), _M_mapping(__mapping),
>> + _M_handle(std::move(__handle))
>> + { }
>> +
>> + template<typename _OElementType, typename _OExtents, typename
>> _OLayout,
>> + typename _OAccessor>
>> + requires is_constructible_v<mapping_type,
>> + const typename _OLayout::mapping<_OExtents>&>
>> + && is_constructible_v<accessor_type, const _OAccessor&>
>> + constexpr explicit(!is_convertible_v<
>> + const typename _OLayout::mapping<_OExtents>&, mapping_type>
>> + || !is_convertible_v<const _OAccessor&, accessor_type>)
>> + mdspan(const mdspan<_OElementType, _OExtents, _OLayout,
>> _OAccessor>&
>> + __other)
>> + : _M_accessor(__other.accessor()), _M_mapping(__other.mapping()),
>> + _M_handle(__other.data_handle())
>> + {
>> + static_assert(is_constructible_v<data_handle_type,
>> + const typename _OAccessor::data_handle_type&>);
>> + static_assert(is_constructible_v<extents_type, _OExtents>);
>> + }
>> +
>> + constexpr mdspan&
>> + operator=(const mdspan& __other) = default;
>> +
>> + constexpr mdspan&
>> + operator=(mdspan&& __other) = default;
>> +
>> + template<__mdspan::__valid_index_type<index_type>... _OIndexTypes>
>> + requires (sizeof...(_OIndexTypes) == rank())
>> + constexpr reference
>> + operator[](_OIndexTypes... __indices) const
>> + {
>> + auto __checked_call = [this](auto... __idxs) -> index_type
>> + {
>> + if constexpr (sizeof...(__idxs) > 0)
>> + __glibcxx_assert(__mdspan::__is_multi_index(extents(),
>> + span<const index_type,
>> sizeof...(__idxs)>({__idxs...})));
>> + return _M_mapping(__idxs...);
>> + };
>> +
>> + auto __index = __checked_call(
>> + static_cast<index_type>(std::move(__indices))...);
>> + return _M_accessor.access(_M_handle, __index);
>> + }
>> +
>> + template<__mdspan::__valid_index_type<index_type> _OIndexType>
>> + constexpr reference
>> + operator[](span<_OIndexType, rank()> __indices) const
>> + {
>> + auto __call = [&]<size_t... _Counts>(index_sequence<_Counts...>)
>> + -> reference
>> + { return
>> (*this)[index_type(as_const(__indices[_Counts]))...]; };
>> + return __call(make_index_sequence<rank()>());
>> + }
>> +
>> + template<__mdspan::__valid_index_type<index_type> _OIndexType>
>> + constexpr reference
>> + operator[](const array<_OIndexType, rank()>& __indices) const
>> + { return (*this)[span<const _OIndexType, rank()>(__indices)]; }
>> +
>> + constexpr size_type
>> + size() const noexcept
>> + {
>> + __glibcxx_assert(cmp_less_equal(_M_mapping.required_span_size(),
>> + numeric_limits<size_t>::max()));
>> + return size_type(__mdspan::__size(extents()));
>> + }
>> +
>> + [[nodiscard]]
>> + constexpr bool
>> + empty() const noexcept
>> + {
>> + return __mdspan::__empty(extents());
>> + }
>> +
>> + friend constexpr void
>> + swap(mdspan& __x, mdspan& __y) noexcept
>> + {
>> + std::swap(__x._M_mapping, __y._M_mapping);
>> + std::swap(__x._M_accessor, __y._M_accessor);
>> + std::swap(__x._M_handle, __y._M_handle);
>> + }
>> +
>> + constexpr const extents_type&
>> + extents() const noexcept { return _M_mapping.extents(); }
>> +
>> + constexpr const data_handle_type&
>> + data_handle() const noexcept { return _M_handle; }
>> +
>> + constexpr const mapping_type&
>> + mapping() const noexcept { return _M_mapping; }
>> +
>> + constexpr const accessor_type&
>> + accessor() const noexcept { return _M_accessor; }
>> +
>> + static constexpr bool
>> + is_always_unique() { return mapping_type::is_always_unique(); }
>> +
>> + static constexpr bool
>> + is_always_exhaustive() { return
>> mapping_type::is_always_exhaustive(); }
>> +
>> + static constexpr bool
>> + is_always_strided() { return mapping_type::is_always_strided(); }
>> +
>> + constexpr bool
>> + is_unique() const { return _M_mapping.is_unique(); }
>> +
>> + constexpr bool
>> + is_exhaustive() const { return _M_mapping.is_exhaustive(); }
>> +
>> + constexpr bool
>> + is_strided() const { return _M_mapping. is_strided(); }
>> +
>> + constexpr index_type
>> + stride(rank_type __r) const { return _M_mapping.stride(__r); }
>> +
>> + private:
>> + [[no_unique_address]] accessor_type _M_accessor;
>> + [[no_unique_address]] mapping_type _M_mapping;
>> + [[no_unique_address]] data_handle_type _M_handle;
>> + };
>> +
>> + template<typename _CArray>
>> + requires is_array_v<_CArray> && (rank_v<_CArray> == 1)
>> + mdspan(_CArray&)
>> + -> mdspan<remove_all_extents_t<_CArray>,
>> + extents<size_t, extent_v<_CArray, 0>>>;
>> +
>> + template<typename _Pointer>
>> + requires is_pointer_v<remove_reference_t<_Pointer>>
>> + mdspan(_Pointer&&)
>> + -> mdspan<remove_pointer_t<remove_reference_t<_Pointer>>,
>> extents<size_t>>;
>> +
>> + template<typename _ElementType, typename... _Integrals>
>> + requires (is_convertible_v<_Integrals, size_t> && ...)
>> + && (sizeof...(_Integrals) > 0)
>> + explicit mdspan(_ElementType*, _Integrals...)
>> + -> mdspan<_ElementType,
>> + extents<size_t,
>> __mdspan::__dynamic_extent<_Integrals>()...>>;
>> +
>> + template<typename _ElementType, typename _OIndexType, size_t _Nm>
>> + mdspan(_ElementType*, span<_OIndexType, _Nm>)
>> + -> mdspan<_ElementType, dextents<size_t, _Nm>>;
>> +
>> + template<typename _ElementType, typename _OIndexType, size_t _Nm>
>> + mdspan(_ElementType*, const array<_OIndexType, _Nm>&)
>> + -> mdspan<_ElementType, dextents<size_t, _Nm>>;
>> +
>> + template<typename _ElementType, typename _IndexType, size_t...
>> _ExtentsPack>
>> + mdspan(_ElementType*, const extents<_IndexType, _ExtentsPack...>&)
>> + -> mdspan<_ElementType, extents<_IndexType, _ExtentsPack...>>;
>> +
>> + template<typename _ElementType, typename _MappingType>
>> + mdspan(_ElementType*, const _MappingType&)
>> + -> mdspan<_ElementType, typename _MappingType::extents_type,
>> + typename _MappingType::layout_type>;
>> +
>> + template<typename _MappingType, typename _AccessorType>
>> + mdspan(const typename _AccessorType::data_handle_type&, const
>> _MappingType&,
>> + const _AccessorType&)
>> + -> mdspan<typename _AccessorType::element_type,
>> + typename _MappingType::extents_type,
>> + typename _MappingType::layout_type, _AccessorType>;
>> +
>> _GLIBCXX_END_NAMESPACE_VERSION
>> }
>> #endif
>> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
>> std.cc.in
>> index e692caaa5f9..b62114be467 100644
>> --- a/libstdc++-v3/src/c++23/std.cc.in
>> +++ b/libstdc++-v3/src/c++23/std.cc.in
>> @@ -1851,7 +1851,8 @@ export namespace std
>> using std::layout_right;
>> using std::layout_stride;
>> using std::default_accessor;
>> - // FIXME layout_left_padded, layout_right_padded, aligned_accessor and
>> mdspan
>> + using std::mdspan;
>> + // FIXME layout_left_padded, layout_right_padded, aligned_accessor,
>> mdsubspan
>> }
>> #endif
>>
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
>> new file mode 100644
>> index 00000000000..de19b6d85c9
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
>> @@ -0,0 +1,41 @@
>> +// { dg-do compile { target c++23 } }
>> +#include<mdspan>
>> +
>> +#include <cstdint>
>> +#include "layout_like.h"
>> +
>> +struct ExtentsLike
>> +{
>> + using index_type = int;
>> + using size_type = unsigned int;
>> + using rank_type = size_t;
>> +
>> + static constexpr size_t rank() { return 1; }
>> + static constexpr size_t rank_dynamic() { return 0; }
>> +};
>> +
>> +constexpr bool
>> +test_custom_extents_type()
>> +{
>> + std::mdspan<double, ExtentsLike> md1; // { dg-error "required from
>> here" }
>> + return true;
>> +}
>> +static_assert(test_custom_extents_type());
>> +
>> +constexpr bool
>> +test_element_type_mismatch()
>> +{
>> + using E = std::extents<int, 1>;
>> + using L = std::layout_right;
>> + using A = std::default_accessor<double>;
>> +
>> + [[maybe_unused]] std::mdspan<float, E, L, A> md2; // { dg-error
>> "required from here" }
>> + return true;
>> +};
>> +static_assert(test_element_type_mismatch());
>> +
>> +// { dg-prune-output "Extents must be a specialization of std::extents" }
>> +// { dg-prune-output "no type named '_S_storage'" }
>> +// { dg-prune-output "non-constant condition" }
>> +// { dg-prune-output "static assertion failed" }
>> +// { dg-prune-output "__glibcxx_assert" }
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> new file mode 100644
>> index 00000000000..fc239d65411
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
>> @@ -0,0 +1,80 @@
>> +#pragma once
>>
> Please use header-guard instead of checking empty.
>
>> +
>> +struct LayoutLike
>> +{
>> + template<typename Extents>
>> + class mapping
>> + {
>> + public:
>> + using extents_type = Extents;
>> + using index_type = typename extents_type::index_type;
>> + using size_type = typename extents_type::size_type;
>> + using rank_type = typename extents_type::rank_type;
>> + using layout_type = LayoutLike;
>> +
>> + constexpr
>> + mapping() noexcept = default;
>> +
>> + constexpr
>> + mapping(Extents exts)
>> + : m_exts(exts)
>> + { }
>> +
>> + constexpr const extents_type&
>> + extents() const noexcept { return m_exts; }
>> +
>> + constexpr index_type
>> + required_span_size() const noexcept
>> + {
>> + for (size_t i = 0; i < extents_type::rank(); ++i)
>> + if (m_exts.extent(i) == 0)
>> + return 0;
>> + return 1;
>> + }
>> +
>> + template<typename... Indices>
>> + requires (sizeof...(Indices) == extents_type::rank())
>> + constexpr index_type
>> + operator()(Indices...) const noexcept
>> + { return 0; }
>> +
>> + static constexpr index_type
>> + stride(rank_type) noexcept
>> + { return 0; }
>> +
>> + static constexpr bool
>> + is_always_unique() noexcept
>> + { return false; }
>> +
>> + static constexpr bool
>> + is_always_exhaustive() noexcept
>> + { return true; }
>> +
>> + static constexpr bool
>> + is_always_strided() noexcept
>> + { return true; }
>> +
>> + constexpr bool
>> + is_unique() noexcept
>> + {
>> + if (required_span_size() == 0)
>> + return true;
>> +
>> + for (size_t i = 0; i < extents_type::rank(); ++i)
>> + if (m_exts.extent(i) > 1)
>> + return false;
>> + return true;
>> + }
>> +
>> + static constexpr bool
>> + is_exhaustive() noexcept
>> + { return true; }
>> +
>> + static constexpr bool
>> + is_strided() noexcept
>> + { return true; }
>> +
>> + private:
>> + Extents m_exts;
>> + };
>> +};
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> new file mode 100644
>> index 00000000000..ba67cb00e1f
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> @@ -0,0 +1,603 @@
>> +// { dg-do run { target c++23 } }
>> +#include <mdspan>
>> +
>> +#include <testsuite_hooks.h>
>> +#include "extents/int_like.h"
>> +#include "layout_like.h"
>> +
>> +constexpr auto dyn = std::dynamic_extent;
>> +
>> +template<typename MDSpan, typename T, typename E, typename L =
>> std::layout_right,
>> + typename A = std::default_accessor<T>>
>> + constexpr void
>> + assert_typedefs()
>> + {
>> + static_assert(std::same_as<typename MDSpan::extents_type, E>);
>> + static_assert(std::same_as<typename MDSpan::layout_type, L>);
>> + static_assert(std::same_as<typename MDSpan::accessor_type, A>);
>> + static_assert(std::same_as<typename MDSpan::mapping_type,
>> + typename L::mapping<E>>);
>> + static_assert(std::same_as<typename MDSpan::element_type, T>);
>> + static_assert(std::same_as<typename MDSpan::value_type,
>> + std::remove_const_t<T>>);
>> + static_assert(std::same_as<typename MDSpan::index_type,
>> + typename E::index_type>);
>> + static_assert(std::same_as<typename MDSpan::size_type,
>> + typename E::size_type>);
>> + static_assert(std::same_as<typename MDSpan::rank_type,
>> + typename E::rank_type>);
>> + static_assert(std::same_as<typename MDSpan::data_handle_type,
>> + typename A::data_handle_type>);
>> + static_assert(std::same_as<typename MDSpan::reference,
>> + typename A::reference>);
>> + }
>> +
>> +template<typename T, typename E, typename L, template<typename U>
>> typename A>
>> + constexpr void
>> + test_typedefs()
>> + { assert_typedefs<std::mdspan<T, E, L, A<T>>, T, E, L, A<T>>(); }
>> +
>> +constexpr void
>> +test_typedefs_all()
>> +{
>> + using E = std::extents<int, 1, 2>;
>> + using L = std::layout_left;
>> +
>> + test_typedefs<double, E, L, std::default_accessor>();
>> + test_typedefs<const double, E, L, std::default_accessor>();
>> +}
>> +
>> +template<typename MDSpan>
>> + constexpr void
>> + test_rank()
>> + {
>> + using Extents = typename MDSpan::extents_type;
>> + static_assert(MDSpan::rank() == Extents::rank());
>> + static_assert(MDSpan::rank_dynamic() == Extents::rank_dynamic());
>> + }
>> +
>> +constexpr bool
>> +test_rank_all()
>> +{
>> + test_rank<std::mdspan<double, std::extents<int>>>();
>> + test_rank<std::mdspan<double, std::extents<int, 1>>>();
>> + test_rank<std::mdspan<double, std::extents<int, dyn>>>();
>> + return true;
>> +}
>> +
>> +template<typename Extents>
>> + constexpr void
>> + test_extent(Extents exts)
>> + {
>> + double data = 1.0;
>> + auto md = std::mdspan(&data, exts);
>> + using MDSpan = decltype(md);
>> +
>> + for(size_t i = 0; i < MDSpan::rank(); ++i)
>> + {
>> + VERIFY(MDSpan::static_extent(i) == Extents::static_extent(i));
>> + VERIFY(md.extent(i) == exts.extent(i));
>> + }
>> + }
>> +
>> +constexpr bool
>> +test_extent_all()
>> +{
>> + // For rank == 0, check existence of the methods without calling them.
>> + test_extent(std::extents<int>{});
>> + test_extent(std::extents<int, 0>{});
>> + test_extent(std::extents<int, dyn>{});
>> + return true;
>> +}
>> +
>> +template<typename MDSpan>
>> + constexpr void
>> + test_class_properties()
>> + {
>> + static_assert(std::copyable<MDSpan>);
>> + static_assert(std::is_nothrow_move_constructible_v<MDSpan>);
>> + static_assert(std::is_nothrow_move_assignable_v<MDSpan>);
>> + static_assert(std::is_nothrow_swappable_v<MDSpan>);
>> + constexpr bool trivially_copyable =
>> + std::is_trivially_copyable_v<typename MDSpan::accessor_type>
>> + && std::is_trivially_copyable_v<typename MDSpan::mapping_type>
>> + && std::is_trivially_copyable_v<typename MDSpan::data_handle_type>;
>> + static_assert(std::is_trivially_copyable_v<MDSpan> ==
>> trivially_copyable);
>> + }
>> +
>> +constexpr bool
>> +test_class_properties_all()
>> +{
>> + test_class_properties<std::mdspan<double, std::extents<int>>>();
>> + test_class_properties<std::mdspan<double, std::extents<int, 1>>>();
>> + test_class_properties<std::mdspan<double, std::extents<int, dyn>>>();
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_default_ctor()
>> +{
>> + static_assert(!std::is_default_constructible_v<std::mdspan<double,
>> + std::extents<int>>>);
>> + static_assert(!std::is_default_constructible_v<std::mdspan<double,
>> + std::extents<int, 1>>>);
>> + static_assert(std::is_default_constructible_v<std::mdspan<double,
>> + std::extents<int, dyn>>>);
>> +
>> + std::mdspan<double, std::extents<int, dyn>> md;
>> + VERIFY(md.data_handle() == nullptr);
>> + VERIFY(md.extent(0) == 0);
>>
> I would check md.empty() instead of checking extent here. I think on
> mdspan level,
> this is more interesting to check.
>
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_from_other()
>> +{
>> + using Extents = std::extents<int, 3, 5, 7>;
>> + auto exts = Extents{};
>> +
>> + auto mapping = std::layout_right::mapping(exts);
>> + constexpr size_t n = mapping.required_span_size();
>> + std::array<double, n> storage{};
>> +
>> + auto md1 = std::mdspan(storage.data(), exts);
>> + auto md2 = std::mdspan<double, std::dextents<int, 3>>(md1);
>> +
>> + VERIFY(md1.data_handle() == md2.data_handle());
>> + VERIFY(md1.size() == md2.size());
>> +
>> + static_assert(!std::is_convertible_v<
>> + std::mdspan<double, std::extents<unsigned int, 2>>,
>> + std::mdspan<double, std::extents<int, 2>>>);
>> +
>> + static_assert(std::is_convertible_v<
>> + std::mdspan<double, std::extents<int, 2>>,
>> + std::mdspan<const double, std::extents<int, 2>>>);
>> +
>> + static_assert(!std::is_constructible_v<
>> + std::mdspan<double, std::extents<int, 2>>,
>> + std::mdspan<const double, std::extents<int, 2>>>);
>> +
>> + return true;
>> +}
>> +
>> +template<typename T, typename E, typename L = std::layout_right,
>> + typename A = std::default_accessor<T>>
>> + constexpr void
>> + assert_deduced_typedefs(auto md)
>> + { assert_typedefs<decltype(md), T, E, L, A>(); }
>> +
>> +constexpr bool
>> +test_from_carray()
>> +{
>> + constexpr size_t n = 5;
>> + double data[n] = {1.1, 2.2, 3.3, 4.4, 5.5};
>> +
>> + auto md = std::mdspan(data);
>> + assert_deduced_typedefs<double, std::extents<size_t, n>>(md);
>> + VERIFY(md.rank() == 1);
>> + VERIFY(md.rank_dynamic() == 0);
>> + VERIFY(md[2] == data[2]);
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_from_pointer()
>> +{
>> + double value = 12.3;
>> + auto md = std::mdspan(&value);
>> + assert_deduced_typedefs<double, std::extents<size_t>>(md);
>> + VERIFY(md.rank() == 0);
>> + VERIFY(md.rank_dynamic() == 0);
>> + VERIFY(md[] == value);
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_from_pointer_and_shape()
>> +{
>> + constexpr size_t n = 6;
>> + std::array<double, n> data{1.1, 2.2, 3.3, 4.4, 5.5, 6.6};
>> + std::array<int, 2> shape{2, 3};
>> + std::span<const int, 2> shape_view(shape);
>> +
>> + auto verify = [&data](auto md)
>> + {
>> + assert_deduced_typedefs<double, std::dextents<size_t, 2>>(md);
>> + VERIFY(md.rank() == 2);
>> + VERIFY(md.rank_dynamic() == 2);
>> + VERIFY((md[0, 0]) == data[0]);
>> + VERIFY((md[0, 1]) == data[1]);
>> + VERIFY((md[1, 0]) == data[3]);
>> + };
>> +
>> + verify(std::mdspan(data.data(), shape[0], shape[1]));
>> + verify(std::mdspan(data.data(), shape));
>> + verify(std::mdspan(data.data(), shape_view));
>> +
>> + std::mdspan<double, std::dextents<size_t, 2>> md1 = {data.data(),
>> shape};
>> + verify(md1);
>> +
>> + std::mdspan<double, std::dextents<size_t, 2>> md2 = {data.data(),
>> shape_view};
>> + verify(md2);
>> +
>> + static_assert(std::is_constructible_v<
>> + std::mdspan<float, std::extents<int, 3, 5>>, float*>);
>> + static_assert(!std::is_constructible_v<
>> + std::mdspan<float, std::extents<int, 3, 5>>, float*, int>);
>> + static_assert(std::is_constructible_v<
>> + std::mdspan<float, std::extents<int, 3, 5>>, float*, int, int>);
>> + static_assert(std::is_constructible_v<
>> + std::mdspan<float, std::extents<int, 3, 5>>, float*,
>> std::span<int, 0>>);
>> + static_assert(std::is_constructible_v<
>> + std::mdspan<float, std::extents<int, 3, 5>>, float*,
>> std::span<int, 2>>);
>> + static_assert(!std::is_convertible_v<
>> + float*, std::mdspan<float, std::extents<int, 3, 5>>>);
>> +
>> + static_assert(std::is_constructible_v<
>> + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int,
>> 2>>);
>> + static_assert(!std::is_constructible_v<
>> + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int,
>> 1>>);
>> + static_assert(!std::is_constructible_v<
>> + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int,
>> 3>>);
>> + static_assert(!std::is_constructible_v<
>> + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int,
>> dyn>>);
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_from_extents()
>> +{
>> + constexpr size_t n = 3*5*7;
>> + std::array<double, n> storage{};
>> + using Extents = std::extents<int, 3, 5, 7>;
>> + auto exts = Extents{};
>> + auto md = std::mdspan(storage.data(), exts);
>> +
>> + assert_deduced_typedefs<double, Extents>(md);
>> + VERIFY(md.data_handle() == storage.data());
>> + VERIFY(md.extents() == exts);
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_from_mapping()
>> +{
>> + constexpr size_t n = 3*5*7;
>> + std::array<double, n> storage{};
>> + using Extents = std::extents<int, 3, 5, 7>;
>> +
>> + auto exts = Extents{};
>> + auto m = std::layout_left::mapping(exts);
>> + auto md = std::mdspan(storage.data(), m);
>> +
>> + assert_deduced_typedefs<double, Extents, std::layout_left>(md);
>> + VERIFY(md.data_handle() == storage.data());
>> + VERIFY(md.mapping() == m);
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_from_accessor()
>> +{
>> + constexpr size_t n = 3*5*7;
>> + std::array<double, n> storage{};
>> + using Extents = std::extents<int, 3, 5, 7>;
>> +
>> + auto exts = Extents{};
>> + auto m = std::layout_left::mapping(exts);
>> + auto a = std::default_accessor<double>{};
>> + auto md = std::mdspan(storage.data(), m, a);
>> +
>> + assert_deduced_typedefs<double, Extents, std::layout_left>(md);
>> + VERIFY(md.data_handle() == storage.data());
>> + VERIFY(md.mapping() == m);
>> + return true;
>> +}
>> +
>> +void
>> +test_from_int_like()
>> +{
>> + constexpr size_t n = 3*5*7;
>> + std::array<double, n> storage{};
>> +
>> + auto verify = [&](auto md)
>> + {
>> + VERIFY(md.data_handle() == storage.data());
>> + VERIFY(md.extent(0) == 3);
>> + VERIFY(md.extent(1) == 5);
>> + VERIFY(md.extent(2) == 7);
>> +
>> + VERIFY((md[IntLike(0), 0, IntLike(0)]) == 0.0);
>> + auto zero = std::array{IntLike(0), IntLike(0), IntLike(0)};
>> + auto zero_view = std::span<IntLike, 3>{zero};
>> + VERIFY((md[zero]) == 0.0);
>> + VERIFY((md[zero_view]) == 0.0);
>> + };
>> +
>> + auto shape = std::array{IntLike(3), IntLike(5), IntLike(7)};
>> + auto shape_view = std::span<IntLike, 3>{shape};
>> + verify(std::mdspan(storage.data(), IntLike(3), 5, IntLike(7)));
>> + verify(std::mdspan(storage.data(), shape));
>> + verify(std::mdspan(storage.data(), shape_view));
>> +}
>> +
>> +template<typename T, bool NothrowConstructible = true,
>> + bool NothrowAssignable = true>
>> + class OpaqueAccessor
>> + {
>> + struct Handle
>> + {
>> + constexpr
>> + Handle(T * other)
>> + : ptr(other)
>> + { }
>> +
>> + constexpr
>> + Handle(const Handle&) noexcept(NothrowConstructible) = default;
>> +
>> + constexpr
>> + Handle(Handle&&) noexcept(NothrowConstructible) = default;
>> +
>> + constexpr Handle&
>> + operator=(const Handle&) noexcept(NothrowAssignable) = default;
>> +
>> + constexpr Handle&
>> + operator=(Handle&&) noexcept(NothrowAssignable) = default;
>> +
>> + T * ptr;
>> + };
>> +
>> + public:
>> + using element_type = T;
>> + using reference = T&;
>> + using data_handle_type = Handle;
>> + using offset_policy = OpaqueAccessor;
>> +
>> + reference
>> + access(data_handle_type p, size_t i) const
>> + {
>> + ++access_count;
>> + return p.ptr[i];
>> + }
>> +
>> + typename offset_policy::data_handle_type
>> + offset(data_handle_type p, size_t i) const
>> + {
>> + ++offset_count;
>> + return typename offset_policy::data_handle_type{(void*)(p.ptr +
>> i)};
>> + }
>> +
>> + mutable size_t access_count = 0;
>> + mutable size_t offset_count = 0;
>> + };
>> +
>> +void
>> +test_from_opaque_accessor()
>> +{
>> + constexpr size_t n = 3*5*7;
>> + std::array<double, n> storage{};
>> + using Extents = std::extents<int, 3, 5, 7>;
>> +
>> + auto exts = Extents{};
>> + auto m = std::layout_left::mapping(exts);
>> + auto a = OpaqueAccessor<double>{};
>> + auto handle = OpaqueAccessor<double>::data_handle_type{storage.data()};
>> + auto md = std::mdspan(handle, m, a);
>> +
>> + using MDSpan = decltype(md);
>> + static_assert(std::same_as<MDSpan::accessor_type, decltype(a)>);
>> +
>> + VERIFY((md[0, 0, 0]) == 0.0);
>> + VERIFY(md.accessor().access_count == 1);
>> +
>> + VERIFY((md[2, 4, 6]) == 0.0);
>> + VERIFY(md.accessor().access_count == 2);
>> +}
>> +
>> +template<typename T, typename Base>
>> + class BaseClassAccessor
>> + {
>> + public:
>> + using element_type = T;
>> + using reference = Base&;
>> + using data_handle_type = T*;
>> + using offset_policy = BaseClassAccessor;
>> +
>> + static_assert(std::common_reference_with<reference&&,
>> element_type&>);
>> +
>> + reference
>> + access(data_handle_type p, size_t i) const
>> + { return p[i]; }
>> +
>> + typename offset_policy::data_handle_type
>> + offset(data_handle_type p, size_t i) const
>> + { return typename offset_policy::data_handle_type{p + i}; }
>> + };
>> +
>> +struct Base
>> +{
>> + double value = 1.0;
>> +};
>> +
>> +struct Derived : Base
>> +{
>> + double value = 2.0;
>> +};
>> +
>> +void
>> +test_from_base_class_accessor()
>> +{
>> + constexpr size_t n = 3*5*7;
>> + std::array<Derived, n> storage{};
>> + using Extents = std::extents<int, 3, 5, 7>;
>> +
>> + auto exts = Extents{};
>> + auto m = std::layout_left::mapping(exts);
>> + auto a = BaseClassAccessor<Derived, Base>{};
>> + auto md = std::mdspan(storage.data(), m, a);
>> +
>> + using MDSpan = decltype(md);
>> + static_assert(std::same_as<MDSpan::accessor_type, decltype(a)>);
>> + static_assert(std::same_as<decltype(md[0, 0, 0]), Base&>);
>> + VERIFY((md[0, 0, 0].value) == 1.0);
>> + VERIFY((md[2, 4, 6].value) == 1.0);
>> +}
>> +
>> +constexpr bool
>> +test_from_mapping_like()
>> +{
>> + double data = 1.1;
>> + auto m = LayoutLike::mapping<std::extents<int, 1, 2, 3>>{};
>> + auto md = std::mdspan(&data, m);
>> + VERIFY((md[0, 0, 0]) == data);
>> + VERIFY((md[0, 1, 2]) == data);
>> + return true;
>> +}
>> +
>> +template<typename MDSpan>
>> + constexpr void
>> + test_empty(MDSpan md)
>> + {
>> + VERIFY(md.empty() == (md.size() == 0));
>> + }
>> +
>> +constexpr bool
>> +test_empty_all()
>> +{
>> + test_empty(std::mdspan<double, std::extents<int, dyn>>{});
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_access()
>> +{
>> + using Extents = std::extents<int, 3, 5, 7>;
>> + auto exts = Extents{};
>> +
>> + auto mapping = std::layout_left::mapping(exts);
>> + constexpr size_t n = mapping.required_span_size();
>> + std::array<double, n> storage{};
>> +
>> + auto md = std::mdspan(storage.data(), mapping);
>> + static_assert(std::__mdspan::__mapping_alike<decltype(md)>);
>> +
>> + for(int i = 0; i < exts.extent(0); ++i)
>> + for(int j = 0; j < exts.extent(1); ++j)
>> + for(int k = 0; k < exts.extent(2); ++k)
>> + {
>> + std::array<int, 3> ijk{i, j, k};
>> + storage[mapping(i, j, k)] = 1.0;
>> + VERIFY((md[i, j, k]) == 1.0);
>> + VERIFY((md[ijk]) == 1.0);
>> + VERIFY((md[std::span(ijk)]) == 1.0);
>> + storage[mapping(i, j, k)] = 0.0;
>> + }
>> + return true;
>> +}
>> +
>> +constexpr bool
>> +test_swap()
>> +{
>> + using Extents = std::dextents<int, 2>;
>> + auto e1 = Extents{3, 5};
>> + auto e2 = Extents{7, 11};
>> +
>> + std::array<double, 3*5> s1{};
>> + std::array<double, 7*11> s2{};
>> +
>> + auto md1 = std::mdspan(s1.data(), e1);
>> + auto md2 = std::mdspan(s2.data(), e2);
>> +
>> + std::swap(md1, md2);
>> +
>> + VERIFY(md1.data_handle() == s2.data());
>> + VERIFY(md2.data_handle() == s1.data());
>> +
>> + VERIFY(md1.size() == s2.size());
>> + VERIFY(md2.size() == s1.size());
>> + return true;
>> +}
>> +
>> +template<bool Constructible, bool Assignable>
>> +constexpr void
>> +test_nothrow_movable()
>> +{
>> + using Layout = std::layout_left;
>> + using Extents = std::dextents<int, 3>;
>> + using Accessor = OpaqueAccessor<int, Constructible, Assignable>;
>> + using Handle = Accessor::data_handle_type;
>> + static_assert(std::is_nothrow_move_assignable_v<Accessor>);
>> + static_assert(std::is_nothrow_move_constructible_v<Accessor>);
>> + static_assert(std::is_nothrow_move_assignable_v<Handle> == Assignable);
>> + static_assert(std::is_nothrow_move_constructible_v<Handle> ==
>> Constructible);
>> +
>> + using MDSpan = std::mdspan<int, Extents, Layout, Accessor>;
>> + static_assert(std::is_nothrow_move_assignable_v<MDSpan> == Assignable);
>> + static_assert(std::is_nothrow_move_constructible_v<MDSpan> ==
>> Constructible);
>> +}
>> +
>> +constexpr void
>> +test_nothrow_movable_all()
>> +{
>> + using MDSpan = std::mdspan<double, std::dextents<int, 3>>;
>> + static_assert(std::is_nothrow_move_assignable_v<MDSpan>);
>> + static_assert(std::is_nothrow_move_constructible_v<MDSpan>);
>> +
>> + test_nothrow_movable<true, true>();
>> + test_nothrow_movable<true, false>();
>> + test_nothrow_movable<false, true>();
>> + test_nothrow_movable<false, false>();
>> +}
>> +
>> +int
>> +main()
>> +{
>> + test_typedefs_all();
>> +
>> + test_rank_all();
>> + test_extent_all();
>> + static_assert(test_extent_all());
>> +
>> + test_class_properties_all();
>> + static_assert(test_class_properties_all());
>> +
>> + test_empty_all();
>> + static_assert(test_empty_all());
>> +
>> + test_default_ctor();
>> + static_assert(test_default_ctor());
>> +
>> + test_from_other();
>> + static_assert(test_from_other());
>> +
>> + test_from_carray();
>> + static_assert(test_from_carray());
>> +
>> + test_from_pointer_and_shape();
>> + static_assert(test_from_pointer_and_shape());
>> +
>> + test_from_extents();
>> + static_assert(test_from_extents());
>> +
>> + test_from_mapping();
>> + static_assert(test_from_mapping());
>> +
>> + test_from_accessor();
>> + static_assert(test_from_accessor());
>> +
>> + test_from_int_like();
>> + test_from_opaque_accessor();
>> + test_from_base_class_accessor();
>> + test_from_mapping_like();
>> + static_assert(test_from_mapping_like());
>> +
>> + test_access();
>> + static_assert(test_access());
>> +
>> + test_swap();
>> + static_assert(test_swap());
>> +
>> + test_nothrow_movable_all();
>> + return 0;
>> +}
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
>> new file mode 100644
>> index 00000000000..dceae56e9d9
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
>> @@ -0,0 +1,24 @@
>> +// { dg-do compile { target c++23 } }
>> +#include<mdspan>
>> +
>> +#include "layout_like.h"
>> +
>> +template<typename Layout>
>> +constexpr bool
>> +test_invalid_multi_index()
>> +{
>> +
>> + double data = 1.1;
>> + auto m = typename Layout::mapping<std::extents<int, 1, 2, 3>>{};
>> + auto md = std::mdspan(&data, m);
>> +
>> + [[maybe_unused]] double x = md[0, 2, 2]; // { dg-error "expansion of" }
>> + return true;
>> +};
>> +static_assert(test_invalid_multi_index<LayoutLike>()); // { dg-error
>> "expansion of" }
>> +static_assert(test_invalid_multi_index<std::layout_left>()); // {
>> dg-error "expansion of" }
>> +static_assert(test_invalid_multi_index<std::layout_right>()); // {
>> dg-error "expansion of" }
>> +static_assert(test_invalid_multi_index<std::layout_stride>()); // {
>> dg-error "expansion of" }
>> +
>> +// { dg-prune-output "non-constant condition" }
>> +// { dg-prune-output "__glibcxx_assert" }
>> --
>> 2.49.0
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250707/991258fe/attachment-0001.htm>
More information about the Libstdc++
mailing list