Is there anything wrong with this approach? Should I be using a stringstream or just concatenating strings?
I use a different approach: I have created a class similar to std::ostream_iterator (see the utility functions at the end of the code for example use):
#pragma once
#include <iosfwd>
#include <iterator>
#include <sstream>
namespace stdex {
/**
* @brief Implements join functionality directing the output into an ostream
*
* This class provides an iterator for writing into an ostream. The type T is
* the only type written by this iterator and there must be an
* operator << (T) defined.
*
* @code
*
* template<typename T> using join = ostream_join_iterator<T>;
* std::ostringstream out;
* std::vector<std::string> strings{"alpha", "beta", "gamma"};
* std::copy(strings.begin(), strings.end(), join<string>{out, ", "});
* assert(out.str() == "alpha, beta, gamma");
*
* @endcode
*
* @remarks This class implements functionality similar to
* std::ostream_iterator except it does not print a separator after the
* last element.
*
* @param T The type to write to the ostream.
* @param C The ostream char_type.
* @param Tr The ostream char_traits.
*/
template<typename T, typename C=char, typename Tr=std::char_traits<C> >
class ostream_join_iterator
: public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
typedef C char_type;
typedef Tr traits_type;
typedef std::basic_string<C, Tr> splitter_type;
typedef std::basic_ostream<C, Tr> ostream_type;
private:
ostream_type *out_;
splitter_type splitter_;
bool at_first_;
public:
ostream_join_iterator(ostream_type& out)
: out_{&out}
, splitter_{}
, at_first_{true}
{
}
/**
* Construct from an ostream.
*
* @remarks The delimiter is not copied, and thus must not be destroyed
* while this iterator is in use.
*
* @param out Underlying ostream to write to.
* @param splitter CharT delimiter string to insert.
*/
ostream_join_iterator(ostream_type& out, const char_type* splitter)
: out_{&out}
, splitter_{splitter}
, at_first_{true}
{
}
/// Copy constructor.
ostream_join_iterator(const ostream_join_iterator& other)
: out_{other.out_}
, splitter_{other.splitter_}
, at_first_{other.at_first_}
{
}
ostream_join_iterator& operator=(const T& value)
{
if((not splitter_.empty()) and (not at_first_))
*out_ << splitter_;
*out_ << value;
at_first_ = false;
return *this;
}
ostream_join_iterator& operator=(ostream_join_iterator temp)
{
using std::swap;
swap(out_, temp.out_);
swap(splitter_, temp.splitter_);
swap(at_first_, temp.at_first_);
return *this;
}
ostream_join_iterator&
operator*() {
return *this;
}
ostream_join_iterator&
operator++() {
return *this;
}
ostream_join_iterator&
operator++(int) {
return *this;
}
};
// join implementation with string separator
template<typename It, typename C, typename Tr, typename A>
void join(const It& begin, const It& end,
const std::basic_string<C,Tr,A>& separator, std::basic_ostream<C,Tr>& out)
{
std::copy(begin, end,
ostream_join_iterator<std::remove_reference<decltype(*begin)>>{out, separator});
}
template<typename It, typename C, typename Tr, typename A>
std::basic_string<C,Tr,A> join(const It& begin, const It& end,
const std::basic_string<C,Tr,A>& separator)
{
std::basic_ostringstream<C,Tr,A> buffer{};
join(begin, end, separator, buffer);
return buffer.str();
}
template<typename S, typename C, typename Tr, typename A>
void join(const S& sequence,
const std::basic_string<C,Tr,A>& separator,
std::basic_ostream<C,Tr>& out)
{
join(std::begin(sequence), std::end(sequence), separator, out);
}
template<typename S, typename C, typename Tr, typename A>
std::basic_string<C,Tr,A> join(
const S& sequence,
const std::basic_string<C,Tr,A>& separator)
{
std::basic_ostringstream<C,Tr,A> buffer{};
join(std::begin(sequence), std::end(sequence), separator, buffer);
return buffer.str();
}
// join implementation with character literal separator
template<typename It>
void join(const It& begin, const It& end,
const char separator[], std::ostream& out)
{
typedef decltype(*begin) iterated;
std::copy(begin, end, ostream_join_iterator<iterated>{out, separator});
}
template<typename It>
std::string join(const It& begin, const It& end, const char separator[])
{
std::ostringstream buffer{};
join(begin, end, separator, buffer);
return buffer.str();
}
} // stdex