Accessor Policy in mdspan
What is the purpose of the accessor policy in mdspan
?
The accessor policy in std::mdspan
is an optional template parameter that allows you to customize how elements are accessed and potentially modified when using the []
operator or the accessor
member function of mdspan
.
The primary purposes of the accessor policy are:
Customizing Element Access Behavior
The accessor policy enables you to define custom behavior for accessing elements in the mdspan
. This can be useful in various scenarios, such as:
- Adding bounds checking: You can implement an accessor policy that checks if the accessed indices are within the valid range of the
mdspan
, preventing out-of-bounds access. - Applying transformations: You can define an accessor policy that applies certain transformations to the elements before returning them, such as scaling, offsetting, or type conversions.
- Implementing read-only or write-only access: You can create accessor policies that restrict access to read-only or write-only operations, enforcing specific access patterns.
Interoperability with External Libraries or APIs
When working with external libraries or APIs that expect a specific accessor behavior, you can use an appropriate accessor policy to adapt the mdspan
to match those expectations.
For example, if an external library expects accessor objects with certain member functions or operators, you can define an accessor policy that provides those required interfaces.
Performance Optimization
In some cases, the accessor policy can be used to optimize element access performance. By defining a custom accessor policy, you can avoid unnecessary function calls or indirections and provide a more efficient way to access elements.
However, it's important to note that the performance impact of accessor policies depends on the specific use case and compiler optimizations. In many cases, the default accessor policy provided by std::mdspan
is already optimized for common scenarios.
Here's an example that demonstrates a custom accessor policy that applies a scaling factor to the elements:
#include <iostream>
#include <mdspan>
// Custom accessor policy that scales
// elements by a factor of 10
struct ScalingAccessor {
template <typename T>
struct accessor {
T* data;
accessor(T* ptr) : data(ptr) {}
T read(std::size_t offset) const {
return data[offset] * 10;
}
void write(std::size_t offset, T value) {
data[offset] = value / 10;
}
};
};
int main() {
std::array<int, 6> arr{1, 2, 3, 4, 5, 6};
std::mdspan<int, std::extents<
std::size_t, 2, 3>, std::layout_right,
ScalingAccessor>
scalingSpan{arr.data()};
std::cout << "Original elements:\n";
for (std::size_t i = 0;
i < scalingSpan.extent(0); ++i) {
for (std::size_t j = 0;
j < scalingSpan.extent(1); ++j) {
std::cout << scalingSpan[i, j] << " ";
}
std::cout << "\n";
}
// Modify elements through the accessor policy
scalingSpan[0, 0] = 10;
scalingSpan[1, 1] = 50;
std::cout << "\nModified elements:\n";
for (std::size_t i = 0;
i < scalingSpan.extent(0); ++i) {
for (std::size_t j = 0;
j < scalingSpan.extent(1); ++j) {
std::cout
<< arr[i * scalingSpan.extent(1) + j]
<< " ";
}
std::cout << "\n";
}
}
Original elements:
10 20 30
40 50 60
Modified elements:
1 2 3
4 5 6
In this example, we define a custom accessor policy called ScalingAccessor
that scales the elements by a factor of 10 when reading and divides them by 10 when writing.
We create an mdspan
object scalingSpan
with the ScalingAccessor
policy. When accessing elements through scalingSpan
, the scaling behavior is applied transparently.
Note that the accessor policy only affects the access through the mdspan
object. When we modify elements through scalingSpan
, the underlying array arr
is updated with the original unscaled values.
Accessor policies provide a powerful way to customize element access behavior in mdspan
, enabling various use cases and optimizations.
Multidimensional Arrays and std::mdspan
A guide to std::mdspan
, allowing us to interact with arrays as if they have multiple dimensions