-
Notifications
You must be signed in to change notification settings - Fork 70
A Gentle Introduction to mdspan
ISO-C++ standard proposals are not the best tutorials for how to use their proposed features. This is intentional—there's enough content to be written in exploring the technical design issues that a tutorial-style introduction is out of place in such documents. However, it also means that proposals such as P0009 for mdspan
reach a level of maturity where they are ready for production implementation and use, but without a single tutorial-style introduction. Here's an attempt to do so for mdspan
.
In it's simplest form, std::mdspan
1 is a trivial extension of std::span
:
int* data = /* ... */
// View data as contiguous memory representing 4 ints
auto s = std::span<int, 4>(data);
// View data as contiguous memory representing 2 rows
// of 2 ints each
auto ms = std::mdspan<int, 2, 2>(data);
Introductions to std::span
can be found all over the internet (it will be in C++20), so we won't discuss it any further here. This tutorial assumes the reader is at least familiar with std::span
in a very basic sense.
Just like span
, you can create a dynamically sized mdspan
using the std::dynamic_extent
sentinel value:
int* data = /* ... */
int size = /* ... */
auto s = std::span<int, std::dynamic_extent>(data, size);
int rows = /* ... */
int cols = /* ... */
auto ms =
std::mdspan<int, std::dynamic_extent, std::dynamic_extent>(data, rows, cols);
As you can see, this can get to be quite verbose very quickly. Like many similar standard-library facilities, the intent is that users should address this with type aliases and alias templates with whatever names are most sensible in a particular domain:
// existing practice with existing library features:
template <class T>
using my_pool_alloc_vector = std::vector<T, my_pool_allocator<T>>;
// similar use with mdspan:
template <class T>
using dyn_span_2d = std::mdspan<T, std::dynamic_extent, std::dynamic_extent>;
template <class T>
using dyn_span_8d =
std::mdspan<T,
std::dynamic_extent, std::dynamic_extent,
std::dynamic_extent, std::dynamic_extent,
std::dynamic_extent, std::dynamic_extent,
std::dynamic_extent, std::dynamic_extent
>;
using particle_positions_view = std::mdspan<double, std::dynamic_extent, 3>;
We will, obviously, continue to use the canonical form in this document, but keep in mind that the verbosity of actual code in your domain may have significantly less verbosity. It is also possible that some helper alias templates will be added to the standard in the future, but the committee will probably wait one cycle at least to see what developes as common practice.
Accessing elements of an mdspan
with more than one dimension uses the call operator, ()
, because the []
operator cannot take multiple indices:2
void apply_rotation(
std::mdspan<double, 3, 3> rotation,
std::mdspan<double, 3> vec,
std::mdspan<double, 3> out,
)
{
for(int i = 0; i < 3; ++i) {
out(i) = 0;
for(int j = 0; j < 3; ++j) {
out(i) += rotation(i, j) * vec(j);
}
}
}
Note that out[i]
and vec[j]
are perfectly acceptable as well, but rotation[i, j]
is currently ill-formed.
The design of mdspan
addresses a much broader variety of needs than span
. In many scenarios, it even makes more sense to use a one-dimensional mdspan
instead of a span
.
TODO finish this