martinmoene / span-lite

span lite - A C++20-like span for C++98, C++11 and later in a single-file header-only library
Boost Software License 1.0
495 stars 40 forks source link

Get a constant sized subspan #31

Closed Flamefire closed 4 years ago

Flamefire commented 5 years ago

Just a question on preferred usage: Assume I have a memory which contains a number of arrays of fixed size. I get this memory as a span<T> and the array count as a runtime parameter. Hence the span size is a runtime parameter. Now I want to Process this memory chunk 1 array at a time:

constexpr size_t arraySize = ...;
for(int i=0; i<numArrays; i++) {
  auto subArray = mySpan.subspan(arraySize*i, arraySize);
  // work on subArray
}

Although arraySize is a compile time constant and could be filled into the subArray template param, subspan does not do/allow that.

Questions:

  1. How to solve this task in general (the preferred way)?
  2. Can I make a subspan with a constant size from a variable-sized span?
  3. Is there any advantage in having a constant size?
martinmoene commented 5 years ago

How is arraySize known at compile-time?

  1. see below.
  2. Compile-time over run-time computations.

For now I leave 1. unanswered.

#include "nonstd/span.hpp"
#include <iostream>

using nonstd::span;

// current span lite default: std::ptrdiff_t:
using index_type = span<int>::index_type; 

int memory[] =
{
    1,2,3,
    4,5,6,
    7,8,9,
    0,0,0,
    0,0,0
};

size_t numArrays()
{
    return 4;
}

template< typename T, index_type Extent >
void consume( int i, span<T,Extent> spn )
{
    std::cout << i << ": ";
    for( auto x : spn )
    {
        std::cout << x << ", ";
    }
    std::cout << "\n";
}

template< typename T >
void consume( span<T> spn, size_t numArrays )
{
    constexpr index_type arraySize = 3; // <== how is this known at compile-time? <==

    for( int i = 0; i < numArrays; i++ )
    {
        consume( i, span<T,arraySize>( spn.begin() + i * arraySize, arraySize ) );
    }
}

int main()
{
    consume( span<int>( memory ), numArrays() );
}

// g++ -std=c++17 -Dspan_CONFIG_INDEX_TYPE=size_t -I../include -o issue-31.exe issue-31.cpp && issue-31.exe
// g++ -std=c++17 -Dspan_CONFIG_INDEX_TYPE=std::ptrdiff_t -I../include -o issue-31.exe issue-31.cpp && issue-31.exe

// > g++ -std=c++17 -I../include -o issue-31.exe issue-31.cpp && issue-31.exe
// 0: 1, 2, 3,
// 1: 4, 5, 6,
// 2: 7, 8, 9,
// 3: 0, 0, 0,
Flamefire commented 5 years ago

Thank you.

How is arraySize known at compile-time?

In our case the sub-array is an image with fixed size. We can get any number of such images but each has the same size.

Ok, I was hoping for something less repetitive than span<T,arraySize>( spn.begin() + i * arraySize, arraySize ) (mentioning the type again and arraySize 3 times) And is there a reason for using begin over data? I'd thought that this would be invalid as begin is an iterator (and only happens to be a pointer)

Compile-time over run-time computations.

Can you elaborate a bit? I'm highly interested in which cases this might be beneficial (in this case). So what computations are then performed on compiletime?

martinmoene commented 5 years ago

Indeed, use data().

In C++17 one can avoid one use of arraySize:

consume( i, span( spn.data() + i * arraySize, arraySize ) );

Compile-time over run-time computations.

was a generic answer.

Depends on what you're going to do and it's perhaps easier to investigate yourself as you know your code, as opposed to multiple back and forths...

martinmoene commented 5 years ago

And using std::vector<std::array<int, arraySize>> memory =... variant 1, variant 2 and variant 3.