Hi, Am 18.01.2017 17:30, schrieb Richard Hodges:
Totally agree with returning a string factory. That makes perfect sense. onto(x) could return the correct kind of wrapper, depending on the argument type of x. So it could cope with x being for example, std::string&, std::string const&, std::string&& or std::ostream&.
How about this: auto s = concat(1, " ", 2).str(); // -> s = "1 2" concat(" ", 3).append_to(s); // -> s = "1 2 3" // reuse preallocated memory concat(4, " ", 5).overwrite(s); // -> s = "4 5" overwrite() could also take a std::string_view with C++17, or const char* and size_t with earlier versions of the standard library.
As an observation, expressing the join as an iterator pair lends itself to being implemented in terms of std::copy(first, last, formatting_iterator<...>).
That definatelly is a possible implementation, yes. Though, of course having join(), concat() and maybe other functions return string factories instead of strings, enables generating the whole string in a single buffer without copying. concat(join(...), " ", 42, " - ", join(...), format("format string %1% %2% %3%", a, b, 42)).str(); concat() will allocate a long enough string and call overwrite() on the results of join() and format() with string views on that string. Taking everything together, a string factory will have at least interface like this: template<typename T> constexpr bool string_factory() { return requires(T a, std::string& s, std::string_view v, const char* p, size_t len) { // estimate necessary memory to render the string { a.size() } const -> size_t; // render and return result { a.str() } const -> std::string; // render at the end of an existing string - return the number of generated chars { a.append_to(s) } -> size_t; // render into an existing string, reusing its preallocated memory { a.overwrite(s) } -> size_t; // render into a string view { a.overwrite(v) } -> size_t; // render into a character buffer { a.overwrite(p, len) } -> size_t; }; } I hope, the constraint syntax is more or less correct. I haven't used constraints in real code up to now ;-)
I think this is good for containers, but for a series of disjoint types, or for joining words (as opposed to letters), you'd still need some templatery.
Yes, sure. You might also want to have somthing like this:
std::tuple
boost::range springs to mind as a reasonable helper for expressing to concat (or join) that you want to treat each element of a container.
Yes, when I wrote about my idea of join(), I thought of ranges as well. With range adaptors, that will make up for a very powerfull and btw. fast library to generate strings: format("file names and sizes:\n%1%\n", join(separator('\n'), my_files | range::transformed([](const std::filesystem::path& f) -> auto { return concat(separator(": "), f.filename(), std::filesystem::file_size(f)); })).str(); format().str() will ask the join() string factory to render into the preallocated buffer. Then join() will walk through its range and find, that it has a range string factories, returned by concat(). Therefore it asks every string factory to render to the given buffer. We "just" need format(), join(), concat(), and the corresponding string factories. Christof