Boost logo

Boost :

Subject: Re: [boost] Variadic append for std::string
From: Richard Hodges (hodges.r_at_[hidden])
Date: 2017-01-18 04:59:32


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_at_[hidden]> 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_at_[hidden]> wrote:
>
>> On Mon, Jan 16, 2017 at 11:41 AM, Richard Hodges <hodges.r_at_[hidden]>
>> 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
>>
>
>


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk