VoidStar
VoidStar

Reputation: 5421

Is there a common pattern for passing vector<string> as span<string_view>?

I don't think there's any getting away from the need to allocate a buffer to hold the span of string_views. So probably an intermediate std::vector<std::string_view>? Any good overall pattern to crush it down to 1 line? If necessary, what kind of helper/util would be best?

std::vector<std::string> lineStorage;

std::span<std::string_view> lines = /*lineStorage */;
std::span<std::string_view, 3> linesFixed = /* lineStorage */;

Upvotes: 4

Views: 1316

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275820

Spans are views into contiguous buffers of that specific object, not abstractions of covariance.

I'd just pass a span of strings. KISS.

Assuming I end up having a reason to be super fancy and need a single stable API, a span of foo is poor. You want multiple spans of foo that are provided on demand. If the spans are large enough, the overhead becomes cheap, but this permits caller to not allocate all data at once.

template<class T>
using ask_for=function_view<void(T)>;
template<class T>
using provide=ask_for<ask_for<T>>;

then we take:

void do_stuff(provide<std::span<std::string_view>> src){
  src([](std::span<std::string_view>> strings){
    // code consuming strings
  });
}

on the provider side, we can make a fixed buffer of string views, wrapping pieces of the std vector of string, and repeatedly pass the fixed buffer (with new strings) to the callback.

void provide_strings(std::span<std::string> strings){
  std::size_t count = 0;
  do_stuff([&](ask_for<std::span<std::string_view>> sink){
    std::array<std::string_view, 1024> views;
    while(count<strings.size()){
      for(std::size_t i = count; i <std::min(count+1024, strings.size());++i){
        views[i-count]=strings; // I think this isn't legal, but you get the idea
      }
      sink( {views.data(), std::min(strings.size()-count, 1024ull)});
      count += 1024;
    }
  });
}

ok a bit pseudocodey. But I hope you get the idea.

...

If you really want a one-liner at point of use:

struct string_view_helper_t:
  std::vector<std::string_view>
{
  string_view_helper_t(std::span<std::string> vec) {
    views().reserve(vec.size());
    for (auto&& str:vec)
      views().push_back(str);
  }
  std::vector<std::string_view>& views() { return *this; }
  std::vector<std::string_view> const& views()const { return *this; }
  operator std::span<std::string_view>()&&{ return views(); }
};
string_view_helper_t as_span_of_views( std::span<std::string> vec ) {
  return {vec};
}

then

void target_func(std::span<std::string_view> s) {
  for (auto str:s)
    std::cout << str << '\n';
}

can be called like this:

std::vector<std::string> vec={"a", "b", "c"};
target_func( as_span_of_views(vec) );

Upvotes: 0

Related Questions