added a mapper that maps the indexes of a view to the indices of a container#2880
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new index_mapper class that enables conversion of indices from a view's coordinate system to the underlying container's coordinate system. This is particularly useful when working with views that contain fixed dimensions (integral slices), which reduce the dimensionality compared to the original container.
Key Changes:
- Implements
index_mappertemplate class with support forxviewtypes - Provides
map()andmap_at()methods for index conversion with and without bounds checking - Adds comprehensive test coverage for 2D and 3D views with various slicing patterns
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 22 comments.
| File | Description |
|---|---|
include/xtensor/views/index_mapper.hpp |
New header file implementing the index_mapper class with template metaprogramming to handle view-to-container index mapping |
test/test_xview.cpp |
Adds test cases for index_mapper functionality covering simple 2D views and 3D views with various slice configurations |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * @throws Assertion failure if `i != 0` for integral slices. | ||
| * @throws Assertion failure if `i >= slice.size()` for non-integral slices. |
There was a problem hiding this comment.
The assertion message is misleading in the documentation. The assertion checks that the index is within bounds (i < slice.size()), but the documentation states it "throws Assertion failure if i >= slice.size()". While technically correct, assertions don't throw exceptions in the traditional sense - they abort the program in debug builds. Consider updating the documentation to clarify this behavior or use proper exception handling if runtime bounds checking is desired.
| * @throws Assertion failure if `i != 0` for integral slices. | |
| * @throws Assertion failure if `i >= slice.size()` for non-integral slices. | |
| * @note This function uses assertions to enforce preconditions: | |
| * - Aborts with assertion failure if `i != 0` for integral slices. | |
| * - Aborts with assertion failure if `i >= slice.size()` for non-integral slices. | |
| * These are not exceptions and will terminate the program in debug builds if triggered. |
| value_type map_at(const UnderlyingContainer& container, const view_type& view, const Indices... indices) const | ||
| requires(sizeof...(Indices) == n_free); | ||
|
|
||
| constexpr size_t dimension() const { return n_free; } |
There was a problem hiding this comment.
The dimension() method returns the number of free (non-integral) dimensions, but the method name is ambiguous. It's unclear whether this returns the view's dimension or the container's dimension. Consider renaming to free_dimensions() or view_dimension() to make the purpose clearer.
| constexpr size_t dimension() const { return n_free; } | |
| constexpr size_t free_dimensions() const { return n_free; } |
…e number of specified slices. If more indices are provided we assume an slice. The underlying container handles the excess/missing indices
JohanMabille
left a comment
There was a problem hiding this comment.
It would be better to have a single public and a single private sections in the index_mapper class (the public being the first one so we can quickly browse its API when we need).
| static constexpr size_t nb_integral_slices = (std::is_integral_v<Slices> + ...); | ||
|
|
||
| /// @brief Number of slices that are xt::newaxis (insert a dimension) | ||
| static constexpr size_t nb_new_axis_slices = (xt::detail::is_newaxis<Slices>::value + ...); |
There was a problem hiding this comment.
I think we can take the opportunity to define xt::detail::is_new_axis_v constant (just list std::is_xxx_v to avoid the need for ::value).
|
|
||
| /// @brief Helper type alias for the I-th slice type | ||
| template <size_t I> | ||
| using ith_slice_type = std::tuple_element_t<I, std::tuple<Slices...>>; |
There was a problem hiding this comment.
Naming: I think the ith prefix can be removed.
|
|
||
| /// @brief True if the I-th slice is an integral slice (fixed index) | ||
| template <size_t I> | ||
| static consteval bool is_ith_slice_integral(); |
|
|
||
| /// @brief True if the I-th slice is a newaxis slice | ||
| template <size_t I> | ||
| static consteval bool is_ith_slice_new_axis(); |
| using new_axis_type = typename indices_sequence_helper<first + 1, bound, indices...>::Type; | ||
|
|
||
| // NOTE: is_ith_slice_new_axis works even if first >= sizeof...(Slices) | ||
| using Type = std::conditional_t<is_ith_slice_new_axis<first>(), new_axis_type, not_new_axis_type>; |
There was a problem hiding this comment.
naming convention: should be type.
| * @return value_type The value at the mapped location in the container. | ||
| */ | ||
| template <size_t n_indices, size_t... Is> | ||
| value_type map_all_indices( |
There was a problem hiding this comment.
The return type should be const_reference. Also, a non-const overload should be provided.
There was a problem hiding this comment.
So the compiler can't chose between the const and non const overload.
// Version 1: Takes const container, returns const_reference
template <size_t n_indices, size_t... Is>
const_reference map_at_all_indices(
const UnderlyingContainer& container, // const reference
const view_type& view,
std::index_sequence<Is...>,
const std::array<size_t, n_indices>& indices
) const;
// Version 2: Takes non-const container, returns reference
template <size_t n_indices, size_t... Is>
reference map_at_all_indices(
UnderlyingContainer& container, // non-const reference
const view_type& view,
std::index_sequence<Is...>,
const std::array<size_t, n_indices>& indices
) const;Should I use a prefix or postfix to differentiate the two implementations ?
There was a problem hiding this comment.
Although I don't really like this option, let's do it for finishing the implementation of the mapper, and we can later understand and fix this issue.
There was a problem hiding this comment.
Fixed cmap for const and map for non-const
| * @throws std::out_of_range if any index is out of bounds. | ||
| */ | ||
| template <size_t n_indices, size_t... Is> | ||
| value_type map_at_all_indices( |
| */ | ||
| template <std::integral... Indices> | ||
| value_type | ||
| map(const UnderlyingContainer& container, const view_type& view, const Indices... indices) const; |
| */ | ||
| template <std::integral... Indices> | ||
| value_type | ||
| map_at(const UnderlyingContainer& container, const view_type& view, const Indices... indices) const; |
| { | ||
|
|
||
| template <class UndefinedView> | ||
| struct index_mapper; |
There was a problem hiding this comment.
This should be declared as class to avoid a warning.
…ator() and .at() calls with a unified subfunction. A
|
Thanks! |
Checklist
Description
The
index_mapperclass provides functionality to convert indices from a view's coordinate systemto the corresponding indices in the underlying container. This is particularly useful for views
that contain integral slices (fixed indices), as these slices reduce the dimensionality of the view.
Example: