Issaquah (Feb 2023) P2630R2 presentation
P2630R2: submdspan
submdspan: multidimensional array slicing
-
Split from P0009 after LEWG review
-
P2630 expands facility to make it work for custom layouts, and adds
strided_slice
R2 changes
-
Added discussion choice of customization point mechanism
-
Renamed
strided_index_rangetostrided_slice -
Introduced named struct combining mapping and offset as return type for
submdspan_mapping -
Removed redundant constraints, which are covered by mandates; this improves error messages for users
Customization point mechanism
-
Paper: Section 2.1.3 "Pure ADL vs. CPO vs. tag invoke"
-
Applies P2279's criteria to judge value of different mechanisms
-
Most users will never call
submdspan_mappingdirectly -
submdspan_mappingonly needs to be implemented for custom layout mappings, a rare use case -
submdspan_mappinghas no default implementation (unlike, say,std::swap) -
Name
submdspan_mappingis extremely specific, making opt-in essentially explicit -
Incorrect opt-in is easy to diagnose: return type must be a layout mapping with specific extents
-
This and other discussion in paper suggest no significant benefits in preferring CPOs or
tag_invokeover pure ADL forsubmdspan_mapping. However, we would accept LEWG's preference fortag_invokehere.
Submdspan Recap
submdspan lets you take subsets of a an mdspan
An example:
// Some data representing 2x3 matrices
double data[6] = { 11, 12, 13,
21, 22, 23 };
// An rank-2 mdspan of the data
mdspan matrix(data, 2, 3);
mdspan<double,extents<size_t, dynamic_extent>, layout_stride>
col_0 = submdspan(matrix, full_extent, 0);
// col_0 == 11, 21
// col_0.stride(0) == 3
mdspan<double,extents<size_t, dynamic_extent>, layout_right>
row_1 = submdspan(matrix, 1, full_extent);
// row_1 == 21, 22, 23
// row_1.stride(0) == 1
// in practice this is a place where auto is your friend:
auto row_0 = submdspan(matrix, 0, full_extent);
submdspan in our proposal is effectively implemented as:
// submdspan takes and mdspan and slice specifiers
template<class T, class E, class L, class A, class ... SliceArgs)
auto submdspan(const mdspan<T,E,L,A>& src, SliceArgs ... args) {
// get the new accessor from the old one
typename A::offset_policy sub_acc(src.accessor());
// get new mapping as well as linear offset from submdspan_mapping
// This function is a customization point and will be called using ADL!
// returns an aggregate type "submdspan_mapping_result"
auto sub_map = submdspan_mapping(src.mapping(), args...);
// Check that we didn't get an invalid submdspan_mapping impl
#ifndef NDEBUG
// submdspan_extents is NOT a customization point.
auto expected_extents = submdspan_extents(src.extents(), args...);
// check that the types match
static_assert(is_same_v<decltype(sub_map.mapping)::extent_type,
decltype(expected_extents)>);
// check that the values match
assert(expected_extents==sub_map.mapping.extents());
#endif
// Compute the new data handle
auto sub_handle = src.accessor().offset(src.data_handle(), sub_map.offset);
// return the new mdspan
return mdspan(sub_handle, sub_map.mapping, sub_acc);
}
Incorporated feedback from last review:
Return Type of submdspan_mapping
Changed from pair to a specific named aggregate type.
Before:
template<class Extents, class... SliceSpecifiers>
constexpr auto submdspan_mapping(
const layout_left::mapping<Extents>& src,
SliceSpecifiers ... slices) {
...
return pair{new_mapping, offset};
}
Now:
template<class LayoutMapping>
struct submdspan_mapping_result {
LayoutMapping mapping;
size_t offset;
};
template<class Extents, class... SliceSpecifiers>
constexpr auto submdspan_mapping(
const layout_left::mapping<Extents>& src,
SliceSpecifiers ... slices) {
...
return submdspan_mapping_result{new_mapping, offset};
}
Explore Customization Point Design
General Considerations
- Most users will never call
submdspan_mappingdirectly - mostly folks will callsubmdspan -
submdspan_mappingonly needs to be implemented for custom layout mappings, a rare use case -
submdspan_mappinghas no default implementation (unlike, say,std::swap)
Explicit Opt-In
- Problem: recognizing that a function is an implementation of a customization point (how do you know that my
swapfriend member is meant to be used withstd::swap? -
submdspan_mappingis a very non-general name - in particular in its overload which takes a mapping and slice specifiers.
Diagnose Incorrect Opt-In
- We know a lot about the return value of
submdspan_mapping, all of which can be checked.- must return a layout mapping
- the return types
extents_typeis mandated - the return values
extents()values are mandated.
Associated Types
- for some customization points things like the return type are well defined by a single template parameter (e.g. return type of the
beginfunction is generallyT::iterator_typeetc. - for
submdspan_mappingnone of the arguments know anything about the return type. The return type is really a combinatorical property of all the arguments.- For example in the following example the input is a
layout_leftmapping but the output is alayout_stridemapping
- For example in the following example the input is a
auto sub = submdspan_mapping(layout_left::mapping(4,4), 0, full_extent);
- But reorder the last two arguments and the return type is a
layout_leftmapping
We concluded: Pure ADL is good enough for us.
GodBolt example of custom layout/accessor in combo with submdspan: https://godbolt.org/z/EhMrYvTfo