Skip to content

Commit b704cd7

Browse files
committed
Rework operator[]
This uses C++23's mutlidimensional subscript operator to improve the indexing, and introduces non-contiguous slices.
1 parent 32accb4 commit b704cd7

43 files changed

Lines changed: 2355 additions & 215 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ set(
104104
-Wno-c++98-compat-pedantic
105105
-Wno-missing-braces
106106
-Wno-padded
107+
-Wno-unsafe-buffer-usage
107108
)
108109

109110
set(
@@ -123,7 +124,7 @@ target_include_directories(
123124
vt-ndarray
124125
INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include
125126
)
126-
target_compile_features(vt-ndarray INTERFACE cxx_std_17)
127+
target_compile_features(vt-ndarray INTERFACE cxx_std_23)
127128

128129
add_library(vt::ndarray ALIAS vt-ndarray)
129130

@@ -137,6 +138,7 @@ if(VT_ENABLE_TESTING)
137138
"${CMAKE_CURRENT_LIST_DIR}/test/vt/ndarray/allocator_test.cpp"
138139
"${CMAKE_CURRENT_LIST_DIR}/test/vt/ndarray/container_test.cpp"
139140
"${CMAKE_CURRENT_LIST_DIR}/test/vt/ndarray/foreach_benchmark.cpp"
141+
"${CMAKE_CURRENT_LIST_DIR}/test/vt/ndarray/index_test.cpp"
140142
"${CMAKE_CURRENT_LIST_DIR}/test/vt/ndarray/matrix_mul_benchmark.cpp"
141143
"${CMAKE_CURRENT_LIST_DIR}/test/vt/ndarray/test_main.cpp"
142144
"${CMAKE_CURRENT_LIST_DIR}/test/vt/ndarray/view_test.cpp"

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ N-dimensional array library for C++, developed and maintained by [VORtech](https
88
Getting started
99
---------------
1010

11-
This is a header-only library. To use it, simply add all files under `include` to your include directories. The library uses C++17 features and assumes a C++17 compliant compiler. Compatibility with the following compilers is tested:
11+
This is a header-only library. To use it, simply add all files under `include` to your include directories. The library uses C++23 features and assumes a C++23 compliant compiler. Compatibility with the following compilers is tested:
1212

1313
- GCC 14.2
1414
- Clang 18.1
@@ -27,8 +27,8 @@ int main() {
2727
1, 5, 9
2828
}};
2929

30-
assert(A[0][0] == 3); assert(A[0][1] == 1); assert(A[0][2] == 4);
31-
assert(A[1][0] == 1); assert(A[1][1] == 5); assert(A[1][2] == 9);
30+
assert(A[0, 0] == 3); assert(A[0, 1] == 1); assert(A[0, 2] == 4);
31+
assert(A[1, 0] == 1); assert(A[1, 1] == 5); assert(A[1, 2] == 9);
3232
}
3333
```
3434

@@ -42,9 +42,9 @@ static void diag(vt::ndview<int, 2> A, vt::ndview<const int, 1> d) {
4242
const size_t n = d.shape(0);
4343
for (size_t i = 0; i < n; ++i) {
4444
for (size_t j = 0; j < n; ++j) {
45-
A[i][j] = 0;
45+
A[i, j] = 0;
4646
}
47-
A[i][i] = d[i];
47+
A[i, i] = d[i];
4848
}
4949
}
5050

doc/vt/ndarray/container/index-operator.md

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,39 @@ vt::ndarray::operator[]
33

44
```c++
55
// (1)
6-
decltype(auto) operator[](std::size_t idx) noexcept;
6+
template<indexer... Index>
7+
decltype(auto) operator[](
8+
Index... idx
9+
) noexcept requires((sizeof...(Index) <= N));
710
// (2)
8-
decltype(auto) operator[](std::size_t idx) const noexcept;
11+
template<indexer... Index>
12+
decltype(auto) operator[](
13+
Index... idx
14+
) const noexcept requires((sizeof...(Index) <= N));
915
```
1016
11-
Accesses the view or element at the specified index. The behavior is undefined if `idx >= shape[0]`.
17+
Accesses the view, slice or element at the specified indices or index ranges. The behavior is undefined if any indices are outside the bounds of the array.
1218
13-
1. Will return a mutable view or reference.
14-
2. Will return a const view or reference.
19+
1. Will return a mutable view, slice or reference.
20+
2. Will return a const view, slice or reference.
21+
22+
If `sizeof...(Index) < N`, the result is similar to the arguments being padded with occurrences of [r()](../index-range/r.md#top) until `N` arguments are reached.
1523
1624
Parameters
1725
----------
1826
1927
|||
20-
------- | --------------------------------------
21-
**idx** | index of the view or element to access
28+
------- | -----------------------------------------
29+
**idx** | parameter-pack of indices or index ranges
2230
2331
Return value
2432
------------
2533
26-
If `N > 1`, returns a view of dimension `N - 1` into the sub-array at the specified index.
34+
If all arguments in the parameter-pack are convertible to `std::size_t`, returns a reference to the element at the specified indices.
35+
36+
Else, if the parameter-pack specifies a contiguous slice, returns a [ndview](../view/readme.md#top) of the slice.
2737
28-
If `N = 1`, returns a reference to the element at the specified index.
38+
Else, if the parameter-pack specifies a non-contiguous slice, returns a [ndslice](../slice/readme.md#top).
2939
3040
Example
3141
-------
@@ -35,18 +45,25 @@ Example
3545
#include <cassert>
3646
3747
int main() {
48+
using vt::r;
49+
3850
const vt::ndarray<int, 2> A{{ 2, 3 }, {
3951
3, 1, 4,
4052
1, 5, 9
4153
}};
4254
43-
// Elements can be accessed by chaining calls to operator[]
44-
assert(A[1][1] == 5);
55+
// Elements can be accessed by specifying only indices
56+
assert(A[1, 1] == 5);
57+
58+
// A contiguous slice will result in a vt::ndview
59+
vt::ndview<const int, 1> A_0j = A[0, r()];
60+
assert(A_0j[0] == 3);
61+
assert(A_0j[1] == 1);
62+
assert(A_0j[2] == 4);
4563
46-
// A single call to operator[] will return a view into a sub-array
47-
vt::ndview<const int, 1> A_0 = A[0];
48-
assert(A_0[0] == 3);
49-
assert(A_0[1] == 1);
50-
assert(A_0[2] == 4);
64+
// A non-contiguous slice will result in a vt::ndslice
65+
vt::ndslice<const int, 1> A_i2 = A[r(), 2];
66+
assert(A_i2[0] == 4);
67+
assert(A_i2[1] == 9);
5168
}
5269
```

