Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

range-v3: Joining piped ranges with a delimeter

Tags:

c++

range-v3

I'm trying to build a basic demo of the range-v3 library: take some integers, filter out odd values, stringify them, then join those into a comma-separated list. For example, { 8, 6, 7, 5, 3, 0, 9 } becomes "8, 6, 0". From reading the docs and going through examples, it seems like the naïve solution would resemble:

string demo(const vector<int>& v)
{
    return v |
        ranges::view::filter([](int i) { return i % 2 == 0; }) |
        ranges::view::transform([](int i) { return to_string(i); }) |
        ranges::view::join(", ");
}

but building on Clang 7 fails with a static assertion that one, "Cannot get a view of a temporary container". Since I'm collecting the result into a string, I can use the eager version - action::join - instead:

string demo(const vector<int>& v)
{
    return v |
        ranges::view::filter([](int i) { return i % 2 == 0; }) |
        ranges::view::transform([](int i) { return to_string(i); }) |
        ranges::action::join;
}

but the eager version doesn't seem to have an overload that takes a delimiter.

Interestingly, the original assertion goes away if you collect join's inputs into a container first. The following compiles and runs fine:

string demo(const vector<int>& v)
{
    vector<string> strings = v |
        ranges::view::filter([](int i) { return i % 2 == 0; }) |
        ranges::view::transform([](int i) { return to_string(i); });
    return strings | ranges::view::join(", ");
}

but this totally defeats the principle of lazy evaluation that drives so much of the library.

Why is the first example failing? If it's not feasible, can action::join be given a delimiter?

like image 416
Matt Kline Avatar asked Oct 27 '25 16:10

Matt Kline


2 Answers

action::join should accept a delimiter. Feel free to file a feature request. The actions need a lot of love.

like image 70
Eric Niebler Avatar answered Oct 30 '25 07:10

Eric Niebler


Some changes since the original question have resolved this.

  • The original problem of views on a temporary container has been patched in the ranges-v3 library. Related SO question Commit

  • Aside: The namespaces have been renamed to ranges::views and ranges::actions

I've updated your two examples:

"Lazy" version

But your demo above doesn't compile because ranges::views::join() returns a view over the individual characters of the joined strings. You can use the ranges-v3 to() utility to join to a string

string demo1(const vector<int>& v)
{
    return v |
        ranges::views::filter([](int i) { return i % 2 == 0; }) | // filtered view over int
        ranges::views::transform([](int i) { return to_string(i); }) | // view over strings
        ranges::views::join(", ") |  //  view over chars
        ranges::to<string>; // flattened to a string
}

"Eager" version

The eager version works but actions::join still doesn't accept a delimiter. You can add one using ranges::view::intersperse(", ").

string demo2(const vector<int>& v)
{
    return v |
        ranges::views::filter([](int i) { return i % 2 == 0; }) |
        ranges::views::transform([](int i) { return to_string(i);}) |
        ranges::views::intersperse(", ") |  // view over strings 
        ranges::actions::join; // joined to a string
}
like image 43
DanHorner Avatar answered Oct 30 '25 06:10

DanHorner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!