-
Notifications
You must be signed in to change notification settings - Fork 81
Description
PL-007 23.7.3.7 [mdspan.sub] Define the extent member of the strided_slice
NB comment: https://github.com/cplusplus/nbballot/issues/816
Paper: https://isocpp.org/files/papers/P3982R0.html
Make strided_slice:::extent define the output extent, not the input span
2 ranges of indices in submdspan
- input span: range of indices into input, that can be accessed by output
- output extent: size of the range of valid indices for output
2 possible choices of meaning for strided_slice's extent
-
input span
$[$ offset,offset + extent$)$ , thus output extent is1 + (extent - 1) / stride- STATUS QUO
-
output extent, thus input span is
$[$ offset,offset + 1 + (extent - 1)*stride$)$ - P3982
Example: status quo vs. P3982R0
Example: strided_slice{.offset = 1, .extent = 10, .stride = 3}.
- Status quo
- View input indices 1, 4, 7, and 10
- Input span:
$[1, 1 + 10) = [1, 11)$ - Output extent:
offset + (extent - 1) / stride=1 + (10 - 1) / 3= 4- NOT same as input
.extent10
- NOT same as input
- P3982R0
- View input indices 1, 4, 7, 10, 13, 16, 19, 22, 25, 28
- Input span:
$[$ offset,offset + 1 + (extent - 1)*stride$)$ =$[1, 29)$ - Output extent: 10
- Same as input
.extent
- Same as input
Status quo aimed for minimal change from first : last : stride
- P2630 lacked P3663's notion of "canonical slice types" vs. all slice types
- Thus, P2630 authors defined slice types based on familiarity
- Most familiar syntax: Fortran's and Python's
first : last : stride - But
first : last : stridecan't represent (dynamic offset, static extent, static stride)- Key case for performance, e.g., unrolled loop
- Status quo in P2630 made the minimal-distance-from-familiarity change to include this case:
- Replace
lastwithextent = last - first - Rename
firsttooffset
- Replace
Why change status quo?
NB comment points out 2 reasons:
- Permit zero strides in the future (for "broadcasting" layouts)
- Enable representing (static extent, dynamic stride)
P3982R0 points out 2 more:
- Avoid integer divisions
- Performance
- Remove undefined behavior (division by zero stride)
- Less confusing (input "extent" is not output extent; I have to look this up every time)
Another reason: strided_slice is a canonical slice type
- Canonical slice types define interface between
submdspanand a layout mapping'ssubmdspan_mapping submdspanfunction body looks like this:
auto [...canonical_slices] =
submdspan_canonicalize_slices(src.extents(), user_slices...);
auto sub_map_result =
submdspan_mapping(src.mapping(), canonical_slices...);
return mdspan(
src.accessor().offset(src.data_handle(), sub_map_result.offset),
sub_map_result.mapping,
typename AccessorPolicy::offset_policy(src.accessor()));- Choose canonical types to express the biggest possible set of slices
- We can always add new user-facing slices to improve familiarity (see
range_slicebelow)
Why do we need to change it for C++26?
Changing the meaning of strided_slice::extent later would silently break the meaning of submdspan.
(It would be an ABI break. You don't like those, right?)
Also for C++26: Rename strided_slice to extent_slice
-
Paper proposes a new non-canonical slice type
range_sliceto cover common use casefirst : last : stride(Python, Fortran, etc.) -
Whether that arrives in C++26 or C++29, that would mean there are multiple
*_slicetypes with a stride -
strided_sliceis a canonical slice type; changing name after C++26 would break users' customizations ofsubmdspan_mapping -
New name:
extent_slice, because it has the output extent, not the input span
Additional new features
Paper proposes 2 new features. They could be added for C++26 or in a later Standard.
-
range_slice{.first = first, .last = last, .stride = stride}- Models slicing in other programming languages
- Calls for renaming
strided_sliceTOextent_sliceNOW, even if we addrange_slicelaterrange_slicealso has a "stride"- So "
strided_slice" wouldn't be the only "strided" slice
-
Interpret any type for which structured binding
auto [first, last, stride] = s;is valid asrange_slice- Only makes sense if we add
range_sliceas in (1)
- Only makes sense if we add
Why add range_slice in C++26?
-
Popular programming languages have
first : last : strideslices, so users will demand them -
Designated initializers alleviate order concerns
Why add auto [first, last, stride] = s;?
- Confusion about order (Python vs. Matlab) led the
submdspan(P2630) authors not to include this feature, but- status quo already interprets
auto [first, last] = s;asstride = cw<1zu> - so
auto [first, last, stride] = s;is less ambiguous, analogous to a default function parameterstride = cw<1zu>
- status quo already interprets
- Trivial to implement: we already handle
auto [...ts] = t;withsizeof...(ts) == 2 - We consider this optional for C++26
Suggested polls
-
Accept rename [to
extent_slice] and changes tostrided_sliceclass template from P3982R0 to C++26. -
Accept the introduction of
range_sliceclass template from P3982R0 to C++26. -
Accept any type decomposable into three elements as
submdspanslice type as proposed in P3982R0 to C++29. -
Accept any type decomposable into three elements as
submdspanslice type as proposed in P3982R0 to C++26.