Ok. here's my second attempt. It has 2 improvements: 1. allows appending to an existing string by specifiying std::string& join(onto(s), [optional separator("xxx"), parts...); 2. replaces use of std::ostringstream with a (very simple!) version that does string appending. #include <sstream> #include <iostream> #include <iomanip> struct string_ref_buffer : std::streambuf { using inherited = std::streambuf; using char_type = inherited::char_type; using char_traits = std::char_traits<char_type>; int overflow(int c) override { if (c != char_traits::eof()) { buffer_.push_back(c); } return char_traits::not_eof(c); } string_ref_buffer(std::string& buffer) : buffer_(buffer) { } const std::string& str() const & { return buffer_; } std::string&& str()&& { return std::move(buffer_); } std::string& buffer_; std::size_t inpos_ = 0; std::size_t outpos_ = 0; }; namespace detail { template<class SepStr> struct separator_object { template<class T> std::ostream& operator ()(std::ostream& s, T&& t) const { return s << sep << t; } // // other iomanp specialisations here // std::ostream& operator ()(std::ostream& s, std::ios_base&(*t)(std::ios_base&)) const { t(s); return s; } SepStr const& sep; }; struct no_separator_object { template<class T> std::ostream& operator ()(std::ostream& s, T&& t) const { return s << t; } }; template<class Target, class Separator, class...Rest> auto& join_onto(Target&& target, Separator&& sep, Rest&&...rest) { string_ref_buffer sbuf { target.str() }; std::ostream ss(std::addressof(sbuf)); using expand = int []; void(expand{0, ((sep(ss, rest)), 0)... }); return target; }; template<class Separator, class String, class...Rest> auto join(Separator&& sep, String&& s, Rest&&...rest) { std::string result {}; string_ref_buffer sbuf { result }; std::ostream ss(std::addressof(sbuf)); ss << s; using expand = int []; void(expand{0, ((sep(ss, rest)), 0)... }); return result; }; template<class String> struct onto_type { String& str() { return target_.get(); } std::reference_wrapper<String> target_; }; } template<class String> auto onto(String& target) { return detail::onto_type<String> { target }; } template<class Sep> static constexpr auto separator(Sep const& sep) { using sep_type = std::remove_const_t<std::remove_reference_t<Sep>>; return detail::separator_object<sep_type> { sep }; } template<class SepObject, class String, class...Rest> decltype(auto) join(detail::separator_object<SepObject> sep, String&& s, Rest&&...rest) { return detail::join(sep, std::forward<String>(s), std::forward<Rest>(rest)...); }; template<class String, class...Rest> decltype(auto) join(String&& s, Rest&&...rest) { return detail::join(detail::no_separator_object(), std::forward<String>(s), std::forward<Rest>(rest)...); }; template<class Target, class SepObject, class String, class...Rest> decltype(auto) join(detail::onto_type<Target> target, detail::separator_object<SepObject> sep, String&& s, Rest&&...rest) { return detail::join_onto(target, sep, std::forward<String>(s), std::forward<Rest>(rest)...); }; template<class Target, class String, class...Rest> decltype(auto) join(detail::onto_type<Target> target, String&& s, Rest&&...rest) { return detail::join_onto(target, detail::no_separator_object(), std::forward<String>(s), std::forward<Rest>(rest)...); }; int main() { auto s= std::string("foo"); s = join("Hello ", ", World.", " The hex for ", 58, " is ", std::hex, 58); std::cout << s << std::endl; s = join(separator(" : "), "a", "b", std::hex, 200 , std::quoted("banana")); std::cout << s << std::endl; join(onto(s), separator(", "), "funky", "chicken"); join(onto(s), "====="); std::cout << s << std::endl; } expected output: Hello , World. The hex for 58 is 3a a : b : c8 : "banana" a : b : c8 : "banana", funky, chicken===== On 18 January 2017 at 09:53, Richard Hodges <hodges.r@gmail.com> wrote:
That's pretty straightforward with another overload:
auto& s = join(to(y), separator(", "), "A", "b", 42);
where to(y) is something like
template<String> struct to_existing_type<String> { String& get() { return s_; } String s_; };
template<class String> auto to(String& s) { return to_existing_type<S>(s); }
With a bit of template unwrapping, we could imagine something like this:
join(to(x), 2, 3, to(y), "foo", "bar", create(), "baz", 42);
which would return a tuple:
std::tuple<std::string&, std::string&, std::string>
in c++17 this would allow:
auto&& [x, y, z] = join(to(x), 2, 3, to(y), "foo", "bar", create(), "baz", 42);
But this maybe taking it a bit far... What do you think?
On 18 January 2017 at 09:06, Olaf van der Spek <ml@vdspek.org> wrote:
On Mon, Jan 16, 2017 at 11:41 AM, Richard Hodges <hodges.r@gmail.com> wrote:
Sorry to chime in so late in the discussion.
What about a syntax similar to this?
int main() { auto s = join("Hello ", ", World.", " The hex for ", 58, " is ", std::hex, 58); std::cout << s << std::endl;
s = join(separator(" : "), "a", "b", std::hex, 200 , std::quoted("banana")); std::cout << s << std::endl;
}
Which would produce the following output:
Hello , World. The hex for 58 is 3a a : b : c8 : “banana"
The syntax is fine but it's missing an appending variant, like append(s, "A", "B", 42); This variant is important as it (also) allows you to reuse existing storage.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman /listinfo.cgi/boost