|
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