Does anyone have an idea as to why
#include <set>
#include <string>
#include <iostream>
template <typename T>
std::string set2Str(const std::set<T> & S)
{
std::string retstr = "{";
typename std::set<T>::const_iterator it(S.begin()), offend(S.end());
if (it != offend)
retstr.push_back(*it++);
while (it != offend)
{
retstr.push_back(',');
retstr.push_back(*it++);
}
retstr.push_back('}');
return retstr;
}
int main()
{
std::set<int> mySet = {1, 5, 9, 69};
std::cout << set2Str(mySet);
}
is outputting
{,, ,E}
????
Also, is there a more elegant way of writing the function set2Str? The fencepost problem with the commas makes my procedure ugly.
When your algorithm does this:
retstr.push_back(*it++);
The values being punched into your target string are treated as (and converted if possible to) char. But {1, 5, 9, 69} doesn't contain char; it contains int. The result is treating them as ASCII code points, See this table, and pay particular attention to the dec value of each character therein. Note the value for E, for example.
This is one of the many purposes std::ostringstream was made for, and has the benefit of allowing anything that can be written to a character stream be representable, including utilizing custom insertion operators.
#include <iostream>
#include <sstream>
#include <string>
#include <set>
#include <tuple>
template<typename T, typename... Args>
std::string set2str(const std::set<T,Args...>& obj)
{
std::ostringstream oss;
oss << '{';
auto it = obj.cbegin();
if (it != obj.cend())
{
oss << *it++;
while (it != obj.cend())
oss << ',' << *it++;
}
oss << '}';
return oss.str();
}
// custom class to demonstrate custom insertion support
class Point
{
friend std::ostream& operator <<(std::ostream& os, const Point& pt)
{
return os << '(' << pt.x << ',' << pt.y << ')';
}
private:
double x,y;
public:
Point(double x, double y) : x(x), y(y) {}
// used by std::less<> for set ordering
bool operator <(const Point& pt) const
{
return std::tie(x,y) < std::tie(pt.x,pt.y);
}
};
int main()
{
std::set<int> si = { 1,2,3,4,5 };
std::set<double> sd = { 1.1, 2.2, 3.3, 4.4, 5.5 };
std::set<char> sc = { 'a', 'b', 'c', 'd', 'e' };
std::set<unsigned> empty;
std::cout << set2str(si) << '\n';
std::cout << set2str(sd) << '\n';
std::cout << set2str(sc) << '\n';
std::cout << set2str(empty) << '\n';
// using custom class with implemented ostream inseter
std::set<Point> pts { {2.2, 3.3}, {1.1, 2.2}, {5.5, 4.4}, {1.1, 3.3} };
std::cout << set2str(pts) << '\n';
return EXIT_SUCCESS;
}
Output
{1,2,3,4,5}
{1.1,2.2,3.3,4.4,5.5}
{a,b,c,d,e}
{}
{(1.1,2.2),(1.1,3.3),(2.2,3.3),(5.5,4.4)}
You will need to convert *it++ to a string - for example using std::to_string. Otherwise, it might be a char and gets printed as such.
For example, in your case, you just add 69, which is the Ascii Code for E. More likely, you want "69", which you would get with std::to_string(69).
You declared the type to be in an int. The numbers are being read as ASCII. They aren't being converted to a string type.
retstr.push_back(*it++);
this line sees the ANSI/ASCII capital E not the characters '6' and ' 5'
Using sprintf which you might not like but a way.
push_back of an int to a string is your problem.
Actually this won't work if your type is not an int so you would need to detect the type used. Or better would be to use stringstream and convert that way.
#include <set>
#include <string>
#include <iostream>
template <typename T>
std::string set2Str(const std::set<T> & S)
{
std::string retstr = "{";
char buffer[20] = {0};
typename std::set<T>::const_iterator it(S.begin()), offend(S.end());
if (it != offend) {
sprintf(buffer, "%u", *it++);
retstr.append(buffer);
}
while (it != offend)
{
retstr.push_back(',');
sprintf(buffer, "%u", *it++);
retstr.append(buffer);
}
retstr.push_back('}');
return retstr;
}
int main()
{
std::set<int> mySet; // // = {1, 5, 9, 69}; - didn't work with my compiler
mySet.insert(1);
mySet.insert(5);
mySet.insert(9);
mySet.insert(69);
std::cout << set2Str(mySet);
}
Related
I want to check whether an element exists in the vector or not. I know the below piece of code will check it.
#include <algorithm>
if ( std::find(vector.begin(), vector.end(), item) != vector.end() )
std::cout << "found";
else
std::cout << "not found";
But I have the vector of any type. i.e. std::vector<std::any>
I am pushing elements into vector like this.
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
So I need to find whether string "A" present in the vector or not. Can std::find help here?
As of now I am using below piece of code to do this
bool isItemPresentInAnyVector(std::vector<std::any> items, std::any item)
{
for (const auto& it : items)
{
if (it.type() == typeid(std::string) && item.type() == typeid(std::string))
{
std::string strVecItem = std::any_cast<std::string>(it);
std::string strItem = std::any_cast<std::string>(item);
if (strVecItem.compare(strItem) == 0)
return true;
}
else if (it.type() == typeid(int) && item.type() == typeid(int))
{
int iVecItem = std::any_cast<int>(it);
int iItem = std::any_cast<int>(item);
if (iVecItem == iItem)
return true;
}
else if (it.type() == typeid(float) && item.type() == typeid(float))
{
float fVecItem = std::any_cast<float>(it);
float fItem = std::any_cast<float>(item);
if (fVecItem == fItem)
return true;
}
}
return false;
}
This should work good I guess:
#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>
int main(){
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
int i = 10;//you can use any type for i variable and it should work fine
//std::string i = "A";
auto found = std::find_if(temp.begin(), temp.end(), [i](const auto &a){
return typeid(i) == a.type() && std::any_cast<decltype(i)>(a) == i;
} );
std::cout << std::any_cast<decltype(i)>(*found);
}
Or to make the code a bit more generic and reusable:
#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>
auto any_compare = [](const auto &i){
return [i] (const auto &val){
return typeid(i) == val.type() && std::any_cast<decltype(i)>(val) == i;
};
};
int main(){
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
//int i = 10;
std::string i = "A";
auto found = std::find_if(temp.begin(), temp.end(), any_compare(i));
std::cout << std::any_cast<decltype(i)>(*found);
}
Live demo
Important note: this is guaranteed to work only within single translation unit due to stadard requirements on std::any type (for example same types don't need to have same type identifier in different translation units)
Using an any for this kind of purpose is not a good use of any. The best way to go is just to use a variant - since you have a closed set of types:
struct Equals {
template <typename T>
constexpr bool operator()(T const& a, T const& b) const { return a == b; }
template <typename T, typename U>
constexpr bool operator()(T const& a, U const& b) const { return false; }
};
using V = std::variant<int, float, std::string>
bool isItemPresentInAnyVector(std::vector<V> const& items, V const& item)
{
auto it = std::find_if(items.begin(), items.end(), [&](V const& elem){
return std::visit(Equals{}, elem, item);
});
return it != items.end();
}
Actually it's even better, because as Kilian points out, variant's operator== already works exactly like this:
using V = std::variant<int, float, std::string>
bool isItemPresentInAnyVector(std::vector<V> const& items, V const& item)
{
return std::find(items.begin(), items.end(), item) != items.end();
}
Unfortunately if you want to find an std::any instance in a vector of std::any instances the answer is no.
std::any does need some "magic" for example to be able to handle the creation of unknown object types but this machinery is private and must only supports object creation and not equality comparison.
It would be possible to implement what you are looking for using the same approach, but not with standard std::any that doesn't publish the needed details. The "manager" template needs to enumerate all possible operations and, for example, in g++ implementation they're "access", "get_type_info", "clone", "destroy", "xfer".
variant is completely different, because explicitly lists all the allowed types and therefore in any place it's used can access all the methods.
Comparison with typeId() should be avoided since it's dependent from translation unit.
A much safer approach can be used with any_cast of pointers:
template<typename T>
std::optional<T> find(const std::vector<std::any>& v)
{
for(auto&& e : v){
if(auto ptr = std::any_cast<T>(&e)){
return *ptr;
}
}
return std::nullopt;
}
Find first element with the given type, or nullopt if it's not found.
If we want to find all element with a specific instead:
template<typename T>
std::vector<T> findAll(const std::vector<std::any>& v)
{
std::vector<T> out;
for(auto&& e : v){
if(auto ptr = std::any_cast<T>(&e)){
out.push_back(*ptr);
}
}
return out;
}
Usage:
int main()
{
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
temp.emplace_back(12);
temp.emplace_back(std::string("B"));
auto outInt = findAll<int>(temp);
std::cout << "out int: " << outInt.size() << std::endl;
for(auto&& out : outInt)
std::cout << out << std::endl;
auto outString = findAll<std::string>(temp);
std::cout << "out string: " << outString.size() << std::endl;
for(auto&& out : outString)
std::cout << out << std::endl;
auto singleInt = find<int>(temp);
if(singleInt)
std::cout << "first int " << *singleInt << std::endl;
auto singleBool = find<bool>(temp);
if(!singleBool)
std::cout << "ok: bool not found" << std::endl;
}
LIVE DEMO
If the types are int, float and string (or a limited set of types), you can use a combination of std::variant and std::get_if to achieve what you want to do in a simple manner:
std::get_if is to determine which of the types is stored in the std::variant.
A minimal example:
#include <iostream>
#include <vector>
#include <string>
#include <variant>
int main(){
std::vector<std::variant<int, float, std::string>> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
for (const auto& var: temp) {
if(std::get_if<std::string>(&var)) {
if(std::get<std::string>(var) == "A") std::cout << "found string\n";
}
if(std::get_if<int>(&var)) {
if(std::get<int>(var) == 10) std::cout << "found int\n";
}
if(std::get_if<float>(&var)) {
if(std::get<float>(var) == 3.14f) std::cout << "found float\n";
}
}
}
Live Demo
Is there any shortcut method in c++ to output 2d array(i.e. apart from for loop)?
Is there a special function in STL to output it.
Well, since you mentioned STL functions, you could use the std::for_each function with lambda functions to print the 2D array:
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
int matrix[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
auto elem_printer = [](int num) { std::cout << num << " "; };
auto row_printer = [&elem_printer](int (&row)[3]) {
std::for_each(std::begin(row),std::end(row),elem_printer);
std::cout << std::endl;
};
std::for_each(std::begin(matrix),std::end(matrix),row_printer);
}
However, this is exactly the same as two for loops, but uglier.
I have this template for streams, which hides away some of the ugliness, and you benefit from it being reusable and it handles multiple dimensions. There is no way to get away without doing the loops somewhere, of course:
template <class Stream, size_t depth>
class Pretty
{
Stream& s;
public:
Pretty(Stream& s): s(s) {}
template <size_t d1, typename T>
Stream& operator <<( T const (&v)[d1])const
{
const char* sep = "{";
for (auto& m : v)
{
s << sep << m;
sep = ", ";
}
s << "}";
return s;
}
template <size_t d1, typename T, size_t d2>
std::ostream& operator <<(T const (&v)[d1][d2])const
{
enum {DENT = 4};
std::string dent (DENT,' ');
std::string indent(depth*DENT,' ');
std::string sep = "{\n" + indent + dent;
for (auto& m : v)
{
s << sep; Pretty<Stream,depth+1>(s) << m;
sep = ",\n" + indent + dent;
}
s << "\n" << indent << "}";
return s;
}
};
class PrettyNext
{};
Pretty<std::ostream,0> operator << (std::ostream& s, const PrettyNext&)
{
return Pretty<std::ostream,0>(s);
}
And usage:
int i [][3][2] = { { {1,2}, {3,4}, {5,6} },{{0}}};
std::cout << "This is a test:\n" << PrettyNext() << i << std::endl;
Output is:
This is a test:
{
{
{1, 2},
{3, 4},
{5, 6}
},
{
{0, 0},
{0, 0},
{0, 0}
}
}
I have been fighting to get this to work directly on std::ostream, but there is a collision with the standard char* handling I can't quite resolve.
I want use Boost.Spirit.Lex to lex a binary file; for this purpose I wrote the following program (here is an extract):
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <fstream>
#include <iterator>
#include <string>
namespace spirit = boost::spirit;
namespace lex = spirit::lex;
#define X 1
#define Y 2
#define Z 3
template<typename L>
class word_count_tokens : public lex::lexer<L>
{
public:
word_count_tokens () {
this->self.add
("[^ \t\n]+", X)
("\n", Y)
(".", Z);
}
};
class counter
{
public:
typedef bool result_type;
template<typename T>
bool operator () (const T &t, size_t &c, size_t &w, size_t &l) const {
switch (t.id ()) {
case X:
++w; c += t.value ().size ();
break;
case Y:
++l; ++c;
break;
case Z:
++c;
break;
}
return true;
}
};
int main (int argc, char **argv)
{
std::ifstream ifs (argv[1], std::ios::in | std::ios::binary);
auto first = spirit::make_default_multi_pass (std::istream_iterator<char> (ifs));
auto last = spirit::make_default_multi_pass (std::istream_iterator<char> ());
size_t w, c, l;
word_count_tokens<lex::lexertl::lexer<>> word_count_functor;
w = c = l = 0;
bool r = lex::tokenize (first, last, word_count_functor, boost::bind (counter (), _1, boost::ref (c), boost::ref (w), boost::ref (l)));
ifs.close ();
if (r) {
std::cout << l << ", " << w << ", " << c << std::endl;
}
return 0;
}
The build returns the following error:
lexer.hpp:390:46: error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type
Now, the error is due to definition of concrete lexer, lex::lexer<>; in fact its first parameter is defaulted to const char *. I obtain the same error also if I use spirit::istream_iterator or spirit::make_default_multi_pass (.....).
But if I specify the correct template parameters of lex::lexer<> I obtain a plethora of errors!
Solutions?
Update
I have putted all source file; it's the word_counter site's example.
Okay, since the question was changed, here's a new answer, addressing some points with the complete code sample.
Firstly, you need to use a custom token type. I.e.
word_count_tokens<lex::lexertl::lexer<lex::lexertl::token<boost::spirit::istream_iterator>>> word_count_functor;
// instead of:
// word_count_tokens<lex::lexertl::lexer<>> word_count_functor;
Obviously, it's customary to typedef lex::lexertl::token<boost::spirit::istream_iterator>
You need to use min_token_id instead of token IDs 1,2,3. Also, make it an enum for ease of maintenance:
enum token_ids {
X = lex::min_token_id + 1,
Y,
Z,
};
You can no longer just use .size() on the default token value() since the iterator range is not RandomAccessRange anymore. Instead, employ boost::distance() which is specialized for iterator_range:
++w; c += boost::distance(t.value()); // t.value ().size ();
Combining these fixes: Live On Coliru
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/bind.hpp>
#include <fstream>
namespace spirit = boost::spirit;
namespace lex = spirit::lex;
enum token_ids {
X = lex::min_token_id + 1,
Y,
Z,
};
template<typename L>
class word_count_tokens : public lex::lexer<L>
{
public:
word_count_tokens () {
this->self.add
("[^ \t\n]+", X)
("\n" , Y)
("." , Z);
}
};
struct counter
{
typedef bool result_type;
template<typename T>
bool operator () (const T &t, size_t &c, size_t &w, size_t &l) const {
switch (t.id ()) {
case X:
++w; c += boost::distance(t.value()); // t.value ().size ();
break;
case Y:
++l; ++c;
break;
case Z:
++c;
break;
}
return true;
}
};
int main (int argc, char **argv)
{
std::ifstream ifs (argv[1], std::ios::in | std::ios::binary);
ifs >> std::noskipws;
boost::spirit::istream_iterator first(ifs), last;
word_count_tokens<lex::lexertl::lexer<lex::lexertl::token<boost::spirit::istream_iterator>>> word_count_functor;
size_t w = 0, c = 0, l = 0;
bool r = lex::tokenize (first, last, word_count_functor,
boost::bind (counter (), _1, boost::ref (c), boost::ref (w), boost::ref (l)));
ifs.close ();
if (r) {
std::cout << l << ", " << w << ", " << c << std::endl;
}
}
When run on itself, prints
65, 183, 1665
I think the real problem is not shown. You don't show first or last and I have a feeling you might have temporaries there.
Here's a sample I came up with to verify, perhaps you can see what it is you're doing ---wrong--- differently :)
Live on Coliru (memory mapped an byte-vector, via const char*)
And this alternative (using spirit::istream_iterator)
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <fstream>
#ifdef MEMORY_MAPPED
# include <boost/iostreams/device/mapped_file.hpp>
#endif
namespace /*anon*/
{
namespace qi =boost::spirit::qi;
namespace lex=boost::spirit::lex;
template <typename Lexer>
struct mylexer_t : lex::lexer<Lexer>
{
mylexer_t()
{
fileheader = "hello";
this->self = fileheader
| space [ lex::_pass = lex::pass_flags::pass_ignore ];
}
lex::token_def<lex::omit>
fileheader, space;
};
template <typename Iterator> struct my_grammar_t
: public qi::grammar<Iterator>
{
template <typename TokenDef>
my_grammar_t(TokenDef const& tok)
: my_grammar_t::base_type(header)
{
header = tok.fileheader;
BOOST_SPIRIT_DEBUG_NODE(header);
}
private:
qi::rule<Iterator> header;
};
}
namespace /* */ {
std::string safechar(char ch) {
switch (ch) {
case '\t': return "\\t"; break;
case '\0': return "\\0"; break;
case '\r': return "\\r"; break;
case '\n': return "\\n"; break;
}
return std::string(1, ch);
}
template <typename It>
std::string showtoken(const boost::iterator_range<It>& range)
{
std::ostringstream oss;
oss << '[';
std::transform(range.begin(), range.end(), std::ostream_iterator<std::string>(oss), safechar);
oss << ']';
return oss.str();
}
}
bool parsefile(const std::string& spec)
{
#ifdef MEMORY_MAPPED
typedef char const* It;
boost::iostreams::mapped_file mmap(spec.c_str(), boost::iostreams::mapped_file::readonly);
char const *first = mmap.const_data();
char const *last = first + mmap.size();
#else
typedef char const* It;
std::ifstream in(spec.c_str());
in.unsetf(std::ios::skipws);
std::string v(std::istreambuf_iterator<char>(in.rdbuf()), std::istreambuf_iterator<char>());
It first = &v[0];
It last = first+v.size();
#endif
typedef lex::lexertl::token<It /*, boost::mpl::vector<char, unsigned int, std::string> */> token_type;
typedef lex::lexertl::actor_lexer<token_type> lexer_type;
typedef mylexer_t<lexer_type>::iterator_type iterator_type;
try
{
static mylexer_t<lexer_type> mylexer;
static my_grammar_t<iterator_type> parser(mylexer);
auto iter = mylexer.begin(first, last);
auto end = mylexer.end();
bool r = qi::parse(iter, end, parser);
r = r && (iter == end);
if (!r)
std::cerr << spec << ": parsing failed at: \"" << std::string(first, last) << "\"\n";
return r;
}
catch (const qi::expectation_failure<iterator_type>& e)
{
std::cerr << "FIXME: expected " << e.what_ << ", got '";
for (auto it=e.first; it!=e.last; it++)
std::cerr << showtoken(it->value());
std::cerr << "'" << std::endl;
return false;
}
}
int main()
{
if (parsefile("input.bin"))
return 0;
return 1;
}
For the variant:
typedef boost::spirit::istream_iterator It;
std::ifstream in(spec.c_str());
in.unsetf(std::ios::skipws);
It first(in), last;
See my code below:(you can compile the code with: g++-4.7 demo.cpp -std=c++11 -lmsgpack)
#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include <msgpack.hpp>
using namespace std;
template<class T>
void pack(T &t, string &str)
{
using namespace msgpack;
sbuffer buff;
pack(buff, t);
ostringstream is;
is << buff.size() << buff.data();
str = string(is.str());
}
template<class T>
T unpack(string &str)
{
using namespace msgpack;
unpacked result;
istringstream ss(str);
int len;
string buff;
ss >> len >> buff;
unpack(&result, buff.c_str(), len);
auto obj = result.get();
return obj.as<T>();
}
int main(int argc, char *argv[]) {
vector<float> t = {1., 2., 3., 4., 5.};
string s;
pack(t, s);
auto r = unpack<vector<float> >(s);
for(auto & v : r)
std::cout << v << std::endl;
// vector<int> is right
/*
vector<int> t = {1, 2, 3, 4, 5};
string s;
pack(t, s);
auto r = unpack<vector<int> >(s);
for(auto & v : r)
std::cout << v << std::endl;
*/
return 0;
}
There is a strange bug that 'vector', 'vector', 'int', 'double' can work in the above encapsulation, while 'vector', 'vector' can not.
runtime error shows below:
terminate called after throwing an instance of 'msgpack::type_error'
what(): std::bad_cast
but in the below encapsulation, any type can work well:
template<class T>
void pack(T &t, msgpack::sbuffer sbuf) {
pack(&sbuf, t);
}
template<class T>
T unpack(const msgpack::sbuffer & sbuf) {
msgpack::unpacked msg;
msgpack::unpack(&msg, sbuf.data(), sbuf.size());
auto obj = msg.get();
return obj.as<t>();
}
What's is the problem in my first code?
Thanks!
msgpack binary output can include null bytes, like it happens in your case. is […] << buff.data(); will only output first 4 bytes in your case.
To fix the error replace is << buff.size() << buff.data(); with is << buff.size() << string(buff.data(), buff.size());.
I'm writing a program to read text from a file and I need to print out the position that each of the numbers appear in the file. For example, the .txt file looks like this:
one
two one two
three three two one
and my output should look like:
one: 0, 2, 7
two: 1, 3, 6
three: 4, 5
Everything works fine until I try to display the map of type string, list(int), then I get the whole ""no match for 'operator<<'" error.
Here is my code, any help would be greatly appreciated, thanks!
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <list>
#include <vector>
using namespace std;
int main()
{
ifstream is("input.txt");
if (is.fail())
{
cout << "File I/O error!" << endl;
exit(1);
}
map<string, list<int> > m;
string word;
vector<string> v;
list<int> l, x, y;
while (is >> word)
{
v.push_back(word);
}
for (unsigned int i = 0; i < v.size(); ++i)
{
if (v[i] == "one")
l.push_back(i);
else if (v[i] == "two")
x.push_back(i);
else if (v[i] == "three")
y.push_back(i);
}
m["One"] = l;
m["Two"] = x;
m["Three"] = y;
for (map<string, list<int> >::iterator i = m.begin(); i != m.end(); ++i)
cout << (*i).first << ", " << (*i).second << endl;
return 0;
}
The problem is when you try to output (*i).second. As i is of type map<string, list<int> >::iterator (*i).second is a list<int> and c++ does not know how to output it.
You have two options - either overload ostream& operator<<(ostream& out, const list<int>& l) or use an inner cycle to output the elements one by one. I personally recommend the second option as overloading the operator for such "popular" type may be dangerous.
The simplest solution:
// !!! DO NOT DO THIS AT HOME OR AT ALL !!!
namespace std {
template <typename T, typename A>
ostream& operator<<(ostream& out, list<T, A> const& l) {
if (l.empty()) { return out << "[]"; }
out << '[';
bool first = true;
for (auto const& t: l) {
if (first) { first = false; } else { out << ", "; }
out << t;
}
return out << ']';
} // operator<<
} // namespace std
Unfortunately... it is strictly forbidden to do so (you are only allowed to specialize existing templates in the std namespace, not add any overload).
Therefore, the best standard compliant solution is to:
Declare a new stream of your own (which forwards everything to a std::ostream&)
Overload this operator for your new stream (in its namespace)
Only ever use this new stream afterward
Feel free to bitch at the clumsiness...
Example of custom stream to get you started:
namespace project {
class OStream {
public:
explicit OStream(std::ostream& out): _out(out) {}
template <typename T>
OStream& operator<<(T const& t) { print(*this, t); return *this; }
template <typename T>
void push(T const& t) { _out << t; }
private:
std::ostream& _out;
}; // class OStream
// Generic Operator (directly forwards to `std::ostream`)
template <typename T>
void print(OStream& out, T const& t) { out.push(t); }
// STL Containers
template <typename It>
void print_range(OStream& out, It begin, It end) {
if (begin == end) { out << "[]"; return; }
out << '[' << *begin;
for (++begin; begin != end; ++begin) { out << ", " << *begin; }
out << ']';
} // push_range
template <typename T, typename A>
void print(OStream& out, std::list<T, A> const& l) {
print_range(out, l.begin(), l.end());
} // operator<<
} // namespace project
// usage
int main() {
std::list<int> example = { 1, 2, 3, 4 };
project::OStream(std::cout) << example << "\n";
}
Note: there are other solutions than creating a custom stream, such as copy pasting std::copy(example.begin(), example.end(), std::ostream_iterator<int>(std::cout, ", ")); everywhere you need to print a list, but I am yet to meet another handy one.