How do I print out the contents of a std::vector
to the screen?
A solution that implements the following operator<<
would be nice as well:
template<container C, class T, String delim = ", ", String open = "[", String close = "]">
std::ostream & operator<<(std::ostream & o, const C<T> & x)
{
// ... What can I write here?
}
Here is what I have so far, without a separate function:
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>
#include <sstream>
#include <cstdio>
using namespace std;
int main()
{
ifstream file("maze.txt");
if (file) {
vector<char> vec(istreambuf_iterator<char>(file), (istreambuf_iterator<char>()));
vector<char> path;
int x = 17;
char entrance = vec.at(16);
char firstsquare = vec.at(x);
if (entrance == 'S') {
path.push_back(entrance);
}
for (x = 17; isalpha(firstsquare); x++) {
path.push_back(firstsquare);
}
for (int i = 0; i < path.size(); i++)开发者_高级运维 {
cout << path[i] << " ";
}
cout << endl;
return 0;
}
}
If you have a C++11 compiler, I would suggest using a range-based for-loop (see below); or else use an iterator. But you have several options, all of which I will explain in what follows.
Range-based for-loop (C++11)
In C++11 (and later) you can use the new range-based for-loop, which looks like this:
std::vector<char> path;
// ...
for (char i: path)
std::cout << i << ' ';
The type char
in the for-loop statement should be the type of the elements of the vector path
and not an integer indexing type. In other words, since path
is of type std::vector<char>
, the type that should appear in the range-based for-loop is char
. However, you will likely often see the explicit type replaced with the auto
placeholder type:
for (auto i: path)
std::cout << i << ' ';
Regardless of whether you use the explicit type or the auto
keyword, the object i
has a value that is a copy of the actual item in the path
object. Thus, all changes to i
in the loop are not preserved in path
itself:
std::vector<char> path{'a', 'b', 'c'};
for (auto i: path) {
i = '_'; // 'i' is a copy of the element in 'path', so although
// we can change 'i' here perfectly fine, the elements
// of 'path' have not changed
std::cout << i << ' '; // will print: "_ _ _"
}
for (auto i: path) {
std::cout << i << ' '; // will print: "a b c"
}
If you would like to proscribe being able to change this copied value of i
in the for-loop as well, you can force the type of i
to be const char
like this:
for (const auto i: path) {
i = '_'; // this will now produce a compiler error
std::cout << i << ' ';
}
If you would like to modify the items in path
so that those changes persist in path
outside of the for-loop, then you can use a reference like so:
for (auto& i: path) {
i = '_'; // changes to 'i' will now also change the
// element in 'path' itself to that value
std::cout << i << ' ';
}
and even if you don't want to modify path
, if the copying of objects is expensive you should use a const reference instead of copying by value:
for (const auto& i: path)
std::cout << i << ' ';
Iterators
Before C++11 the canonical solution would have been to use an iterator, and that is still perfectly acceptable. They are used as follows:
std::vector<char> path;
// ...
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i)
std::cout << *i << ' ';
If you want to modify the vector's contents in the for-loop, then use iterator
rather than const_iterator
.
Supplement: typedef / type alias (C++11) / auto (C++11)
This is not another solution, but a supplement to the above iterator
solution. If you are using the C++11 standard (or later), then you can use the auto
keyword to help the readability:
for (auto i = path.begin(); i != path.end(); ++i)
std::cout << *i << ' ';
Here the type of i
will be non-const (i.e., the compiler will use std::vector<char>::iterator
as the type of i
). This is because we called the begin
method, so the compiler deduced the type for i
from that. If we call the cbegin
method instead ("c" for const), then i
will be a std::vector<char>::const_iterator
:
for (auto i = path.cbegin(); i != path.cend(); ++i) {
*i = '_'; // will produce a compiler error
std::cout << *i << ' ';
}
If you're not comfortable with the compiler deducing types, then in C++11 you can use a type alias to avoid having to type the vector out all the time (a good habit to get into):
using Path = std::vector<char>; // C++11 onwards only
Path path; // 'Path' is an alias for std::vector<char>
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
std::cout << *i << ' ';
If you do not have access to a C++11 compiler (or don't like the type alias syntax for whatever reason), then you can use the more traditional typedef
:
typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
Path path;
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
std::cout << *i << ' ';
Side note:
At this point, you may or may not have come across iterators before, and you may or may not have heard that iterators are what you are "supposed" to use, and may be wondering why. The answer is not easy to appreciate, but, in brief, the idea is that iterators are an abstraction that shield you from the details of the operation.
It is convenient to have an object (the iterator) that does the operation you want (like sequential access) rather than you writing the details yourself (the "details" being the code that does the actual accessing of the elements of the vector). You should notice that in the for-loop you are only ever asking the iterator to return you a value (*i
, where i
is the iterator) -- you are never interacting with path
directly itself. The logic goes like this: you create an iterator and give it the object you want to loop over (iterator i = path.begin()
), and then all you do is ask the iterator to get the next value for you (*i
); you never had to worry exactly how the iterator did that -- that's its business, not yours.
OK, but what's the point? Well, imagine if getting a value wasn't simple. What if it involves a bit of work? You don't need to worry, because the iterator has handled that for you -- it sorts out the details, all you need to do is ask it for a value. Additionally, what if you change the container from std::vector
to something else? In theory, your code doesn't change even if the details of how accessing elements in the new container does: remember, the iterator sorts all the details out for you behind the scenes, so you don't need to change your code at all -- you just ask the iterator for the next value in the container, same as before.
So, whilst this may seem like confusing overkill for looping through a vector, there are good reasons behind the concept of iterators and so you might as well get used to using them.
Indexing
You can also use a integer type to index through the elements of the vector in the for-loop explicitly:
for (int i=0; i<path.size(); ++i)
std::cout << path[i] << ' ';
If you are going to do this, it's better to use the container's member types, if they are available and appropriate. std::vector
has a member type called size_type
for this job: it is the type returned by the size
method.
typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
for (Path::size_type i=0; i<path.size(); ++i)
std::cout << path[i] << ' ';
Why not use this in preference to the iterator
solution? For simple cases, you can do that, but using an iterator
brings several advantages, which I have briefly outlined above. As such, my advice would be to avoid this method unless you have good reasons for it.
std::copy (C++11)
See Joshua's answer. You can use the STL algorithm std::copy
to copy the vector contents onto the output stream. I don't have anything to add, except to say that I don't use this method; but there's no good reason for that besides habit.
std::ranges::copy (C++20)
For completeness, C++20 introduced ranges, which can act on the whole range of a std::vector
, so no need for begin
and end
:
#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support
std::vector<char> path;
// ...
std::ranges::copy(path, std::ostream_iterator<char>(std::cout, " "));
Unless you have a recent compiler (on GCC apparently at least version 10.1), likely you will not have ranges support even if you might have some C++20 features available.
Overload std::ostream::operator<<
See also Chris's answer below. This is more a complement to the other answers since you will still need to implement one of the solutions above in the overloading, but the benefit is much cleaner code. This is how you could use the std::ranges::copy
solution above:
#include <iostream>
#include <vector>
#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support
using Path = std::vector<char>; // type alias for std::vector<char>
std::ostream& operator<< (std::ostream& out, const Path& v) {
if ( !v.empty() ) {
out << '[';
std::ranges::copy(v, std::ostream_iterator<char>(out, ", "));
out << "\b\b]"; // use two ANSI backspace characters '\b' to overwrite final ", "
}
return out;
}
int main() {
Path path{'/', 'f', 'o', 'o'};
// will output: "path: [/, f, o, o]"
std::cout << "path: " << path << std::endl;
return 0;
}
Now you can pass your Path
objects to your output stream just like fundamental types. Using any of the other solutions above should also be equally straightforward.
Conclusion
Any of the solutions presented here will work. It's up to you (and context or your coding standards) on which one is the "best". Anything more detailed than this is probably best left for another question where the pros/cons can be properly evaluated, but as always user preference will always play a part: none of the solutions presented are objectively wrong, but some will look nicer to each coder.
Addendum
This is an expanded solution of an earlier one I posted. Since that post kept getting attention, I decided to expand on it and refer to the other excellent solutions posted here, at least those that I have personally used in the past at least once. I would, however, encourage the reader to look at the answers below because there are probably good suggestions that I have forgotten, or do not know, about.
A much easier way to do this is with the standard copy algorithm:
#include <iostream>
#include <algorithm> // for copy
#include <iterator> // for ostream_iterator
#include <vector>
int main() {
/* Set up vector to hold chars a-z */
std::vector<char> path;
for (int ch = 'a'; ch <= 'z'; ++ch)
path.push_back(ch);
/* Print path vector to console */
std::copy(path.begin(), path.end(), std::ostream_iterator<char>(std::cout, " "));
return 0;
}
The ostream_iterator is what's called an iterator adaptor. It is templatized over the type to print out to the stream (in this case, char
). cout
(aka console output) is the stream we want to write to, and the space character (" "
) is what we want printed between each element stored in the vector.
This standard algorithm is powerful and so are many others. The power and flexibility the standard library gives you are what make it so great. Just imagine: you can print a vector to the console with just one line of code. You don't have to deal with special cases with the separator character. You don't need to worry about for-loops. The standard library does it all for you.
This solution was inspired by Marcelo's solution, with a few changes:
#include <iostream>
#include <iterator>
#include <type_traits>
#include <vector>
#include <algorithm>
// This works similar to ostream_iterator, but doesn't print a delimiter after the final item
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar> >
class pretty_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
typedef TChar char_type;
typedef TCharTraits traits_type;
typedef std::basic_ostream<TChar, TCharTraits> ostream_type;
pretty_ostream_iterator(ostream_type &stream, const char_type *delim = NULL)
: _stream(&stream), _delim(delim), _insertDelim(false)
{
}
pretty_ostream_iterator<T, TChar, TCharTraits>& operator=(const T &value)
{
if( _delim != NULL )
{
// Don't insert a delimiter if this is the first time the function is called
if( _insertDelim )
(*_stream) << _delim;
else
_insertDelim = true;
}
(*_stream) << value;
return *this;
}
pretty_ostream_iterator<T, TChar, TCharTraits>& operator*()
{
return *this;
}
pretty_ostream_iterator<T, TChar, TCharTraits>& operator++()
{
return *this;
}
pretty_ostream_iterator<T, TChar, TCharTraits>& operator++(int)
{
return *this;
}
private:
ostream_type *_stream;
const char_type *_delim;
bool _insertDelim;
};
#if _MSC_VER >= 1400
// Declare pretty_ostream_iterator as checked
template<typename T, typename TChar, typename TCharTraits>
struct std::_Is_checked_helper<pretty_ostream_iterator<T, TChar, TCharTraits> > : public std::tr1::true_type
{
};
#endif // _MSC_VER >= 1400
namespace std
{
// Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time.
// These aren't necessary if you do actually include the headers.
template<typename T, typename TAllocator> class vector;
template<typename T, typename TAllocator> class list;
template<typename T, typename TTraits, typename TAllocator> class set;
template<typename TKey, typename TValue, typename TTraits, typename TAllocator> class map;
}
// Basic is_container template; specialize to derive from std::true_type for all desired container types
template<typename T> struct is_container : public std::false_type { };
// Mark vector as a container
template<typename T, typename TAllocator> struct is_container<std::vector<T, TAllocator> > : public std::true_type { };
// Mark list as a container
template<typename T, typename TAllocator> struct is_container<std::list<T, TAllocator> > : public std::true_type { };
// Mark set as a container
template<typename T, typename TTraits, typename TAllocator> struct is_container<std::set<T, TTraits, TAllocator> > : public std::true_type { };
// Mark map as a container
template<typename TKey, typename TValue, typename TTraits, typename TAllocator> struct is_container<std::map<TKey, TValue, TTraits, TAllocator> > : public std::true_type { };
// Holds the delimiter values for a specific character type
template<typename TChar>
struct delimiters_values
{
typedef TChar char_type;
const TChar *prefix;
const TChar *delimiter;
const TChar *postfix;
};
// Defines the delimiter values for a specific container and character type
template<typename T, typename TChar>
struct delimiters
{
static const delimiters_values<TChar> values;
};
// Default delimiters
template<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "{ ", ", ", " }" };
template<typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"{ ", L", ", L" }" };
// Delimiters for set
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters<std::set<T, TTraits, TAllocator>, char>::values = { "[ ", ", ", " ]" };
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters<std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"[ ", L", ", L" ]" };
// Delimiters for pair
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
template<typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T1, typename T2> const delimiters_values<wchar_t> delimiters<std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };
// Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type.
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar> >
struct print_container_helper
{
typedef TChar char_type;
typedef TDelimiters delimiters_type;
typedef std::basic_ostream<TChar, TCharTraits>& ostream_type;
print_container_helper(const T &container)
: _container(&container)
{
}
void operator()(ostream_type &stream) const
{
if( delimiters_type::values.prefix != NULL )
stream << delimiters_type::values.prefix;
std::copy(_container->begin(), _container->end(), pretty_ostream_iterator<typename T::value_type, TChar, TCharTraits>(stream, delimiters_type::values.delimiter));
if( delimiters_type::values.postfix != NULL )
stream << delimiters_type::values.postfix;
}
private:
const T *_container;
};
// Prints a print_container_helper to the specified stream.
template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const print_container_helper<T, TChar, TDelimiters> &helper)
{
helper(stream);
return stream;
}
// Prints a container to the stream using default delimiters
template<typename T, typename TChar, typename TCharTraits>
typename std::enable_if<is_container<T>::value, std::basic_ostream<TChar, TCharTraits>&>::type
operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const T &container)
{
stream << print_container_helper<T, TChar, TCharTraits>(container);
return stream;
}
// Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>.
template<typename T1, typename T2, typename TChar, typename TCharTraits>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const std::pair<T1, T2> &value)
{
if( delimiters<std::pair<T1, T2>, TChar>::values.prefix != NULL )
stream << delimiters<std::pair<T1, T2>, TChar>::values.prefix;
stream << value.first;
if( delimiters<std::pair<T1, T2>, TChar>::values.delimiter != NULL )
stream << delimiters<std::pair<T1, T2>, TChar>::values.delimiter;
stream << value.second;
if( delimiters<std::pair<T1, T2>, TChar>::values.postfix != NULL )
stream << delimiters<std::pair<T1, T2>, TChar>::values.postfix;
return stream;
}
// Used by the sample below to generate some values
struct fibonacci
{
fibonacci() : f1(0), f2(1) { }
int operator()()
{
int r = f1 + f2;
f1 = f2;
f2 = r;
return f1;
}
private:
int f1;
int f2;
};
int main()
{
std::vector<int> v;
std::generate_n(std::back_inserter(v), 10, fibonacci());
std::cout << v << std::endl;
// Example of using pretty_ostream_iterator directly
std::generate_n(pretty_ostream_iterator<int>(std::cout, ";"), 20, fibonacci());
std::cout << std::endl;
}
Like Marcelo's version, it uses an is_container type trait that must be specialized for all containers that are to be supported. It may be possible to use a trait to check for value_type
, const_iterator
, begin()
/end()
, but I'm not sure I'd recommend that since it might match things that match those criteria but aren't actually containers, like std::basic_string
. Also like Marcelo's version, it uses templates that can be specialized to specify the delimiters to use.
The major difference is that I've built my version around a pretty_ostream_iterator
, which works similar to the std::ostream_iterator
but doesn't print a delimiter after the last item. Formatting the containers is done by the print_container_helper
, which can be used directly to print containers without an is_container trait, or to specify a different delimiters type.
I've also defined is_container and delimiters so it will work for containers with non-standard predicates or allocators, and for both char and wchar_t. The operator<< function itself is also defined to work with both char and wchar_t streams.
Finally, I've used std::enable_if
, which is available as part of C++0x, and works in Visual C++ 2010 and g++ 4.3 (needs the -std=c++0x flag) and later. This way there is no dependency on Boost.
In C++11 you can now use a range-based for loop:
for (auto const& c : path)
std::cout << c << ' ';
You can print containers as well as ranges and tuples using the {fmt} library. For example:
#include <vector>
#include <fmt/ranges.h>
int main() {
auto v = std::vector<int>{1, 2, 3};
fmt::print("{}", v);
}
prints
[1, 2, 3]
to stdout
(godbolt).
I wouldn't recommend overloading operator<<
for standard containers because it may introduce ODR violations.
Disclaimer: I'm the author of {fmt}.
I think the best way to do this is to just overload operator<<
by adding this function to your program:
#include <vector>
using std::vector;
#include <iostream>
using std::ostream;
template<typename T>
ostream& operator<< (ostream& out, const vector<T>& v) {
out << "{";
size_t last = v.size() - 1;
for(size_t i = 0; i < v.size(); ++i) {
out << v[i];
if (i != last)
out << ", ";
}
out << "}";
return out;
}
Then you can use the <<
operator on any possible vector, assuming its elements also have ostream& operator<<
defined:
vector<string> s = {"first", "second", "third"};
vector<bool> b = {true, false, true, false, false};
vector<int> i = {1, 2, 3, 4};
cout << s << endl;
cout << b << endl;
cout << i << endl;
Outputs:
{first, second, third}
{1, 0, 1, 0, 0}
{1, 2, 3, 4}
This has been edited a few times, and we have decided to call the main class that wraps a collection RangePrinter
.
This should work automatically with any collection once you have written the one-time operator<<
overload, except that you will need a special one for maps to print the pair, and may want to customize the delimiter there.
You could also have a special "print" function to use on the item instead of just outputting it directly, a bit like STL algorithms allow you to pass in custom predicates. With map
you would use it this way, with a custom printer for the std::pair
.
Your "default" printer would just output it to the stream.
Ok, let's work on a custom printer. I will change my outer class to RangePrinter
. So we have 2 iterators and some delimiters but have not customized how to print the actual items.
struct DefaultPrinter
{
template< typename T >
std::ostream & operator()( std::ostream& os, const T& t ) const
{
return os << t;
}
// overload for std::pair
template< typename K, typename V >
std::ostream & operator()( std::ostream & os, std::pair<K,V> const& p)
{
return os << p.first << '=' << p.second;
}
};
// some prototypes
template< typename FwdIter, typename Printer > class RangePrinter;
template< typename FwdIter, typename Printer >
std::ostream & operator<<( std::ostream &,
RangePrinter<FwdIter, Printer> const& );
template< typename FwdIter, typename Printer=DefaultPrinter >
class RangePrinter
{
FwdIter begin;
FwdIter end;
std::string delim;
std::string open;
std::string close;
Printer printer;
friend std::ostream& operator<< <>( std::ostream&,
RangePrinter<FwdIter,Printer> const& );
public:
RangePrinter( FwdIter b, FwdIter e, Printer p,
std::string const& d, std::string const & o, std::string const& c )
: begin( b ), end( e ), printer( p ), open( o ), close( c )
{
}
// with no "printer" variable
RangePrinter( FwdIter b, FwdIter e,
std::string const& d, std::string const & o, std::string const& c )
: begin( b ), end( e ), open( o ), close( c )
{
}
};
template<typename FwdIter, typename Printer>
std::ostream& operator<<( std::ostream& os,
RangePrinter<FwdIter, Printer> const& range )
{
const Printer & printer = range.printer;
os << range.open;
FwdIter begin = range.begin, end = range.end;
// print the first item
if (begin == end)
{
return os << range.close;
}
printer( os, *begin );
// print the rest with delim as a prefix
for( ++begin; begin != end; ++begin )
{
os << range.delim;
printer( os, *begin );
}
return os << range.close;
}
Now by default it will work for maps as long as the key and value types are both printable and you can put in your own special item printer for when they are not (as you can with any other type), or if you do not want "=" as the delimiter.
I am moving the free-function to create these to the end now:
A free-function (iterator version) would look like something this and you could even have defaults:
template<typename Collection>
RangePrinter<typename Collection::const_iterator> rangePrinter
( const Collection& coll, const char * delim=",",
const char * open="[", const char * close="]")
{
return RangePrinter< typename Collection::const_iterator >
( coll.begin(), coll.end(), delim, open, close );
}
You could then use it for std::set
by
std::cout << outputFormatter( mySet );
You can also write free-function version that take a custom printer and ones that take two iterators. In any case they will resolve the template parameters for you, and you will be able to pass them through as temporaries.
How about for_each
+ lambda expression:
#include <vector>
#include <algorithm>
// ...
std::vector<char> vec;
// ...
std::for_each(
vec.cbegin(),
vec.cend(),
[] (const char c) {std::cout << c << " ";}
);
// ...
Of course, a range-based for is the most elegant solution for this concrete task, but this one gives many other possibilities as well.
Explanation
The for_each
algorithm takes an input range and a callable object, calling this object on every element of the range. An input range is defined by two iterators. A callable object can be a function, a pointer to function, an object of a class which overloads () operator
or as in this case, a lambda expression. The parameter for this expression matches the type of the elements from vector.
The beauty of this implementation is the power you get from lambda expressions - you can use this approach for a lot more things than just printing the vector.
Here is a working library, presented as a complete working program, that I just hacked together:
#include <set>
#include <vector>
#include <iostream>
#include <boost/utility/enable_if.hpp>
// Default delimiters
template <class C> struct Delims { static const char *delim[3]; };
template <class C> const char *Delims<C>::delim[3]={"[", ", ", "]"};
// Special delimiters for sets.
template <typename T> struct Delims< std::set<T> > { static const char *delim[3]; };
template <typename T> const char *Delims< std::set<T> >::delim[3]={"{", ", ", "}"};
template <class C> struct IsContainer { enum { value = false }; };
template <typename T> struct IsContainer< std::vector<T> > { enum { value = true }; };
template <typename T> struct IsContainer< std::set<T> > { enum { value = true }; };
template <class C>
typename boost::enable_if<IsContainer<C>, std::ostream&>::type
operator<<(std::ostream & o, const C & x)
{
o << Delims<C>::delim[0];
for (typename C::const_iterator i = x.begin(); i != x.end(); ++i)
{
if (i != x.begin()) o << Delims<C>::delim[1];
o << *i;
}
o << Delims<C>::delim[2];
return o;
}
template <typename T> struct IsChar { enum { value = false }; };
template <> struct IsChar<char> { enum { value = true }; };
template <typename T, int N>
typename boost::disable_if<IsChar<T>, std::ostream&>::type
operator<<(std::ostream & o, const T (&x)[N])
{
o << "[";
for (int i = 0; i != N; ++i)
{
if (i) o << ",";
o << x[i];
}
o << "]";
return o;
}
int main()
{
std::vector<int> i;
i.push_back(23);
i.push_back(34);
std::set<std::string> j;
j.insert("hello");
j.insert("world");
double k[] = { 1.1, 2.2, M_PI, -1.0/123.0 };
std::cout << i << "\n" << j << "\n" << k << "\n";
}
It currently only works with vector
and set
, but can be made to work with most containers, just by expanding on the IsContainer
specializations. I haven't thought much about whether this code is minimal, but I can't immediately think of anything I could strip out as redundant.
EDIT: Just for kicks, I included a version that handles arrays. I had to exclude char arrays to avoid further ambiguities; it might still get into trouble with wchar_t[]
.
The code proved to be handy on several occasions now and I feel the expense to get into customization as usage is quite low. Thus, I decided to release it under MIT license and provide a GitHub repository where the header and a small example file can be downloaded.
http://djmuw.github.io/prettycc
0. Preface and wording
A 'decoration' in terms of this answer is a set of prefix-string, delimiter-string, and a postfix-string. Where the prefix string is inserted into a stream before and the postfix string after the values of a container (see 2. Target containers). The delimiter string is inserted between the values of the respective container.
Note: Actually, this answer does not address the question to 100% since the decoration is not strictly compiled time constant because runtime checks are required to check whether a custom decoration has been applied to the current stream. Nevertheless, I think it has some decent features.
Note2: May have minor bugs since it is not yet well tested.
1. General idea/usage
Zero additional code required for usage
It is to be kept as easy as
#include <vector>
#include "pretty.h"
int main()
{
std::cout << std::vector<int>{1,2,3,4,5}; // prints 1, 2, 3, 4, 5
return 0;
}
Easy customization ...
... with respect to a specific stream object
#include <vector>
#include "pretty.h"
int main()
{
// set decoration for std::vector<int> for cout object
std::cout << pretty::decoration<std::vector<int>>("(", ",", ")");
std::cout << std::vector<int>{1,2,3,4,5}; // prints (1,2,3,4,5)
return 0;
}
or with respect to all streams:
#include <vector>
#include "pretty.h"
// set decoration for std::vector<int> for all ostream objects
PRETTY_DEFAULT_DECORATION(std::vector<int>, "{", ", ", "}")
int main()
{
std::cout << std::vector<int>{1,2,3,4,5}; // prints {1, 2, 3, 4, 5}
std::cout << pretty::decoration<std::vector<int>>("(", ",", ")");
std::cout << std::vector<int>{1,2,3,4,5}; // prints (1,2,3,4,5)
return 0;
}
Rough description
- The code includes a class template providing a default decoration for any type
- which can be specialized to change the default decoration for (a) certain type(s) and it is
- using the private storage provided by
ios_base
usingxalloc
/pword
in order to save a pointer to apretty::decor
object specifically decorating a certain type on a certain stream.
If no pretty::decor<T>
object for this stream has been set up explicitly pretty::defaulted<T, charT, chartraitT>::decoration()
is called to obtain the default decoration for the given type.
The class pretty::defaulted
is to be specialized to customize default decorations.
2. Target objects / containers
Target objects obj
for the 'pretty decoration' of this code are objects having either
- overloads
std::begin
andstd::end
defined (includes C-Style arrays), - having
begin(obj)
andend(obj)
available via ADL, - are of type
std::tuple
- or of type
std::pair
.
The code includes a trait for identification of classes with range features (begin
/end
).
(There's no check included, whether begin(obj) == end(obj)
is a valid expression, though.)
The code provides operator<<
s in the global namespace that only apply to classes not having a more specialized version of operator<<
available.
Therefore, for example std::string
is not printed using the operator in this code although having a valid begin
/end
pair.
3. Utilization and customization
Decorations can be imposed separately for every type (except different tuple
s) and stream (not stream type!).
(I.e. a std::vector<int>
can have different decorations for different stream objects.)
A) Default decoration
The default prefix is ""
(nothing) as is the default postfix, while the default delimiter is ", "
(comma+space).
B) Customized default decoration of a type by specializing the pretty::defaulted
class template
The struct defaulted
has a static member function decoration()
returning a decor
object which includes the default values for the given type.
Example using an array:
Customize default array printing:
namespace pretty
{
template<class T, std::size_t N>
struct defaulted<T[N]>
{
static decor<T[N]> decoration()
{
return{ { "(" }, { ":" }, { ")" } };
}
};
}
Print an arry array:
float e[5] = { 3.4f, 4.3f, 5.2f, 1.1f, 22.2f };
std::cout << e << '\n'; // prints (3.4:4.3:5.2:1.1:22.2)
Using the PRETTY_DEFAULT_DECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...)
macro for char
streams
The macro expands to
namespace pretty {
template< __VA_ARGS__ >
struct defaulted< TYPE > {
static decor< TYPE > decoration() {
return { PREFIX, DELIM, POSTFIX };
}
};
}
enabling the above partial specialization to be rewritten to
PRETTY_DEFAULT_DECORATION(T[N], "", ";", "", class T, std::size_t N)
or inserting a full specialization like
PRETTY_DEFAULT_DECORATION(std::vector<int>, "(", ", ", ")")
Another macro for wchar_t
streams is included: PRETTY_DEFAULT_WDECORATION
.
C) Impose decoration on streams
The function pretty::decoration
is used to impose a decoration on a certain stream.
There are overloads taking either
- one string argument being the delimiter (adopting prefix and postfix from the defaulted class)
- or three string arguments assembling the complete decoration
Complete decoration for given type and stream
float e[3] = { 3.4f, 4.3f, 5.2f };
std::stringstream u;
// add { ; } decoration to u
u << pretty::decoration<float[3]>("{", "; ", "}");
// use { ; } decoration
u << e << '\n'; // prints {3.4; 4.3; 5.2}
// uses decoration returned by defaulted<float[3]>::decoration()
std::cout << e; // prints 3.4, 4.3, 5.2
Customization of delimiter for given stream
PRETTY_DEFAULT_DECORATION(float[3], "{{{", ",", "}}}")
std::stringstream v;
v << e; // prints {{{3.4,4.3,5.2}}}
v << pretty::decoration<float[3]>(":");
v << e; // prints {{{3.4:4.3:5.2}}}
v << pretty::decoration<float[3]>("((", "=", "))");
v << e; // prints ((3.4=4.3=5.2))
4. Special handling of std::tuple
Instead of allowing a specialization for every possible tuple type, this code applies any decoration available for std::tuple<void*>
to all kind of std::tuple<...>
s.
5. Remove custom decoration from the stream
To go back to the defaulted decoration for a given type use pretty::clear
function template on the stream s
.
s << pretty::clear<std::vector<int>>();
5. Further examples
Printing "matrix-like" with newline delimiter
std::vector<std::vector<int>> m{ {1,2,3}, {4,5,6}, {7,8,9} };
std::cout << pretty::decoration<std::vector<std::vector<int>>>("\n");
std::cout << m;
Prints
1, 2, 3
4, 5, 6
7, 8, 9
See it on ideone/KKUebZ
6. Code
#ifndef pretty_print_0x57547_sa4884X_0_1_h_guard_
#define pretty_print_0x57547_sa4884X_0_1_h_guard_
#include <string>
#include <iostream>
#include <type_traits>
#include <iterator>
#include <utility>
#define PRETTY_DEFAULT_DECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...) \
namespace pretty { template< __VA_ARGS__ >\
struct defaulted< TYPE > {\
static decor< TYPE > decoration(){\
return { PREFIX, DELIM, POSTFIX };\
} /*decoration*/ }; /*defaulted*/} /*pretty*/
#define PRETTY_DEFAULT_WDECORATION(TYPE, PREFIX, DELIM, POSTFIX, ...) \
namespace pretty { template< __VA_ARGS__ >\
struct defaulted< TYPE, wchar_t, std::char_traits<wchar_t> > {\
static decor< TYPE, wchar_t, std::char_traits<wchar_t> > decoration(){\
return { PREFIX, DELIM, POSTFIX };\
} /*decoration*/ }; /*defaulted*/} /*pretty*/
namespace pretty
{
namespace detail
{
// drag in begin and end overloads
using std::begin;
using std::end;
// helper template
template <int I> using _ol = std::integral_constant<int, I>*;
// SFINAE check whether T is a range with begin/end
template<class T>
class is_range
{
// helper function declarations using expression sfinae
template <class U, _ol<0> = nullptr>
static std::false_type b(...);
template <class U, _ol<1> = nullptr>
static auto b(U &v) -> decltype(begin(v), std::true_type());
template <class U, _ol<0> = nullptr>
static std::false_type e(...);
template <class U, _ol<1> = nullptr>
static auto e(U &v) -> decltype(end(v), std::true_type());
// return types
using b_return = decltype(b<T>(std::declval<T&>()));
using e_return = decltype(e<T>(std::declval<T&>()));
public:
static const bool value = b_return::value && e_return::value;
};
}
// holder class for data
template<class T, class CharT = char, class TraitT = std::char_traits<CharT>>
struct decor
{
static const int xindex;
std::basic_string<CharT, TraitT> prefix, delimiter, postfix;
decor(std::basic_string<CharT, TraitT> const & pre = "",
std::basic_string<CharT, TraitT> const & delim = "",
std::basic_string<CharT, TraitT> const & post = "")
: prefix(pre), delimiter(delim), postfix(post) {}
};
template<class T, class charT, class traits>
int const decor<T, charT, traits>::xindex = std::ios_base::xalloc();
namespace detail
{
template<class T, class CharT, class TraitT>
void manage_decor(std::ios_base::event evt, std::ios_base &s, int const idx)
{
using deco_type = decor<T, CharT, TraitT>;
if (evt == std::ios_base::erase_event)
{ // erase deco
void const * const p = s.pword(idx);
if (p)
{
delete static_cast<deco_type const * const>(p);
s.pword(idx) = nullptr;
}
}
else if (evt == std::ios_base::copyfmt_event)
{ // copy deco
void const * const p = s.pword(idx);
if (p)
{
auto np = new deco_type{ *static_cast<deco_type const * const>(p) };
s.pword(idx) = static_cast<void*>(np);
}
}
}
template<class T> struct clearer {};
template<class T, class CharT, class TraitT>
std::basic_ostream<CharT, TraitT>& operator<< (
std::basic_ostream<CharT, TraitT> &s, clearer<T> const &)
{
using deco_type = decor<T, CharT, TraitT>;
void const * const p = s.pword(deco_type::xindex);
if (p)
{ // delete if set
delete static_cast<deco_type const *>(p);
s.pword(deco_type::xindex) = nullptr;
}
return s;
}
template <class CharT>
struct default_data { static const CharT * decor[3]; };
template <>
const char * default_data<char>::decor[3] = { "", ", ", "" };
template <>
const wchar_t * default_data<wchar_t>::decor[3] = { L"", L", ", L"" };
}
// Clear decoration for T
template<class T>
detail::clearer<T> clear() { return{}; }
template<class T, class CharT, class TraitT>
void clear(std::basic_ostream<CharT, TraitT> &s) { s << detail::clearer<T>{}; }
// impose decoration on ostream
template<class T, class CharT, class TraitT>
std::basic_ostream<CharT, TraitT>& operator<<(
std::basic_ostream<CharT, TraitT> &s, decor<T, CharT, TraitT> && h)
{
using deco_type = decor<T, CharT, TraitT>;
void const * const p = s.pword(deco_type::xindex);
// delete if already set
if (p) delete static_cast<deco_type const *>(p);
s.pword(deco_type::xindex) = static_cast<void *>(new deco_type{ std::move(h) });
// check whether we alread have a callback registered
if (s.iword(deco_type::xindex) == 0)
{ // if this is not the case register callback and set iword
s.register_callback(detail::manage_decor<T, CharT, TraitT>, deco_type::xindex);
s.iword(deco_type::xindex) = 1;
}
return s;
}
template<class T, class CharT = char, class TraitT = std::char_traits<CharT>>
struct defaulted
{
static inline decor<T, CharT, TraitT> decoration()
{
return{ detail::default_data<CharT>::decor[0],
detail::default_data<CharT>::decor[1],
detail::default_data<CharT>::decor[2] };
}
};
template<class T, class CharT = char, class TraitT = std::char_traits<CharT>>
decor<T, CharT, TraitT> decoration(
std::basic_string<CharT, TraitT> const & prefix,
std::basic_string<CharT, TraitT> const & delimiter,
std::basic_string<CharT, TraitT> const & postfix)
{
return{ prefix, delimiter, postfix };
}
template<class T, class CharT = char,
class TraitT = std::char_traits < CharT >>
decor<T, CharT, TraitT> decoration(
std::basic_string<CharT, TraitT> const & delimiter)
{
using str_type = std::basic_string<CharT, TraitT>;
return{ defaulted<T, CharT, TraitT>::decoration().prefix,
delimiter, defaulted<T, CharT, TraitT>::decoration().postfix };
}
template<class T, class CharT = char,
class TraitT = std::char_traits < CharT >>
decor<T, CharT, TraitT> decoration(CharT const * const prefix,
CharT const * const delimiter, CharT const * const postfix)
{
using str_type = std::basic_string<CharT, TraitT>;
return{ str_type{ prefix }, str_type{ delimiter }, str_type{ postfix } };
}
template<class T, class CharT = char,
class TraitT = std::char_traits < CharT >>
decor<T, CharT, TraitT> decoration(CharT const * const delimiter)
{
using str_type = std::basic_string<CharT, TraitT>;
return{ defaulted<T, CharT, TraitT>::decoration().prefix,
str_type{ delimiter }, defaulted<T, CharT, TraitT>::decoration().postfix };
}
template<typename T, std::size_t N, std::size_t L>
struct tuple
{
template<class CharT, class TraitT>
static void print(std::basic_ostream<CharT, TraitT>& s, T const & value,
std::basic_string<CharT, TraitT> const &delimiter)
{
s << std::get<N>(value) << delimiter;
tuple<T, N + 1, L>::print(s, value, delimiter);
}
};
template<typename T, std::size_t N>
struct tuple<T, N, N>
{
template<class CharT, class TraitT>
static void print(std::basic_ostream<CharT, TraitT>& s, T const & value,
std::basic_string<CharT, TraitT> const &) {
s << std::get<N>(value);
}
};
}
template<class CharT, class TraitT>
std::basic_ostream<CharT, TraitT> & operator<< (
std::basic_ostream<CharT, TraitT> &s, std::tuple<> const & v)
{
using deco_type = pretty::decor<std::tuple<void*>, CharT, TraitT>;
using defaulted_type = pretty::defaulted<std::tuple<void*>, CharT, TraitT>;
void const * const p = s.pword(deco_type::xindex);
auto const d = static_cast<deco_type const * const>(p);
s << (d ? d->prefix : defaulted_type::decoration().prefix);
s << (d ? d->postfix : defaulted_type::decoration().postfix);
return s;
}
template<class CharT, class TraitT, class ... T>
std::basic_ostream<CharT, TraitT> & operator<< (
std::basic_ostream<CharT, TraitT> &s, std::tuple<T...> const & v)
{
using deco_type = pretty::decor<std::tuple<void*>, CharT, TraitT>;
using defaulted_type = pretty::defaulted<std::tuple<void*>, CharT, TraitT>;
using pretty_tuple = pretty::tuple<std::tuple<T...>, 0U, sizeof...(T)-1U>;
void const * const p = s.pword(deco_type::xindex);
auto const d = static_cast<deco_type const * const>(p);
s << (d ? d->prefix : defaulted_type::decoration().prefix);
pretty_tuple::print(s, v, d ? d->delimiter :
defaulted_type::decoration().delimiter);
s << (d ? d->postfix : defaulted_type::decoration().postfix);
return s;
}
template<class T, class U, class CharT, class TraitT>
std::basic_ostream<CharT, TraitT> & operator<< (
std::basic_ostream<CharT, TraitT> &s, std::pair<T, U> const & v)
{
using deco_type = pretty::decor<std::pair<T, U>, CharT, TraitT>;
using defaulted_type = pretty::defaulted<std::pair<T, U>, CharT, TraitT>;
void const * const p = s.pword(deco_type::xindex);
auto const d = static_cast<deco_type const * const>(p);
s << (d ? d->prefix : defaulted_type::decoration().prefix);
s << v.first;
s << (d ? d->delimiter : defaulted_type::decoration().delimiter);
s << v.second;
s << (d ? d->postfix : defaulted_type::decoration().postfix);
return s;
}
template<class T, class CharT = char,
class TraitT = std::char_traits < CharT >>
typename std::enable_if < pretty::detail::is_range<T>::value,
std::basic_ostream < CharT, TraitT >> ::type & operator<< (
std::basic_ostream<CharT, TraitT> &s, T const & v)
{
bool first(true);
using deco_type = pretty::decor<T, CharT, TraitT>;
using default_type = pretty::defaulted<T, CharT, TraitT>;
void const * const p = s.pword(deco_type::xindex);
auto d = static_cast<pretty::decor<T, CharT, TraitT> const * const>(p);
s << (d ? d->prefix : default_type::decoration().prefix);
for (auto const & e : v)
{ // v is range thus range based for works
if (!first) s << (d ? d->delimiter : default_type::decoration().delimiter);
s << e;
first = false;
}
s << (d ? d->postfix : default_type::decoration().postfix);
return s;
}
#endif // pretty_print_0x57547_sa4884X_0_1_h_guard_
Just copy the container to the console.
std::vector<int> v{1,2,3,4};
std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout, " " ));
Should output :
1 2 3 4
Using std::copy
but without extra trailing separator
An alternative/modified approach using std::copy
(as originally used in @JoshuaKravtiz answer) but without including an additional trailing separator after the last element:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
template <typename T>
void print_contents(const std::vector<T>& v, const char * const separator = " ")
{
if(!v.empty())
{
std::copy(v.begin(),
--v.end(),
std::ostream_iterator<T>(std::cout, separator));
std::cout << v.back() << "\n";
}
}
// example usage
int main() {
std::vector<int> v{1, 2, 3, 4};
print_contents(v); // '1 2 3 4'
print_contents(v, ":"); // '1:2:3:4'
v = {};
print_contents(v); // ... no std::cout
v = {1};
print_contents(v); // '1'
return 0;
}
Example usage applied to container of a custom POD type:
// includes and 'print_contents(...)' as above ...
class Foo
{
int i;
friend std::ostream& operator<<(std::ostream& out, const Foo& obj);
public:
Foo(const int i) : i(i) {}
};
std::ostream& operator<<(std::ostream& out, const Foo& obj)
{
return out << "foo_" << obj.i;
}
int main() {
std::vector<Foo> v{1, 2, 3, 4};
print_contents(v); // 'foo_1 foo_2 foo_3 foo_4'
print_contents(v, ":"); // 'foo_1:foo_2:foo_3:foo_4'
v = {};
print_contents(v); // ... no std::cout
v = {1};
print_contents(v); // 'foo_1'
return 0;
}
The problem is probably in the previous loop:
(x = 17; isalpha(firstsquare); x++)
This loop will run not at all (if firstsquare
is non-alphabetic) or will run forever (if it is alphabetic). The reason is that firstsquare
doesn't change as x
is incremented.
In C++11, a range-based for loop might be a good solution:
vector<char> items = {'a','b','c'};
for (char n : items)
cout << n << ' ';
Output:
a b c
overload operator<<:
template<typename OutStream, typename T>
OutStream& operator<< (OutStream& out, const vector<T>& v)
{
for (auto const& tmp : v)
out << tmp << " ";
out << endl;
return out;
}
Usage:
vector <int> test {1,2,3};
wcout << test; // or any output stream
I am going to add another answer here, because I have come up with a different approach to my previous one, and that is to use locale facets.
The basics are here
Essentially what you do is:
- Create a class that derives from
std::locale::facet
. The slight downside is that you will need a compilation unit somewhere to hold its id. Let's call it MyPrettyVectorPrinter. You'd probably give it a better name, and also create ones for pair and map. - In your stream function, you check
std::has_facet< MyPrettyVectorPrinter >
- If that returns true, extract it with
std::use_facet< MyPrettyVectorPrinter >( os.getloc() )
- Your facet objects will have values for the delimiters and you can read them. If the facet isn't found, your print function (
operator<<
) provides default ones. Note you can do the same thing for reading a vector.
I like this method because you can use a default print whilst still being able to use a custom override.
The downsides are needing a library for your facet if used in multiple projects (so can't just be headers-only) and also the fact that you need to beware about the expense of creating a new locale object.
I have written this as a new solution rather than modify my other one because I believe both approaches can be correct and you take your pick.
This answer is based on the answer from Zorawar, but I couldn't leave a comment there.
You can make the auto
(C++11)/typedef
version const
by using cbegin
and cend
instead
for (auto i = path.cbegin(); i != path.cend(); ++i)
std::cout << *i << ' ';
I see two problems. As pointed out in
for (x = 17; isalpha(firstsquare); x++)
there's either an infinite loop or never executed at all, and also in if (entrance == 'S')
if the entrance character is different than 'S'
then nothing in pushed to the path vector, making it empty and thus printing nothing on screen. You can test the latter checking for path.empty()
or printing path.size()
.
Either way, wouldn't it be better to use a string instead of a vector? You can access the string contents like an array as well, seek characters, extract substrings and print the string easily (without a loop).
Doing it all with strings might be the way to have it written in a less convoluted way and make it easier to spot the problem.
The goal here is to use ADL to do customization of how we pretty print.
You pass in a formatter tag, and override 4 functions (before, after, between and descend) in the tag's namespace. This changes how the formatter prints 'adornments' when iterating over containers.
A default formatter that does {(a->b),(c->d)}
for maps, (a,b,c)
for tupleoids, "hello"
for strings, [x,y,z]
for everything else included.
It should "just work" with 3rd party iterable types (and treat them like "everything else").
If you want custom adornments for your 3rd party iterables, simply create your own tag. It will take a bit of work to handle map descent (you need to overload pretty_print_descend( your_tag
to return pretty_print::decorator::map_magic_tag<your_tag>
). Maybe there is a cleaner way to do this, not sure.
A little library to detect iterability, and tuple-ness:
namespace details {
using std::begin; using std::end;
template<class T, class=void>
struct is_iterable_test:std::false_type{};
template<class T>
struct is_iterable_test<T,
decltype((void)(
(void)(begin(std::declval<T>())==end(std::declval<T>()))
, ((void)(std::next(begin(std::declval<T>()))))
, ((void)(*begin(std::declval<T>())))
, 1
))
>:std::true_type{};
template<class T>struct is_tupleoid:std::false_type{};
template<class...Ts>struct is_tupleoid<std::tuple<Ts...>>:std::true_type{};
template<class...Ts>struct is_tupleoid<std::pair<Ts...>>:std::true_type{};
// template<class T, size_t N>struct is_tupleoid<std::array<T,N>>:std::true_type{}; // complete, but problematic
}
template<class T>struct is_iterable:details::is_iterable_test<std::decay_t<T>>{};
template<class T, std::size_t N>struct is_iterable<T(&)[N]>:std::true_type{}; // bypass decay
template<class T>struct is_tupleoid:details::is_tupleoid<std::decay_t<T>>{};
template<class T>struct is_visitable:std::integral_constant<bool, is_iterable<T>{}||is_tupleoid<T>{}> {};
A library that lets us visit the contents of an iterable or tuple type object:
template<class C, class F>
std::enable_if_t<is_iterable<C>{}> visit_first(C&& c, F&& f) {
using std::begin; using std::end;
auto&& b = begin(c);
auto&& e = end(c);
if (b==e)
return;
std::forward<F>(f)(*b);
}
template<class C, class F>
std::enable_if_t<is_iterable<C>{}> visit_all_but_first(C&& c, F&& f) {
using std::begin; using std::end;
auto it = begin(c);
auto&& e = end(c);
if (it==e)
return;
it = std::next(it);
for( ; it!=e; it = std::next(it) ) {
f(*it);
}
}
namespace details {
template<class Tup, class F>
void visit_first( std::index_sequence<>, Tup&&, F&& ) {}
template<size_t... Is, class Tup, class F>
void visit_first( std::index_sequence<0,Is...>, Tup&& tup, F&& f ) {
std::forward<F>(f)( std::get<0>( std::forward<Tup>(tup) ) );
}
template<class Tup, class F>
void visit_all_but_first( std::index_sequence<>, Tup&&, F&& ) {}
template<size_t... Is,class Tup, class F>
void visit_all_but_first( std::index_sequence<0,Is...>, Tup&& tup, F&& f ) {
int unused[] = {0,((void)(
f( std::get<Is>(std::forward<Tup>(tup)) )
),0)...};
(void)(unused);
}
}
template<class Tup, class F>
std::enable_if_t<is_tupleoid<Tup>{}> visit_first(Tup&& tup, F&& f) {
details::visit_first( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) );
}
template<class Tup, class F>
std::enable_if_t<is_tupleoid<Tup>{}> visit_all_but_first(Tup&& tup, F&& f) {
details::visit_all_but_first( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) );
}
A pretty printing library:
namespace pretty_print {
namespace decorator {
struct default_tag {};
template<class Old>
struct map_magic_tag:Old {}; // magic for maps
// Maps get {}s. Write trait `is_associative` to generalize:
template<class CharT, class Traits, class...Xs >
void pretty_print_before( default_tag, std::basic_ostream<CharT, Traits>& s, std::map<Xs...> const& ) {
s << CharT('{');
}
template<class CharT, class Traits, class...Xs >
void pretty_print_after( default_tag, std::basic_ostream<CharT, Traits>& s, std::map<Xs...> const& ) {
s << CharT('}');
}
// tuples and pairs get ():
template<class CharT, class Traits, class Tup >
std::enable_if_t<is_tupleoid<Tup>{}> pretty_print_before( default_tag, std::basic_ostream<CharT, Traits>& s, Tup const& ) {
s << CharT('(');
}
template<class CharT, class Traits, class Tup >
std::enable_if_t<is_tupleoid<Tup>{}> pretty_print_after( default_tag, std::basic_ostream<CharT, Traits>& s, Tup const& ) {
s << CharT(')');
}
// strings with the same character type get ""s:
template<class CharT, class Traits, class...Xs >
void pretty_print_before( default_tag, std::basic_ostream<CharT, Traits>& s, std::basic_string<CharT, Xs...> const& ) {
s << CharT('"');
}
template<class CharT, class Traits, class...Xs >
void pretty_print_after( default_tag, std::basic_ostream<CharT, Traits>& s, std::basic_string<CharT, Xs...> const& ) {
s << CharT('"');
}
// and pack the characters together:
template<class CharT, class Traits, class...Xs >
void pretty_print_between( default_tag, std::basic_ostream<CharT, Traits>&, std::basic_string<CharT, Xs...> const& ) {}
// map magic. When iterating over the contents of a map, use the map_magic_tag:
template<class...Xs>
map_magic_tag<default_tag> pretty_print_descend( default_tag, std::map<Xs...> const& ) {
return {};
}
template<class old_tag, class C>
old_tag pretty_print_descend( map_magic_tag<old_tag>, C const& ) {
return {};
}
// When printing a pair immediately within a map, use -> as a separator:
template<class old_tag, class CharT, class Traits, class...Xs >
void pretty_print_between( map_magic_tag<old_tag>, std::basic_ostream<CharT, Traits>& s, std::pair<Xs...> const& ) {
s << CharT('-') << CharT('>');
}
}
// default behavior:
template<class CharT, class Traits, class Tag, class Container >
void pretty_print_before( Tag const&, std::basic_ostream<CharT, Traits>& s, Container const& ) {
s << CharT('[');
}
template<class CharT, class Traits, class Tag, class Container >
void pretty_print_after( Tag const&, std::basic_ostream<CharT, Traits>& s, Container const& ) {
s << CharT(']');
}
template<class CharT, class Traits, class Tag, class Container >
void pretty_print_between( Tag const&, std::basic_ostream<CharT, Traits>& s, Container const& ) {
s << CharT(',');
}
template<class Tag, class Container>
Tag&& pretty_print_descend( Tag&& tag, Container const& ) {
return std::forward<Tag>(tag);
}
// print things by default by using <<:
template<class Tag=decorator::default_tag, class Scalar, class CharT, class Traits>
std::enable_if_t<!is_visitable<Scalar>{}> print( std::basic_ostream<CharT, Traits>& os, Scalar&& scalar, Tag&&=Tag{} ) {
os << std::forward<Scalar>(scalar);
}
// for anything visitable (see above), use the pretty print algorithm:
template<class Tag=decorator::default_tag, class C, class CharT, class Traits>
std::enable_if_t<is_visitable<C>{}> print( std::basic_ostream<CharT, Traits>& os, C&& c, Tag&& tag=Tag{} ) {
pretty_print_before( std::forward<Tag>(tag), os, std::forward<C>(c) );
visit_first( c, [&](auto&& elem) {
print( os, std::forward<decltype(elem)>(elem), pretty_print_descend( std::forward<Tag>(tag), std::forward<C>(c) ) );
});
visit_all_but_first( c, [&](auto&& elem) {
pretty_print_between( std::forward<Tag>(tag), os, std::forward<C>(c) );
print( os, std::forward<decltype(elem)>(elem), pretty_print_descend( std::forward<Tag>(tag), std::forward<C>(c) ) );
});
pretty_print_after( std::forward<Tag>(tag), os, std::forward<C>(c) );
}
}
Test code:
int main() {
std::vector<int> x = {1,2,3};
pretty_print::print( std::cout, x );
std::cout << "\n";
std::map< std::string, int > m;
m["hello"] = 3;
m["world"] = 42;
pretty_print::print( std::cout, m );
std::cout << "\n";
}
live example
This does use C++14 features (some _t
aliases, and auto&&
lambdas), but none are essential.
My solution is simple.h, which is part of scc package. All std containers, maps, sets, c-arrays are printable.
Coming out of one of the first BoostCon (now called CppCon), I and two others worked on a library to do just this. The main sticking point was needing to extend namespace std
. That turned out to be a no-go for a boost library.
Unfortunately the links to the code no longer work, but you might find some interesting tidbits in the discussions (at least those that aren't talking about what to name it!)
http://boost.2283326.n4.nabble.com/explore-Library-Proposal-Container-Streaming-td2619544.html
Here is my version of implementation done in 2016
Everything is in one header, so it's easy to use https://github.com/skident/eos/blob/master/include/eos/io/print.hpp
/*! \file print.hpp
* \brief Useful functions for work with STL containers.
*
* Now it supports generic print for STL containers like: [elem1, elem2, elem3]
* Supported STL conrainers: vector, deque, list, set multiset, unordered_set,
* map, multimap, unordered_map, array
*
* \author Skident
* \date 02.09.2016
* \copyright Skident Inc.
*/
#pragma once
// check is the C++11 or greater available (special hack for MSVC)
#if (defined(_MSC_VER) && __cplusplus >= 199711L) || __cplusplus >= 201103L
#define MODERN_CPP_AVAILABLE 1
#endif
#include <iostream>
#include <sstream>
#include <vector>
#include <deque>
#include <set>
#include <list>
#include <map>
#include <cctype>
#ifdef MODERN_CPP_AVAILABLE
#include <array>
#include <unordered_set>
#include <unordered_map>
#include <forward_list>
#endif
#define dump(value) std::cout << (#value) << ": " << (value) << std::endl
#define BUILD_CONTENT \
std::stringstream ss; \
for (; it != collection.end(); ++it) \
{ \
ss << *it << elem_separator; \
} \
#define BUILD_MAP_CONTENT \
std::stringstream ss; \
for (; it != collection.end(); ++it) \
{ \
ss << it->first \
<< keyval_separator \
<< it->second \
<< elem_separator; \
} \
#define COMPILE_CONTENT \
std::string data = ss.str(); \
if (!data.empty() && !elem_separator.empty()) \
data = data.substr(0, data.rfind(elem_separator)); \
std::string result = first_bracket + data + last_bracket; \
os << result; \
if (needEndl) \
os << std::endl; \
////
///
///
/// Template definitions
///
///
//generic template for classes: deque, list, forward_list, vector
#define VECTOR_AND_CO_TEMPLATE \
template< \
template<class T, \
class Alloc = std::allocator<T> > \
class Container, class Type, class Alloc> \
#define SET_TEMPLATE \
template< \
template<class T, \
class Compare = std::less<T>, \
class Alloc = std::allocator<T> > \
class Container, class T, class Compare, class Alloc> \
#define USET_TEMPLATE \
template< \
template < class Key, \
class Hash = std::hash<Key>, \
class Pred = std::equal_to<Key>, \
class Alloc = std::allocator<Key> \
> \
class Container, class Key, class Hash, class Pred, class Alloc \
> \
#define MAP_TEMPLATE \
template< \
template<class Key, \
class T, \
class Compare = std::less<Key>, \
class Alloc = std::allocator<std::pair<const Key,T> > \
> \
class Container, class Key, \
class Value/*, class Compare, class Alloc*/> \
#define UMAP_TEMPLATE \
template< \
template<class Key, \
class T, \
class Hash = std::hash<Key>, \
class Pred = std::equal_to<Key>, \
class Alloc = std::allocator<std::pair<const Key,T> >\
> \
class Container, class Key, class Value, \
class Hash, class Pred, class Alloc \
> \
#define ARRAY_TEMPLATE \
template< \
template<class T, std::size_t N> \
class Array, class Type, std::size_t Size> \
namespace eos
{
static const std::string default_elem_separator = ", ";
static const std::string default_keyval_separator = " => ";
static const std::string default_first_bracket = "[";
static const std::string default_last_bracket = "]";
//! Prints template Container<T> as in Python
//! Supported containers: vector, deque, list, set, unordered_set(C++11), forward_list(C++11)
//! \param collection which should be printed
//! \param elem_separator the separator which will be inserted between elements of collection
//! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
//! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
template<class Container>
void print( const Container& collection
, const std::string& elem_separator = default_elem_separator
, const std::string& first_bracket = default_first_bracket
, const std::string& last_bracket = default_last_bracket
, std::ostream& os = std::cout
, bool needEndl = true
)
{
typename Container::const_iterator it = collection.begin();
BUILD_CONTENT
COMPILE_CONTENT
}
//! Prints collections with one template argument and allocator as in Python.
//! Supported standard collections: vector, deque, list, forward_list
//! \param collection which should be printed
//! \param elem_separator the separator which will be inserted between elements of collection
//! \param keyval_separator separator between key and value of map. For default it is the '=>'
//! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
//! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
VECTOR_AND_CO_TEMPLATE
void print( const Container<Type>& collection
, const std::string& elem_separator = default_elem_separator
, const std::string& first_bracket = default_first_bracket
, const std::string& last_bracket = default_last_bracket
, std::ostream& os = std::cout
, bool needEndl = true
)
{
typename Container<Type>::const_iterator it = collection.begin();
BUILD_CONTENT
COMPILE_CONTENT
}
//! Prints collections like std:set<T, Compare, Alloc> as in Python
//! \param collection which should be printed
//! \param elem_separator the separator which will be inserted between elements of collection
//! \param keyval_separator separator between key and value of map. For default it is the '=>'
//! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
//! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
SET_TEMPLATE
void print( const Container<T, Compare, Alloc>& collection
, const std::string& elem_separator = default_elem_separator
, const std::string& first_bracket = default_first_bracket
, const std::string& last_bracket = default_last_bracket
, std::ostream& os = std::cout
, bool needEndl = true
)
{
typename Container<T, Compare, Alloc>::const_iterator it = collection.begin();
BUILD_CONTENT
COMPILE_CONTENT
}
//! Prints collections like std:unordered_set<Key, Hash, Pred, Alloc> as in Python
//! \param collection which should be printed
//! \param elem_separator the separator which will be inserted between elements of collection
//! \param keyval_separator separator between key and value of map. For default it is the '=>'
//! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
//! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
USET_TEMPLATE
void print( const Container<Key, Hash, Pred, Alloc>& collection
, const std::string& elem_separator = default_elem_separator
, const std::string& first_bracket = default_first_bracket
, const std::string& last_bracket = default_last_bracket
, std::ostream& os = std::cout
, bool needEndl = true
)
{
typename Container<Key, Hash, Pred, Alloc>::const_iterator it = collection.begin();
BUILD_CONTENT
COMPILE_CONTENT
}
//! Prints collections like std:map<T, U> as in Python
//! supports generic objects of std: map, multimap
//! \param collection which should be printed
//! \param elem_separator the separator which will be inserted between elements of collection
//! \param keyval_separator separator between key and value of map. For default it is the '=>'
//! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
//! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
MAP_TEMPLATE
void print( const Container<Key, Value>& collection
, const std::string& elem_separator = default_elem_separator
, const std::string& keyval_separator = default_keyval_separator
, const std::string& first_bracket = default_first_bracket
, const std::string& last_bracket = default_last_bracket
, std::ostream& os = std::cout
, bool needEndl = true
)
{
typename Container<Key, Value>::const_iterator it = collection.begin();
BUILD_MAP_CONTENT
COMPILE_CONTENT
}
//! Prints classes like std:unordered_map as in Python
//! \param collection which should be printed
//! \param elem_separator the separator which will be inserted between elements of collection
//! \param keyval_separator separator between key and value of map. For default it is the '=>'
//! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
//! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
UMAP_TEMPLATE
void print( const Container<Key, Value, Hash, Pred, Alloc>& collection
, const std::string& elem_separator = default_elem_separator
, const std::string& keyval_separator = default_keyval_separator
, const std::string& first_bracket = default_first_bracket
, const std::string& last_bracket = default_last_bracket
, std::ostream& os = std::cout
, bool needEndl = true
)
{
typename Container<Key, Value, Hash, Pred, Alloc>::const_iterator it = collection.begin();
BUILD_MAP_CONTENT
COMPILE_CONTENT
}
//! Prints collections like std:array<T, Size> as in Python
//! \param collection which should be printed
//! \param elem_separator the separator which will be inserted between elements of collection
//! \param keyval_separator separator between key and value of map. For default it is the '=>'
//! \param first_bracket data before collection's elements (usual it is the parenthesis, square or curly bracker '(', '[', '{')
//! \param last_bracket data after collection's elements (usual it is the parenthesis, square or curly bracker ')', ']', '}')
ARRAY_TEMPLATE
void print( const Array<Type, Size>& collection
, const std::string& elem_separator = default_elem_separator
, const std::string& first_bracket = default_first_bracket
, const std::string& last_bracket = default_last_bracket
, std::ostream& os = std::cout
, bool needEndl = true
)
{
typename Array<Type, Size>::const_iterator it = collection.begin();
BUILD_CONTENT
COMPILE_CONTENT
}
//! Removes all whitespaces before data in string.
//! \param str string with data
//! \return string without whitespaces in left part
std::string ltrim(const std::string& str);
//! Removes all whitespaces after data in string
//! \param str string with data
//! \return string without whitespaces in right part
std::string rtrim(const std::string& str);
//! Removes all whitespaces before and after data in string
//! \param str string with data
//! \return string without whitespaces before and after data in string
std::string trim(const std::string& str);
////////////////////////////////////////////////////////////
////////////////////////ostream logic//////////////////////
/// Should be specified for concrete containers
/// because of another types can be suitable
/// for templates, for example templates break
/// the code like this "cout << string("hello") << endl;"
////////////////////////////////////////////////////////////
#define PROCESS_VALUE_COLLECTION(os, collection) \
print( collection, \
default_elem_separator, \
default_first_bracket, \
default_last_bracket, \
os, \
false \
); \
#define PROCESS_KEY_VALUE_COLLECTION(os, collection) \
print( collection, \
default_elem_separator, \
default_keyval_separator, \
default_first_bracket, \
default_last_bracket, \
os, \
false \
); \
///< specialization for vector
template<class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
///< specialization for deque
template<class T>
std::ostream& operator<<(std::ostream& os, const std::deque<T>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
///< specialization for list
template<class T>
std::ostream& operator<<(std::ostream& os, const std::list<T>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
///< specialization for set
template<class T>
std::ostream& operator<<(std::ostream& os, const std::set<T>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
///< specialization for multiset
template<class T>
std::ostream& operator<<(std::ostream& os, const std::multiset<T>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
#ifdef MODERN_CPP_AVAILABLE
///< specialization for unordered_map
template<class T>
std::ostream& operator<<(std::ostream& os, const std::unordered_set<T>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
///< specialization for forward_list
template<class T>
std::ostream& operator<<(std::ostream& os, const std::forward_list<T>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
///< specialization for array
template<class T, std::size_t N>
std::ostream& operator<<(std::ostream& os, const std::array<T, N>& collection)
{
PROCESS_VALUE_COLLECTION(os, collection)
return os;
}
#endif
///< specialization for map, multimap
MAP_TEMPLATE
std::ostream& operator<<(std::ostream& os, const Container<Key, Value>& collection)
{
PROCESS_KEY_VALUE_COLLECTION(os, collection)
return os;
}
///< specialization for unordered_map
UMAP_TEMPLATE
std::ostream& operator<<(std::ostream& os, const Container<Key, Value, Hash, Pred, Alloc>& collection)
{
PROCESS_KEY_VALUE_COLLECTION(os, collection)
return os;
}
}
In C++11
for (auto i = path.begin(); i != path.end(); ++i)
std::cout << *i << ' ';
for(int i=0; i<path.size(); ++i)
std::cout << path[i] << ' ';
If boost is an option then you can use boost::algorithm::join
. For example to print out a vector of std::string
:
#include <boost/algorithm/string/join.hpp>
std::vector<std::string> vs { "some", "string", "vector" };
std::cout << boost::algorithm::join(vs, " | ") << '\n';
For vectors of other types you'll need to transform to string first
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
int main()
{
using boost::adaptors::transformed;
using boost::algorithm::join;
// Generate the vector
std::vector<int> vi(10);
std::iota(vi.begin(), vi.end(), -3);
// Print out the vector
std::cout << join(vi |
transformed(static_cast<std::string(*)(int)>(std::to_string)),
", ")
<< '\n';
}
Demo on Godbolt
You can use std::experimental::make_ostream_joiner
:
#include <algorithm>
#include <experimental/iterator>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main()
{
std::vector<int> vi(12);
std::iota(vi.begin(), vi.end(), -5);
std::cout << "Int vector:\n";
std::copy(std::begin(vi),
std::end(vi),
std::experimental::make_ostream_joiner(std::cout, ", "));
std::cout <<"\nString vector:\n[";
std::vector<std::string> vs { "some", "string", "vector" };
std::copy(std::begin(vs),
std::end(vs),
std::experimental::make_ostream_joiner(std::cout, "] - ["));
std::cout << "]\n";
}
Demo on Godbolt
template <typename T>
std::ostream& operator<<( std::ostream& ostrm, const std::vector<T>& vec ){
ostrm << "[";
for( int j = 0, n = vec.size(); j < n; ++j ){
ostrm << " " << vec[ j ] << " ,"[ j < n - 1 ];
}
return ostrm << "]";
}
[ 1, 2, 3, 4 ]
I wrote a an operator<<
which prints any iterable, which includes custom containers, standard containers, and arrays with known bounds. Requires c++11:
template<typename Container, typename =
std::enable_if_t<std::is_same_v<std::void_t<
decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cbegin)),
decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cend))>, void>
&& !std::is_same_v<std::string, Container>>>
std::ostream& operator<<(std::ostream& out, const Container &vec)
{
std::cout << "[ ";
for(const auto& t: vec){
std::cout << t << " ";
}
std::cout << "] ";
return out;
}
You could use perr.h
as a starting point:
#include <vector>
#include "perr.h"
int main() {
std::vector< int > v = { 1, 2, 3 };
perr << v;
}
You just need to grab the header from GitHub (https://github.com/az5112/perr).
For those that are interested: I wrote a generalized solution that takes the best of both worlds, is more generalized to any type of range and puts quotes around non-arithmetic types (desired for string-like types). Additionally, this approach should not have any ADL issues and also avoid 'surprises' (since it's added explicitly on a case-by-case basis):
template <typename T>
inline constexpr bool is_string_type_v = std::is_convertible_v<const T&, std::string_view>;
template<class T>
struct range_out {
range_out(T& range) : r_(range) {
}
T& r_;
static_assert(!::is_string_type_v<T>, "strings and string-like types should use operator << directly");
};
template <typename T>
std::ostream& operator<< (std::ostream& out, range_out<T>& range) {
constexpr bool is_string_like = is_string_type_v<T::value_type>;
constexpr std::string_view sep{ is_string_like ? "', '" : ", " };
if (!range.r_.empty()) {
out << (is_string_like ? "['" : "[");
out << *range.r_.begin();
for (auto it = range.r_.begin() + 1; it != range.r_.end(); ++it) {
out << sep << *it;
}
out << (is_string_like ? "']" : "]");
}
else {
out << "[]";
}
return out;
}
Now it's fairly easy to use on any range:
std::cout << range_out{ my_vector };
The string-like check leaves room for improvement.
I do also have static_assert
check in my solution to avoid std::basic_string<>
, but I left it out here for simplicity.
For people who want one-liners without loops:
I can't believe that noone has though of this, but perhaps it's because of the more C-like approach. Anyways, it is perfectly safe to do this without a loop, in a one-liner, ASSUMING that the std::vector<char>
is null-terminated:
std::vector<char> test { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\0' };
std::cout << test.data() << std::endl;
But I would wrap this in the ostream
operator, as @Zorawar suggested, just to be safe:
template <typename T>std::ostream& operator<< (std::ostream& out, std::vector<T>& v)
{
v.push_back('\0'); // safety-check!
out << v.data();
return out;
}
std::cout << test << std::endl; // will print 'Hello, world!'
We can achieve similar behaviour by using printf
instead:
fprintf(stdout, "%s\n", &test[0]); // will also print 'Hello, world!'
NOTE:
The overloaded ostream
operator needs to accept the vector as non-const. This might make the program insecure or introduce misusable code. Also, since null-character is appended, a reallocation of the std::vector
might occur. So using for-loops with iterators will likely be faster.
精彩评论