doc/vt/ndarray/container/readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ int main() {
113113
std::cout << a << '\n';
114114

115115
// The index-operator can be used to access elements:
116-
std::cout << a[0][2][3] << '\n';
116+
std::cout << a[0, 2, 3] << '\n';
117117

118118
// The shape can be queried:
119119
std::cout << a.shape(0) << ',' << a.shape(1) << ',' a.shape(2) << '\n';

doc/vt/ndarray/container/reshape.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ int main() {
5454
assert(A[3] == 1);
5555
5656
vt::ndview<const int, 2> B = A.reshape({ 2, 2 });
57-
assert(B[0][0] == 3);
58-
assert(B[0][1] == 1);
59-
assert(B[1][0] == 4);
60-
assert(B[1][1] == 1);
57+
assert(B[0, 0] == 3);
58+
assert(B[0, 1] == 1);
59+
assert(B[1, 0] == 4);
60+
assert(B[1, 1] == 1);
6161
}
6262
```

doc/vt/ndarray/container/slice.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ int main() {
5252
// When slicing a 2D-array, rows outside of the slice will not be in the
5353
// obtained view
5454
vt::ndview<const int, 2> B = A.slice(1, 2);
55-
assert(B[0][0] == 4);
56-
assert(B[0][1] == 1);
57-
assert(B[1][0] == 5);
58-
assert(B[1][1] == 9);
55+
assert(B[0, 0] == 4);
56+
assert(B[0, 1] == 1);
57+
assert(B[1, 0] == 5);
58+
assert(B[1, 1] == 9);
5959
}
6060
```

doc/vt/ndarray/index-range/r.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
vt::r
2+
=====
3+
4+
```c++
5+
// (1)
6+
constexpr index_range<0> r() noexcept;
7+
// (2)
8+
constexpr index_range<1> r(std::size_t start) noexcept;
9+
// (3)
10+
constexpr index_range<2> r(std::size_t start, std::size_t end) noexcept;
11+
```
12+
13+
Convenience function for creating instances of `vt::index_range`.
14+
15+
1. Creates an index range from the start of an array dimension to the end of an array dimension. (Similar to `:` in Python.)
16+
2. Creates an index range from a specified start to the end of an array dimension. (Similar to `start:` in Python.)
17+
3. Creates an index range from a specified start to a specified end. (Similar to `start:end` in Python.)
18+
19+
The behavior is undefined if `start` or `end` are outside the bounds of the array that the resulting `vt::index_range` indexes into.
20+
21+
Parameters
22+
----------
23+
24+
|||
25+
--------- | -----------------------------------
26+
**start** | start index of the range
27+
**end** | one past the end index of the range
28+
29+
Return value
30+
------------
31+
32+
The resulting `vt::index_range`.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
vt::index_range
2+
===============
3+
4+
- Defined in header `<vt/ndarray/index.hpp>`
5+
- Defined in header `<vt/ndarray.hpp>`
6+
7+
```c++
8+
template<std::size_t N>
9+
struct index_range;
10+
```
11+
12+
Represents a range of indices, for creating a slice when indexing into an array. Either from the start of an array dimension to the end of an array dimension (`index_range<0>`), from a specified start to the end of an array dimension (`index_range<1>`), or from a specified start to a specified end (`index_range<2>`).
13+
14+
Non-member functions
15+
--------------------
16+
17+
|||
18+
------------- | --------------------------------------------------
19+
[r](r.md#top) | convenience function for creating an `index_range`
20+
21+
Example
22+
-------
23+
24+
```c++
25+
#include <vt/ndarray/container.hpp>
26+
#include <iostream>
27+
28+
int main() {
29+
// Use the vt::r convenience function to reduce clutter when creating
30+
// instances of vt::index_range when performing a slicing operation.
31+
using vt::r;
32+
33+
const vt::ndarray<int, 3> a{{ 2, 3, 4 }, {
34+
3, 1, 4, 1,
35+
5, 9, 2, 6,
36+
5, 3, 5, 8,
37+
38+
9, 7, 9, 3,
39+
2, 3, 8, 4,
40+
6, 2, 6, 4
41+
}};
42+
43+
std::cout << a[1, r(1), r(1, 3)] << '\n';
44+
}
45+
```
46+
47+
Output:
48+
49+
```
50+
[[3,8],[2,6]]
51+
```

doc/vt/ndarray/indexer/readme.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
vt::indexer
2+
===========
3+
4+
- Defined in header `<vt/ndarray/index.hpp>`
5+
- Defined in header `<vt/ndarray.hpp>`
6+
7+
```c++
8+
template<typename T>
9+
concept indexer =
10+
std::convertible_to<T, std::size_t>
11+
|| std::is_same_v<T, vt::index_range<0>>
12+
|| std::is_same_v<T, vt::index_range<1>>
13+
|| std::is_same_v<T, vt::index_range<2>>;
14+
```
15+
16+
The concept `indexer<T>` specifies that `T` is an object type that can be used to index into a `vt::ndview`/`vt::ndslice`/`vt::ndarray`.

doc/vt/ndarray/readme.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ This reference documents the API of the vt-ndarray library. Follow the links bel
55

66
- [ndarray](container/readme.md#top)
77
- [ndview](view/readme.md#top)
8+
- [ndslice](slice/readme.md#top)
9+
- [index_range](index-range/readme.md#top)
10+
- [indexer](indexer/readme.md#top)
811
- [ndarray_allocator](allocator/readme.md#top)
912

1013
Notes

0 commit comments

Comments
 (0)