• entries
21
28
• views
34327

# [C++] ArrayView-class

2526 views

So I've been quite busy with refactoring the rendering and other systems of the engine for the last few months, and once I'm done I'll make some more entries for the graphical render-system.

Today I've been wondering if there is no cleaner solution to having to pass an arbitrary array to a function. I generally tend to fully qualify the container in such a case, but thats not always possible (and quite frankly not the flexible). What I'm talking about is a case like that:DrawItem* Compile(const StateGroup* pGroups, size_t numGroups);{ for(size_t i = 0; i < numGroups; i++) // no range-based for loops eigther :( { }}
Having to pass in the pointer and size separately to the function seems unsafe, C-esque and redundant to me. Not being able to use range-based for-loops is a minor annoyance. But there is no other native way, if I potentially want to support any source of array (raw fixed-size array, std::vector, std::array, dynamic allocation, ...).

So I came up with what I call an ArrayView-class. What it does and how it works is actually quite simple, which makes me wonder why I havn't seen anybody else come up with something like that:DrawItem* Compile(ArrayView groups){ for(const auto& group : groups) { }}
Yeah, thats the gist of it. I'm using a templated class to pass a view to an array, with a known size. The cool thing is, now it doesn't matter where this array comes from:void printArray(ArrayView view){ for(const int value : view) { std::cout << value; } std::cout << std::endl << std::endl;}void main(void){ std::vector vector = {1, 2, 3, 4, 5}; std::array array = { 1, 2, 3, 4, 5 }; int raw[] = { 1, 2, 3, 4, 5 }; int* pDynamic = new int[5] { 1, 2, 3, 4, 5}; const auto vectorView = makeArrayView(vector); const auto arrayView = makeArrayView(array); const auto rawView = makeArrayView(raw); const auto iteratorView = makeArrayView(vector.begin() + 2, vector.begin() + 4); const auto pointerView = makeArrayView(raw + 1, raw + 3); const auto dynamicView = makeArrayView(pDynamic, 5); const ArrayView* views[] = { &vectorView, &arrayView, &rawView, &iteratorView, &pointerView, &dynamicView, }; for(const auto pView : views) { printArray(*pView); } }
You can construct it from std::vector, std::array, compile-time arrays, even random-access iterators (probably a tad unsafe), and a raw-range of pointers (even more unsafe, but meh).

________________________________________________________

So... any reasons nobody has come up with something like that yet? Is the problem I'm trying to solve so uncommon/unimportant that nobody cared? Or are people still wary of using templates?
For me, I think I've spent my ~hour to write this quite well, as this will probably kill my conditioning to just put "vector" everywhere, even when I could just pass in a regular array. :D

For those of you interested in using this or trying it out, I've attached the header-file, alongside a visual-studio natvis-file. Requires C++11 (VS2015 or higher), and you'll have to replace the AE_ASSERTS with your own assert-macro, if you use one. This is the first iteration just after writing it, so there's probably still lots of stuff missing, but I consider it useable :)

This is pretty neat, it is in C++ GSL btw.

Otherwise this is what i do for ranged for loops when there are no iterators:

https://gist.github.com/ongamex/0ecf47bd5eff4044b0cc26db20c86102

the usage is

for(const auto& v : range_int(0, size))
{
// ...
}

As mentioned above, this is pretty much exactly what gsl::span does. In addition to this gsl also provides string_span and multi_span. If you haven't played around yet with gsl, I definitely suggest you give it a try. It's been incredibly useful for us so far.

EDIT:

I also remember seeing a Microsoft implementation of array_view, and am quite sure that an implementation of array_view was suggested for an upcoming C++ standard. I also believe that boost provides a similar implementation.

Ah, I didn't know about GSL. span seems to be exactly what I've done, just with lots more features (and probably more robust).

Microsofts array_view seems to take <T, Size>-parameters, and is thus unfit for my use-case. Seems to be more about some data as an array, which is something I've seen people ask for while researching this, too.

I've read the array_view proposal, but just from skimming over the proposal & source, I couldn't tell if it does the same as I wanted to have.

DrawItem* Compile(ArrayView<const StateGroup> groups)
{
for(const auto& group : groups)
{
}
}


better

DrawItem* Compile(const ArrayView<const StateGroup> &groups)
{
for(const auto& group : groups)
{
}
}


you do not want to copy an array everytime you pass it , not referencing copies the array

The ArrayView doesn't store the array, just a pointer+size. So not referecing copies exaclty 8 byte (12 on 64 bit), the same if you passed pointer+size separately.

Copying such small structs is usually equally fast, if not faster, then passing by reference, especially when the entire function argument list still fits into registers. It could even help with aliasing (MSVC is especially bad it this), and letting the compiler know that the value of the ArrayView won't change during this function call.

I usually just pass every struct <= 8, sometimes even 16 byte by value, unless the argument list gets too long (in which case one should probably just create an Args-struct anyways).

I didn't know arraview were just pointer, sorry my bad.

## Create an account

Register a new account