-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++] P3029R1: Better mdspan
's CTAD
#87873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
mdspan
's CTAD
@llvm/pr-subscribers-libcxx Author: Xiaoyang Liu (xiaoyang-sde) ChangesFull diff: https://github.com/llvm/llvm-project/pull/87873.diff 8 Files Affected:
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index abf1570737df9e..4a1fca4ce6db9c 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -46,6 +46,7 @@ Implemented Papers
- P2872R3 - Remove ``wstring_convert`` From C++26
- P2302R4 - ``std::ranges::contains``
- P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
+- P3029R1 - Better ``mdspan``'s CTAD
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index af270150fda0c0..f707d996a26ea8 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -61,5 +61,5 @@
"`P1068R11 <https://wg21.link/P1068R11>`__","LWG","Vector API for random number generation","Tokyo March 2024","","",""
"`P2944R3 <https://wg21.link/P2944R3>`__","LWG","Comparisons for ``reference_wrapper``","Tokyo March 2024","","",""
"`P2642R6 <https://wg21.link/P2642R6>`__","LWG","Padded ``mdspan`` layouts","Tokyo March 2024","","",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","LWG","Better ``mdspan``'s CTAD","Tokyo March 2024","","",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","LWG","Better ``mdspan``'s CTAD","Tokyo March 2024","|Complete|","19.0",""
"","","","","","",""
diff --git a/libcxx/include/__fwd/span.h b/libcxx/include/__fwd/span.h
index 8dafa742c19df5..2a438254bc4468 100644
--- a/libcxx/include/__fwd/span.h
+++ b/libcxx/include/__fwd/span.h
@@ -10,7 +10,13 @@
#ifndef _LIBCPP___FWD_SPAN_H
#define _LIBCPP___FWD_SPAN_H
+#include <__concepts/convertible_to.h>
+#include <__concepts/equality_comparable.h>
#include <__config>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_integral.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_const.h>
#include <cstddef>
#include <limits>
@@ -31,6 +37,23 @@ class span;
#endif
+#if _LIBCPP_STD_VER >= 26
+
+template <class _Tp>
+concept __integral_constant_like =
+ is_integral_v<decltype(_Tp::value)> && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>> &&
+ convertible_to<_Tp, decltype(_Tp::value)> && equality_comparable_with<_Tp, decltype(_Tp::value)> &&
+ bool_constant<_Tp() == _Tp::value>::value &&
+ bool_constant<static_cast<decltype(_Tp::value)>(_Tp()) == _Tp::value>::value;
+
+template <class _Tp>
+constexpr size_t __maybe_static_ext = dynamic_extent;
+
+template <__integral_constant_like _Tp>
+constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
+
+#endif // _LIBCPP_STD_VER >= 26
+
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index d9a0108d6d3507..ed029ae0a0ea7a 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -17,6 +17,7 @@
#ifndef _LIBCPP___MDSPAN_MDSPAN_H
#define _LIBCPP___MDSPAN_MDSPAN_H
+#include "__fwd/span.h"
#include <__assert>
#include <__config>
#include <__fwd/mdspan.h>
@@ -266,10 +267,17 @@ class mdspan {
friend class mdspan;
};
+# if _LIBCPP_STD_VER < 26
template <class _ElementType, class... _OtherIndexTypes>
requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
explicit mdspan(_ElementType*, _OtherIndexTypes...)
-> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
+# else
+template <class _ElementType, class... _OtherIndexTypes>
+ requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
+explicit mdspan(_ElementType*, _OtherIndexTypes...)
+ -> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
+# endif
template <class _Pointer>
requires(is_pointer_v<remove_reference_t<_Pointer>>)
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index c13d9eef001ac9..76f34b67e41004 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -370,7 +370,11 @@ namespace std {
template<class ElementType, class... Integrals>
requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
explicit mdspan(ElementType*, Integrals...)
- -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;
+ -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>; // until C++26
+ template<class ElementType, class... Integrals>
+ requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
+ explicit mdspan(ElementType*, Integrals...)
+ -> mdspan<ElementType, dextents<size_t, maybe-static-ext<Integrals>...>>; // since C++26
template<class ElementType, class OtherIndexType, size_t N>
mdspan(ElementType*, span<OtherIndexType, N>)
diff --git a/libcxx/include/span b/libcxx/include/span
index c0fe25ddb4beb9..7c430036478d55 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -18,6 +18,20 @@ namespace std {
// constants
inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
+template<class T>
+ concept integral-constant-like = // exposition only, since C++26
+ is_integral_v<decltype(T::value)> &&
+ !is_same_v<bool, remove_const_t<decltype(T::value)>> &&
+ convertible_to<T, decltype(T::value)> &&
+ equality_comparable_with<T, decltype(T::value)> &&
+ bool_constant<T() == T::value>::value &&
+ bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
+
+template<class T>
+ constexpr size_t maybe-static-ext = dynamic_extent; // exposition only, since C++26
+template<integral-constant-like T>
+ constexpr size_t maybe-static-ext<T> = {T::value};
+
// [views.span], class template span
template <class ElementType, size_t Extent = dynamic_extent>
class span;
@@ -110,7 +124,9 @@ private:
};
template<class It, class EndOrSize>
- span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
+ span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>; // until C++26
+template<class It, class EndOrSize>
+ span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
template<class T, size_t N>
span(T (&)[N]) -> span<T, N>;
@@ -563,8 +579,13 @@ _LIBCPP_HIDE_FROM_ABI auto as_writable_bytes(span<_Tp, _Extent> __s) noexcept {
return __s.__as_writable_bytes();
}
+#if _LIBCPP_STD_VER < 26
template <contiguous_iterator _It, class _EndOrSize>
span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
+#else
+template <contiguous_iterator _It, class _EndOrSize>
+span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;
+#endif
template <class _Tp, size_t _Sz>
span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
index 557829dcbad9be..8d3a86910e33d7 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
@@ -22,7 +22,11 @@
// template<class ElementType, class... Integrals>
// requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
// explicit mdspan(ElementType*, Integrals...)
-// -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;
+// -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>; // until C++26
+// template<class ElementType, class... Integrals>
+// requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
+// explicit mdspan(ElementType*, Integrals...)
+// -> mdspan<ElementType, dextents<size_t, maybe-static-ext<Integrals>...>>; // since C++26
//
// template<class ElementType, class OtherIndexType, size_t N>
// mdspan(ElementType*, span<OtherIndexType, N>)
@@ -102,6 +106,18 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
// deduction from pointer and integral like
ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan<T, std::dextents<size_t, 2>>);
+#if _LIBCPP_STD_VER >= 26
+ // P3029R1: deduction from `integral_constant`
+ ASSERT_SAME_TYPE(
+ decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{})), std::mdspan<T, std::extents<size_t, 5>>);
+ ASSERT_SAME_TYPE(decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent)),
+ std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent>>);
+ ASSERT_SAME_TYPE(
+ decltype(std::mdspan(
+ handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent, std::integral_constant<size_t, 7>{})),
+ std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent, 7>>);
+#endif
+
std::array<char, 3> exts;
// deduction from pointer and array
ASSERT_SAME_TYPE(decltype(std::mdspan(handle, exts)), std::mdspan<T, std::dextents<size_t, 3>>);
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 398862dccdb6a1..92bb7c9caea836 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -31,6 +31,7 @@
#include <iterator>
#include <memory>
#include <string>
+#include <type_traits>
#include "test_macros.h"
@@ -48,6 +49,16 @@ void test_iterator_sentinel() {
assert(s.size() == std::size(arr));
assert(s.data() == std::data(arr));
}
+
+#if _LIBCPP_STD_VER >= 26
+ // P3029R1: deduction from `integral_constant`
+ {
+ std::span s{std::begin(arr), std::integral_constant<size_t, 3>{}};
+ ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+ assert(s.size() == std::size(arr));
+ assert(s.data() == std::data(arr));
+ }
+#endif
}
void test_c_array() {
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution. A few minor comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! LGTM!
Thanks for the code review! Would you mind merging this pull request if it looks good? I don't have the permission to do so. |
Thanks for the ping. I just landed the patch. |
## Abstract This pull request implements [P3029R1](https://wg21.link/P3029R1). The paper discusses the current behavior of `mdspan`'s most common pointer-indices CTAD, where the `Extents` template parameter is deduced as `dextents` (dynamic extents), even when passing compile-time constant values. The author believes this behavior is suboptimal, as it doesn't take advantage of the compile-time information. The proposed change suggests deducing static extents if `integral_constant`-like constants are passed, resulting in more intuitive syntax and less error-prone code. ## Reference - [P3029R1](https://wg21.link/P3029R1) - [Draft C++ Standard: [span.syn]](https://eel.is/c++draft/span.syn) - [Draft C++ Standard: [mdspan.syn]](https://eel.is/c++draft/mdspan.syn)
You seem to be missing the CTAD for |
Thanks for pointing this out! I'm attempting to fix this in #89015. |
This patch implements an improvement introduced in P3029R1 that was missed in #87873. It adds a deduction of static extents if integral_constant-like constants are passed to `std::extents`.
This patch implements an improvement introduced in P3029R1 that was missed in llvm#87873. It adds a deduction of static extents if integral_constant-like constants are passed to `std::extents`.
Abstract
This pull request implements P3029R1. The paper discusses the current behavior of
mdspan
's most common pointer-indices CTAD, where theExtents
template parameter is deduced asdextents
(dynamic extents), even when passing compile-time constant values. The author believes this behavior is suboptimal, as it doesn't take advantage of the compile-time information. The proposed change suggests deducing static extents ifintegral_constant
-like constants are passed, resulting in more intuitive syntax and less error-prone code.Reference