3

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 */;
VoidStar
  • 5,241
  • 1
  • 31
  • 45
  • Why not just use `std::span`? – 康桓瑋 Nov 07 '21 at 03:33
  • Well, for one if the function already exists that expects `std::string_view`. Generally speaking I greatly prefer passing `std::string_view` to `const string&` in most other contexts at least. – VoidStar Nov 07 '21 at 04:46
  • Must it be`std::span`? How about C++20 view adaptors? – 康桓瑋 Nov 07 '21 at 04:55
  • ```std::span``` is not necessarily a must. However it's not clear to me to me that raw ranges are a good way to deal with passing to a function because of how the typing works. You'd have to rely on template inference to pass a different custom type every time the function gets called, which can be okay sometimes but it can result in big template bloat. I'd prefer if the function that I'm passing to doesn't require any form of templating as it's pretty big. – VoidStar Nov 07 '21 at 08:09

1 Answers1

0

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 havig 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, bht 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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • my head is spinning – bolov Nov 07 '21 at 04:37
  • Interesting idea, turning it inside-out like this makes it a bit like passing an IEnumerable in C#. Not necessarily a bad idea but IDK if this is idiom-worthy yet- kinda hard to read and requires a lot more effort to write too. Maybe this is a job for the new `ranges` stuff? – VoidStar Nov 07 '21 at 04:52
  • @voidstar you can write template glue that makes it take a range of convertable to string view. So at least user doesn't care. – Yakk - Adam Nevraumont Nov 07 '21 at 04:57
  • This blog post has some thoughts along these lines, they create a range-like helper for passing stuff like this: https://artificial-mind.net/blog/2020/10/24/range_ref – VoidStar Nov 07 '21 at 05:04
  • C++20 already has something similar, it's called [`ref_view`](https://en.cppreference.com/w/cpp/ranges/ref_view). – 康桓瑋 Nov 07 '21 at 05:15
  • I think there isn't a decent way to use `ref_view` as a contract due to how ranges work with typing. Unlike `range_ref` on the post. – VoidStar Nov 07 '21 at 07:05