Code Monkey home page Code Monkey logo

array_view's Introduction

array_view

An implementation of the ISO C++ proposal Multidimensional bounds, offset and array_view by Mendakiewicz & Sutter. This implements the original proposal N3851 up to the last revision 7. As I understand it, the proposal was with an aim for inclusion in C++17 although is no longer under consideration in it's current form. For later contributions, see the successive proposal p0122r0 (and related GSL implementation), and alternative proposals such as P0546.

Principles behind the proposal are the representation of a multidimensional array as a view over contiguous (or strided) data and the straightforward expression for multidimensional indexing into these arrays. The result is a safe, bounded view that works naturally with existing algorithms while maintaining a nice, expressive syntax.

The library is header-only with no external dependencies. If you want to build the tests there is a CMakeLists.txt for building with CMake and Google Test.

Example usage

The following is a very brief overview of some typical usage. For more details, see the original proposal.

int X = 12;
int Y = 8;
int Z = 6;

vector<int> vec(X*Y*Z);

bounds<3> bnds = {X,Y,Z};
array_view<int,3> av(vec, bnds);

A bounds<Rank> defines a multidimensional bounds into an array space of rank Rank over which we wish to construct a view. Above, we construct an array_view<T,Rank> over our vec using the bounds bnds. Note that a view has reference semantics so our vec must stay valid for the lifetime of av.

An offset<Rank> represents a multidimensional index, an integral vector into this array space. Both offset<Rank> and bounds<Rank> share similar interfaces. To access an element:

offset<3> idx = {5,3,2};

av[idx] = 28;
av[{5,3,2}] = 28;  // also fine

The bounds<Rank> class provides begin() and end() member functions that return iterators that iterate over each index of the bounds in turn. Iteration increments first along the least significant dimension (contiguous for non-strided data). This facility allows iteration with a simple range-based for loop:

for (auto& idx : av.bounds())
{
	auto i = idx[0];
	auto j = idx[1];
	auto k = idx[2];  // least significant dimension, incremented first

	av[idx] = i**2 + j - k;
}

Above, bounds() returns the current bounds to the array_view. An alternative would be to construct a bounds_iterator directly. A bounds_iterator is a (nearly) random access iterator whose value_type is an offset into the array space. Iterating a bounds_iterator is typically done over the range defined by bounds:

bounds_iterator<3> first = begin(av.bounds());
bounds_iterator<3> last = end(av.bounds());

// Dereferencing an iterator returns a `const offset<Rank>`. Indices are always immutable.
for_each(first, last, [&av](const offset<3>& idx) {
	auto i = idx[0];
	auto j = idx[1];
	auto k = idx[2];
	assert(av[idx] == i**2 + j - k);
});

Slicing

Slicing returns a lower dimensional 'slice' as a new view of the same data. Slices always slice from the most significant dimensions (here, x):

int x0 = 5, y0 = 3;
array_view<int, 2> slice2d = av[x0];      // a 2d slice in the yz plane
array_view<int, 1> slice1d = av[x0][y0];  // a row in z, also the contiguous dimension

assert( slice2d[{3,2}] == 28 );

For dimensions of rank 1, you can omit the initializer_list:

assert( slice1d[2] == 28 );

Sectioning

Sectioning creates a new view given a new bounds that fully subsumes the original. Sections must be of the same rank as the original. All views created from sections return a strided_array_view:

offset<3> origin = {6,3,2};
bounds<3> window = {3,3,2};
auto section = av.section(origin, window);

// Work with just this section of the data
int sum = std::accumulate(begin(section.bounds()), end(section.bounds()), 0,
                          [&](int a, offset<3> idx) {
	return a + section[idx];
});

Strided data

An additional class strided_array_view relaxes the requirement that the least significant dimension of the referenced data must be contiguous. Strided views commonly arise by sectioning an array_view, but it is possible to construct a strided view directly with a pointer to data in memory for which you must provide the stride.

For example, the following creates a new view of vec that includes every third element and is one-third the extent in the Z dimension:

array_view<int,3> av = .. // as before

// The strides in X and Y are as calculated as for `av`, but the stride in Z is no longer 1
bounds<3> newExtents = {X, Y, Z/3};
offset<3> newStride = {av.stride()[0], av.stride()[1], 3};
strided_array_view<int,3> sav(vec.data(), newExtents, newStride);

for (auto& idx : sav.bounds())
{
	// do something with sav[idx]
}

Taking sliced views of contiguous data, sections of sliced views and views of views all compose and work naturally as you would expect.

Acknowledgements

This implementation follows the original proposal by Mendakiewicz & Sutter and subsequent revisions (latest revision 7). As noted in the proposal, the proposal itself builds on previous work by Callahan, Levanoni and Sutter for their work designing the original interfaces for C++ AMP.

Limitations

My experience implementing the standard is zero, I expect this to fall way short of a conforming implementation. However, as a practical solution it has served sufficient for my purposes, and at least supports a working set of tests. Also I thought this might make an interesting exercise. All comments & suggestions welcome.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.