On 24 Jan 2017, at 12:54, Christof Donat
wrote: That is the adaptor API from boost::range and is the same in ranges::v3. It's not my invention.
I didn't have time to look into boost::range, yet, so I didn't recognise this. Well that's a pity. :((
I think, that composes less elegantly with boost::range or ranges::v3. Maybe we could lean towards their adaptor APIs like this:
join(separator(" "), my_nums | hex<int>()));
If you use the design with the unary function in the beginning, it works for boost::range and classic iterators. You can still compose several unary functions by using a lambda.
OK, here you made a point. In a specification I'd leave the behavior undefined. In an actual implementation I'd have the later ones overwrite the previous ones, because I expect that to be easy to implement. We could also try and prevent that with meta programming, but I think, that is not worth the effort. Leaving it unspecified in the specification, still leaves us the option to do that later.
If you can catch the error it at compile time, so that it costs nothing at runtime, it is certainly worth the effort.
Still separator("...") is a bit out of the picture now. Up to mow I thought, that it is some kind of formatting information and therefore I handled it like other formatting tags. Actually I begin to like the idea of formatting functions, that return string factories. That fits very nicely with the rest of the API and makes concat(), join() and format() simpler.
A completely different approach, inspired by Python:
separator(" ").join(my_nums | hex<int>());
-1. It looks artificial in C++. In Python it is okay, because it creates a nice symmetry with .split(…). Here, there is no symmetry with .split. And any case, both should be methods of std::string then. For a user it will feel quite arbitrary that he/she has do use separator(" ").join(…) and instead of the simpler std::string(" ").join(…).