0

Sometimes there is a need to execute a particular function once and than switch to another implementation.

For example, I am really annoyed by the stream output iterator which prints delimiter after the item being printed in conjunction with the copy algorithm:

> 1,2,3,4,
         ^---- this is what happens in that case

The question isn't about nicely printing items, but more about properly concatenating them.

For example Python produces a correct result using string.join function:

','.join((1,2,3,4))
> 1,2,3,4

I also want to avoid if/else statement, because we only need once to make the switch after the first execution. So what I came up with, is:

std::function<char const*()> get_delim;

get_delim = [&get_delim]()
{
  get_delim = [](){ return ","; };
  return "";
};

for(auto b : some_byte_range)
  std::cout << get_delim() << b;

Note: b is just a byte in my case, that's why I did not use auto const&.

Now the questions:

  • Is there a way to get rid of the prior std::function<char const*()> declaration and somehow declare get_delim with auto which effectively involves self-capturing of lambda?
  • Is there may be another more expressive (like in Python) way to do that kind of join?
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
ovanes
  • 5,483
  • 2
  • 34
  • 60
  • I think [this question](https://stackoverflow.com/questions/3496982/printing-lists-with-commas-c) concerning comma-separated output has some interesting ideas – Tobias Ribizel Jul 07 '17 at 17:38
  • You wouldn't be able to use `auto` because lambda types are unique (you won't be able to assign a different lambda to such variable). But why lambdas? It's a nice trickery, but you could as well just write a simple function which constructs needed string using a plain loop with an if or something similar. – HolyBlackCat Jul 07 '17 at 17:41

1 Answers1

4

In this case, all you need is:

auto get_delim = [c=char(0)]() mutable {
    char cur = c;
    c = ',';
    return cur;
};

or even:

auto get_delim = [c=char(0)]() mutable { return std::exchange(c, ','); }

Or use "" and "," if you prefer, should be easy to see how to adjust this to suit your needs.


As far as library goes, there is a std::experimental::ostream_joiner.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • But that would execute std::exchange every time `get_delim` is called? – ovanes Jul 07 '17 at 17:47
  • @ovanes Assigning a char is pretty cheap. – Barry Jul 07 '17 at 17:48
  • Well, I know that... :) I am just trying to understand what is involved... – ovanes Jul 07 '17 at 17:50
  • But I like your solution :) It is very elegant (the second proposal with one-liner). I know about ostream_joiner, but it's not part of the standard lib right now :( – ovanes Jul 07 '17 at 17:58
  • or `auto get_delim = [first=true]() mutable { return first ? first = false, "" : ","; }` though the conditional branch is probably just as expensive as the swap. – Chris Dodd Jul 07 '17 at 20:01