Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reduce verbosity when splitting `std::string_view` by `std::views::split`?

Consider the following snippet:

// included, for exposition only
SomeData buzz(std::string_view);

constexpr auto foo(std::string_view v) {
    using namespace std::string_view_literals;

    return v 
    | std::views::split(";"sv) 

    // `std::string_view` constructor from Range is explicit, so this line is necessary
    | std::views::transform([](const auto& s) { return std::string_view(s); })
    | std::views::transform(buzz)
    ;
}

Ideally I'd have only the transformations required by the actual algorithm without working around the language's/Standard Library's limitations:

constexpr auto bar(std::string_view v) {
    using namespace std::string_view_literals;

    // To be used as a chainable range closure more code is required
    constexpr auto split{[](std::string_view v, std::string_view delim){
        return v | std::views::split(delim) | std::views::transform([](const auto& v){ return std::string_view(v); });
    }};

    return split(v, ";"sv)
    | std::views::transform(buzz);
}

Does the Standard Library provide a view adaptor that splits a std::string_view into std::string_view elements without the extra transform, or is a custom wrapper unavoidable?

like image 224
Sergey Kolesnik Avatar asked Dec 22 '25 22:12

Sergey Kolesnik


2 Answers

There is no such custom adaptor. But it's easy enough to write your own — which also the advantage that you can make it work with string literals. It's not that much boilier plate:

struct SplitClosure : std::ranges::range_adaptor_closure<SplitClosure> {
  std::string_view delim;
  explicit constexpr SplitClosure(std::string_view delim) : delim(delim) { }

  template <std::ranges::viewable_range R>
  constexpr auto operator()(R&& r) const {
    return r | std::views::split(delim)
             | std::views::transform([](auto&& r){
                  return std::string_view(r);
               });
  }
};


struct SplitAdaptor {
   constexpr auto operator()(std::string_view s) const {
      return SplitClosure(s);
   }
};
inline constexpr SplitAdaptor split2;

That makes s | split2(" ") work (in the sense that we're splitting on a space, not a space and null terminator, in addition to s | split2(" "sv).


If you copy the adaptor implementation from P2387R3, this could be:

inline constexpr adaptor split2 = 
   []<std::ranges::viewable_range R>(R&& r, std::string_view delim){
       return (R&&)r
            | std::views::split(delim)
            | std::views::transform([](auto&& sub){
                  return std::string_view(sub);
              });
   };
like image 128
Barry Avatar answered Dec 24 '25 11:12

Barry


Barry's answer is predictably great, but since you are commenting for a specific clarification...

Your code:

    return v 
    | std::views::split(";"sv) 

    // `std::string_view` constructor from Range is explicit, so this line is necessary
    | std::views::transform([](const auto& s) { return std::string_view(s); })
    | std::views::transform([](std::string_view v) { 
        // Some further logic here
        return v; 
      })
    ;

is functionally identical to:

    return v 
    | std::views::split(";"sv) 
    | std::views::transform([](auto&& range) {
        std::string_view v{range} 
        // Some further logic here
        return v; 
      })
    ;
like image 23
Drew Dormann Avatar answered Dec 24 '25 13:12

Drew Dormann