user2705377
user2705377

Reputation: 93

Use of std::span<std::span<uint8_t>>

I use a lot this new functionality of C++20 : std::span<T>. This is useful for writing functions that usually took a pointer and a size.

#include <array>
#include <span>
#include <vector>

std::vector<uint8_t> v1 {1,2,3,4,5};
std::array<uint8_t, 4> v2 {1,2,3,4};
uint8_t v3[3] = {1,2,3};

void f(std::span<uint8_t> arr){
    ...
}

/// call f with v1 or v2 or v3
f(v1);
f(v2);
f(v3);

Now, I would like to have a function that take as parameter a span of span.

std::vector<std::array<uint8_t,10>> v1;
v1.push_back({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
v1.push_back({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
v1.push_back({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});

void f(std::span<std::span<uint8_t, 10>> arr){
    ...
}

f(v1);

Unfortunately, this is not compiling at all. This would be a way to work with 2D array.

Compilation error :

<source>:21:1: error: no matching function for call to 'f'
f(v1);
^
<source>:8:6: note: candidate function not viable: no known conversion from 'std::vector<std::array<uint8_t, 10>>' (aka 'vector<array<unsigned char, 10>>') to 'std::span<std::span<uint8_t, 10>>' (aka 'span<span<unsigned char, 10>>') for 1st argument
void f(std::span<std::span<uint8_t, 10>> arr){
     ^
1 error generated.
ASM generation compiler returned: 1
<source>:21:1: error: no matching function for call to 'f'
f(v1);
^
<source>:8:6: note: candidate function not viable: no known conversion from 'std::vector<std::array<uint8_t, 10>>' (aka 'vector<array<unsigned char, 10>>') to 'std::span<std::span<uint8_t, 10>>' (aka 'span<span<unsigned char, 10>>') for 1st argument
void f(std::span<std::span<uint8_t, 10>> arr){
     ^
1 error generated.
Execution build compiler returned: 1

Do you have any idea on how I can get this working without having to use raw pointer ?

Upvotes: 1

Views: 2748

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275585

Spans require the thing you are spanning over to actually be there in a contiguous buffer.

There is no continguous buffer of actusl span objects there. There is a contiguous buffer of things convertible to span objects.

In this specific case, the subobjects are uniform and constsnt size, but there is a variable number of them.

One of the beauties of std span is that the conversion overhead is nearly zero; to make an actual span of spans, nearly zero (but not zero) work has to be done for each sub-span.

If this is ok with you, writing an automatic vector builder is plausible:

templafr<class T>
struct autoconverting_vector:std::vector<T> {
  using base=std::vector<T>::vector;
  using base::base;
  template<class Range> requires( Range's iterators elements are convertible to T )
  autoconverting_vector( Range&& range ): base(begin(range), end(range)) {}
};

then take an autoconverting_vector<span<uint8_t>>

Another option is to write a 2d span type; this only works cheaply when the 2nd dimension is a std array or similar, because then the sub element layout is fixed and just math.

There are n-dimensional array view types you can use for this.

Upvotes: 1

eerorika
eerorika

Reputation: 238381

That doesn't work because the vector doesn't contain spans. The mistake is somewhat analogous to trying to pass an int[x][y] into function accepting int**.

You should either:

  1. Use a std::span<std::array<uint8_t,10>> to access the vector
void f2(std::span<std::array<uint8_t,10>> arr)
  1. Or transform the vector into a range of spans.
auto to_span = [](auto& arr) -> std::span<uint8_t, 10> {
    return {arr};
};
auto span_range = std::ranges::transform_view(v1, to_span);
std::vector v2(std::begin(span_range), std::end(span_range));
f(v2);

it will not always be a vector of array

If you sometimes want to use one type of argument and other times another type of argument, then perhaps you should be defining a function template:

template<class Range>
void f3(Range& range)

but I am looking for another way, because it has a certain cost O(N).

You already pay the cost of O(N) * O(M) when you create the vector. That one O(N) on top isn't likely to be significant.

Upvotes: 5

Related Questions