I would like to write a function that will take an stl container (like set, vector or list) and then iterate over the contents and then append them to a string and give back the string.
Something like this.
开发者_如何学C// I dont know how to do this. Just using stl::container for meanings sake Not sure if such a thing exists?
template<typename T, typename Container = stl::container<T> >
void JoinToString(const Container<T> cont, const char * delim, string &str)
{
stringstream s;
Container<T>::const_iterator it = cont.begin, last = cont.end();
while(it != last)
{
s<<(*it);
++it;
if(it == last)
break;
s<<delim;
}
str = s.str();
}
I want something to this effect. Not sure how to write such a code.
The STL style is to pass in begin
and end
iterators to any algorithm, not the container itself: this keeps things general, and allows the use of native vectors with pointers. General C++ style considerations would also suggest returning a std::string
instead of using a reference parameter.
You need to decide what you want. You can either pass a type or a template. Not both. In the code you posted you declare Container
to a be type, but use it as a template.
template<typename T, typename Container = vector<T> >
void test() { Container x; };
template<typename T, template <typename> class Container = vector >
void test() { Container<T> x; }
If you really need access to the container, then this will do what you want:
template<typename Container>
void JoinToString(const Container& cont, const char * delim, string &str)
{
typedef typename Container::value_type T;
...
}
However, it's more idiomatic to use an iterator range like this:
template<typename FwdIt>
void JoinToString(FwdIt it, FwdIt end, const char * delim, string &str)
{
typedef typename std::iterator_traits<Container::iterator>::value_type T;
while(it != end)
{
...
}
}
Another solution which does exactly what you want is boost::algorithm::join
:
This algorithm joins all strings in a 'list' into one long string. Segments are concatenated by given separator.
Example of use :
#include <boost/algorithm/string/join.hpp>
#include <boost/assign/list_of.hpp>
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> v = boost::assign::list_of("A")("B")("C");
std::cout << boost::algorithm::join(v, "/") << std::endl;
}
Outputs : A/B/C
Have a look at remove char from stringstream and append some data
Such a function does not exist.
This is a working example,
template<typename T>
std::string JoinToString(const T& cont, const char* delim, std::string &str)
{
std::stringstream s;
T::const_iterator it= cont.begin();
T::const_iterator last= cont.end();
while(it != last)
{
s << (*it);
++it;
s << delim;
if (it == last)
break;
}
return s.str() + str;
}
int main()
{
std::string s("String! ");
std::vector<std::string> v(1, "String!, String!");
std::cout << JoinToString(v, ", ", s) << "\n";
std::list<std::string> l(1, "String!, String!");
std::cout << JoinToString(l, ", ", s);
}
There are a few things worth noting though.
You could use template<template<class> class T
, although it might cause problems, depending on the amount of template arguments the container has.
I would like to note (for future reference), if you want to plug in a type into a class template, e.g. a std::string as a template argument into a std::vector the safest solution is,
template<class T>
struct something
{
typedef typename boost::mpl::apply<T, std::string>::type type;
};
something<std::vector<boost::mpl::placeholders::_1>>::type;
The reason this is safer, than using a template<template<class> class T
, is that it will allow more customisation from the user side and will work on class templates with any amount of arguments/default arguments.
Your solution is almost correct. Just do this:
template<typename Container >
string JoinToString(const Container & cont, const string &delim)
{
stringstream s;
for (Container::const_iterator it = cont.begin(); it != cont.end(); it++ )
{
s<<(*it);
if ( (it+1) != cont.end() )
s<<delim;
}
return s.str();
}
Better function would be this:
template<typename FwdIt>
string JoinToString(FwdIt from, FwdIt to, const string &delim)
{
stringstream s;
for (; from != to; from++ )
{
s<<(*from);
if ( (from+1) != to )
s<<delim;
}
return s.str();
}
Using this decide from
and to
using which to join the elements!
Create custom output iterator:
struct append_to_string_with_delim
: std::iterator<std::output_iterator_tag, void, void, void, void>
{
append_to_string_with_delim(std::string &ss, char const *dd) : s(ss), d(dd)
{
}
template<typename T>
append_to_string_with_delim &operator=(T const &t)
{
std::ostringstream o;
o << t;
s += o.str();
s += d;
return(*this);
}
append_to_string_with_delim &operator*()
{
return(*this);
}
append_to_string_with_delim &operator++()
{
return(*this);
}
append_to_string_with_delim const &operator++(int)
{
return(*this);
}
std::string &s;
char const *const d;
};
and use std::copy:
std::vector<int> v;
std::string s("The v vector elements are: ");
...
copy(v.begin(), v.end(), append_to_string_with_delim(s, " "));
精彩评论