From my “Printing Standard Library Sequence and Associative Containers in C++ 2011″ post, let’s discuss how to print the Container Adaptors (i.e. queues, priority queues, and stacks):

template < template < typename, typename, typename ... > class A, typename T, typename C, typename ... P_PACK >
struct std__adaptor : public A< T, C, P_PACK ... > {
	friend inline std::ostream & operator << ( std::ostream & o, std__adaptor< A, T, C, P_PACK ... > const & a ) {
		o << a.A< T, C, P_PACK ... >::c;
		return o;
	}
};

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::queue< T, C > const & q ) {
	o << static_cast< std__adaptor< std::queue, T, C > const & >( q );
	return o;
}

template < typename T, typename C, typename P >
inline std::ostream & operator << ( std::ostream & o, std::priority_queue< T, C, P > const & p ) {
	o << static_cast< std__adaptor< std::priority_queue, T, C, P > const & >( p );
	return o;
}

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::stack< T, C > const & s ) {
	o << static_cast< std__adaptor< std::stack, T, C > const & >( s );
	return o;
}

Container Adaptors are located in 23.6 of the C++ 2011 Standard. Currently, there are only three Container Adaptors in C++: std::queue, std::priority_queue, and std::stack. When we look at the definition of std::queue in 23.6.3.1[1] of the Standard, we see the following:

namespace std {
  template <class T, class Container = deque<T> >
  class queue {
  public:
    typedef typename Container::value_type      value_type;
    typedef typename Container::reference       reference;
    typedef typename Container::const_reference const_reference;
    typedef typename Container::size_type       size_type;
    typedef Container                           container_type;
  protected:
    Container c;
.
.
.

The definition of std::priority_queue in 23.6.4[1] and std::stack in 23.6.5.2 also contain this same member variable:

  protected:
    Container c;

As we continue to look at these definitions, we notice the lack of iterators … which shouldn’t be too shocking since we’re talking about queues and stacks here … it wouldn’t make too much sense if we could easily get at the stuff in the middle of these containers. But that’s exactly what we need to do if we want to print them. There are a bunch of different ways to do this, but I led with that protected member variable, so let’s see if this Container c can get us where we need to go.

WARNING: We are about to rely on a C++ 2011 Standard Library that implements the Container Adaptors portion of the C++ 2011 Standard __exactly__. If your C++ 2011 Standard Library does not implement Container Adaptors using the class definitions in 23.6 of the C++ 2011 Standard, then that protected Container c may not exist or may be slightly different (i.e. c may be named something like m_c or _c or some other non-standard nonsense) … and none of this Container Adaptor printing code should work and (hopefully) won’t even compile. Note: Container Adaptors in g++ 4.6.2 conform exactly to the C++ 2011 Standard … hopefully, that conformance will continue.

So how can we take advantage of the protected Container c member variable that all of these Container Adaptors share?

Simply, all we need to do to print any of the current Container Adaptors is to print that Container Adaptor’s protected Container c member variable. Okay … so how do we do that (esp. since that member variable is protected)?

From 11[1] of the C++ 2011 Standard, a member of a class can be:

  • private; …
  • protected; that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).
  • public; …

In 11.4 (Protected member access), the last half reads:

As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.

So let’s see if we can use that c member variable (i.e. the name of the protected Container) through a reference to a struct derived from that Container Adaptor class in a friend function. In other words, let’s use this (from the code above):

template < template < typename, typename, typename ... > class A, typename T, typename C, typename ... P_PACK >
struct std__adaptor : public A< T, C, P_PACK ... > {
	friend inline std::ostream & operator << ( std::ostream & o, std__adaptor< A, T, C, P_PACK ... > const & a ) {
		o << a.A< T, C, P_PACK ... >::c;
		return o;
	}
};

So the friend function is operator <<, a is the reference to a constant struct derived from that Container Adaptor class through which the member variable c can be used (i.e. a.A< T, C, P_PACK ... >::c), and the struct is std__adaptor which derives from the Container Adaptor that is relatively obfuscated in the template A< T, C, P_PACK ... > … where A stands for the Container Adaptor (i.e. std::queue, std::priority_queue, or std::stack), T stands for type of elements held in that Container Adaptor, C stands for the container that the Container Adaptor uses internally, and P_PACK is the parameter pack that captures zero or more additional parameters that the Container Adaptor may have.

Okay … but why does this work? I mean, we want to print std::queue, std::priority_queue, and std::stack, not std__adaptor … right? True … and that’s why the other code exists (also from above):

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::queue< T, C > const & q ) {
	o << static_cast< std__adaptor< std::queue, T, C > const & >( q );
	return o;
}

template < typename T, typename C, typename P >
inline std::ostream & operator << ( std::ostream & o, std::priority_queue< T, C, P > const & p ) {
	o << static_cast< std__adaptor< std::priority_queue, T, C, P > const & >( p );
	return o;
}

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::stack< T, C > const & s ) {
	o << static_cast< std__adaptor< std::stack, T, C > const & >( s );
	return o;
}

This code forces a reference to a constant std::queue, std::priority_queue, or std::stack to be a reference to a constant std__adaptor whenever we print these Container Adaptors … and then we print that reference to a std__adaptor.

Okay .. but why does that work?

The first half of 5.2.9[2] of the C++ 2011 Standard reads:

An lvalue of type “cv1 B,” where B is a class type, can be cast to type “reference to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D. The result has type “cv2 D.”

Moreover, the Example from 5.2.9[2] contains the following:

struct B { };
struct D : public B { };
D d;
B &br = d;

static_cast<D&>(br); // produces lvalue to the original d object

In our code, B is the Container Adaptor (i.e. std::queue, std::priority_queue, or std::stack) and D is our std__adaptor. Although we haven’t previously constructed a std__adaptor, we are casting from a reference to a constant Container Adaptor (i.e. the base class) to a reference to a constant std__adaptor (i.e. the derived class), we are maintaining our cv-qualification, and we aren’t dealing with virtual base classes, so all of the following casts are good to go:

// q is a std::queue< T, C > const &
static_cast< std__adaptor< std::queue, T, C > const & >( q );

// p is a std::priority_queue< T, C, P > const &
static_cast< std__adaptor< std::priority_queue, T, C, P > const & >( p );

// s is a std::stack< T, C > const &
static_cast< std__adaptor< std::stack, T, C > const & >( s );

Since all of these work, we are left with a reference to a constant std__adaptor … which just happens to have access to Container c through its base class. Since std__adaptor has access to Container c, so do all the friends of std__adaptor, namely operator <<. Any friend of std__adaptor can access Container c using either:

a.A< T, C, P_PACK ... >::c

… or, more succinctly, …

a.c

Once we’re allowed to drill down to Container c, we print that container the same way we’ve printed all the other containers. So, if we have the following main.cpp:

#include <iostream>
#include <string>
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <stack>

template < typename C >
inline void write ( std::ostream & o, C const & c ) {
	auto i = std::begin( c );
	auto end = std::end( c );
	o << '{';
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
}

template < template < typename, typename, typename ... > class C, typename T1, typename T2, typename ... T_PACK >
inline std::ostream & operator << ( std::ostream & o, C< T1, T2, T_PACK ... > const & c ) {
	write( o, c );
	return o;
}

template < typename T, size_t N >
inline std::ostream & operator << ( std::ostream & o, std::array< T, N > const & a ) {
	o << '{';
	write( o, a );
	o << '}';
	return o;
}

template < typename F, typename S >
inline std::ostream & operator << ( std::ostream & o, std::pair< F, S > const & p ) {
	o << "{ " << std::get< 0 >( p ) << ", " << std::get< 1 >( p ) << " }";
	return o;
}

inline std::ostream & operator << ( std::ostream & o, std::string const & s ) {
	auto i = std::begin( s );
	auto end = std::end( s );
	for ( ; i != end; ++i )
		o << *i;
	return o;
}

template < template < typename, typename, typename ... > class A, typename T, typename C, typename ... P_PACK >
struct std__adaptor : public A< T, C, P_PACK ... > {
	friend inline std::ostream & operator << ( std::ostream & o, std__adaptor< A, T, C, P_PACK ... > const & a ) {
		o << a.c;
		return o;
	}
};

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::queue< T, C > const & q ) {
	o << static_cast< std__adaptor< std::queue, T, C > const & >( q );
	return o;
}

template < typename T, typename C, typename P >
inline std::ostream & operator << ( std::ostream & o, std::priority_queue< T, C, P > const & p ) {
	o << static_cast< std__adaptor< std::priority_queue, T, C, P > const & >( p );
	return o;
}

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::stack< T, C > const & s ) {
	o << static_cast< std__adaptor< std::stack, T, C > const & >( s );
	return o;
}

int main () {

	std::queue< std::vector< int > > q;
	q.push( { 1 } );
	q.push( { 2, 2 } );
	q.push( { 3, 3, 3 } );
	std::cout << "q: " << q << '\n';

	std::priority_queue< std::list< double > > pq;
	pq.push( { 1.1 } );
	pq.push( { 2.2, 2.2 } );
	pq.push( { 3.3, 3.3, 3.3 } );
	std::cout << "pq: " << pq << '\n';

	std::stack< std::multimap< std::string, int > > s;
	s.push( { { "one", 1 } } );
	s.push( { { "two", 2 }, { "two", 22 } } );
	s.push( { { "three", 3 }, { "three", 33 }, { "three", 333 } } );
	std::cout << "s: " << s << '\n';

	return 0;
}

And we executed the following command:

g++ -o main.exe main.cpp -std=c++0x -O3 -march=native -Wall -Wextra -Werror && ./main.exe

Then we would get the following output:

q: { { 1 }, { 2, 2 }, { 3, 3, 3 } }
pq: { { 3.3, 3.3, 3.3 }, { 1.1 }, { 2.2, 2.2 } }
s: { { { one, 1 } }, { { two, 2 }, { two, 22 } }, { { three, 3 }, { three, 33 }, { three, 333 } } }

As mentioned before, this only works if the Standard Library used by our C++ 2011 compiler conforms to the C++ 2011 Standard … i.e. they copied & pasted the definitions of std::queue, std::priority_queue, std::stack straight from the Standard into their Standard Library so that all of these Container Adaptors have a protected member variable named c that is a Container … just like the C++ 2011 Standard says they should.

In my next post, I’ll detail how to print std::tuple.

 

From my “Printing Standard Library Sequence and Associative Containers in C++ 2011″ post, let’s discuss the second template:

template < template < typename, typename, typename ... > class C, typename T1, typename T2, typename ... T_PACK >
inline std::ostream & operator << ( std::ostream & o, C< T1, T2, T_PACK ... > const & c ) {
	write( o, c );
	return o;
}

NOTE: This code uses write() as discussed in my last post, “Writing with Iterators in C++ 2011″.

From 14[1] of the C++ 2011 Standard, we have the following:

A template defines a family of classes or functions or an alias for a family of types.

  • template-declaration:
    • template < template-parameter-list > declaration
  • template-parameter-list:
    • template-parameter
    • template-parameter-list , template-parameter

From 14.1[2], there is no semantic difference between class and typename in a template-parameter; however, from 14.1[1], the Standard delineates the following:

The syntax for template-parameters is:

  • template-parameter:
    • type-parameter
    • parameter-declaration
  • type-parameter:
    • class ...opt identifieropt
    • class identifieropt = type-id
    • typename ...opt identifieropt
    • typename identifieropt = type-id
    • template < template-parameter-list > class ...opt identifieropt
    • template < template-parameter-list > class identifieropt = id-expression

So we can use typename everywhere except before the optional identifier in a template template parameter (the last couple of items in the type-parameter listing above) … where we must use class … but we can still use typename in its template-parameter-list (in the inner <>).

Also, notice that all identifiers are optional for type-parameters … even after an ellipsis (...).  Moreover, the second example from 14.3.3[2] of the C++ 2011 Standard includes the following line:

template<template<class ...> class Q> class Y { /* ... */ };

Since typename and class are usually interchangeable, we could swap out the first class for typename in that example … but we would still need to use class for that second instance of class. In other words, we could change the line above to:

template<template<typename ...> class Q> class Y { /* ... */ };

As we’ve previously discussed, a template parameter pack is a template parameter that accepts zero or more template arguments per 14.5.3[1] of the 2011 Standard. If we’d like to ensure that we capture at least two types as template arguments (such as T and A in C< T, A >), then we can just put two typenames in front of a typename ....

So now that we have template parameter packs to go with template template parameters, we have an easy way to describe Standard Library Sequence and Associative Containers … not including std::array. Standard Library Sequence Containers have the form C< T, A >. Standard Library Associative Containers have the form C< K, P, A > for set-related containers and C< K, T, P, A > for map-related containers. Hence, these Sequence and Associative Containers are classes with either 2, 3, or 4 template parameters (some of which contain default template arguments). One way of doing this is just to create a template that matches classes with two or more template parameters:

template < template < typename, typename, typename ... > class C,

Of course, we’re not done … we’d still like the compiler to use template argument deduction (14.8.2) so that we can just use operator << () the same way we always have … without specifying its templates explicitly .. so for a container of the form C< T1, T2, T_PACK ... >, we’d need:

template < template < typename, typename, typename ... > class C, typename T1, typename T2, typename ... T_PACK >

Per 14.5.3[4], a pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more
instantiations of the pattern in a list. Per 14.5.3[5], a parameter pack whose name appears within the pattern of a pack expansion is expanded by that pack … so our second line holds our pack expansion:

inline std::ostream & operator << ( std::ostream & o, C< T1, T2, T_PACK ... > const & c ) {

Now to tie up loose ends.

In order to print std::map, we’ll need to print std::pair … and to get all C++ 2011 up in here, we’ll use std::get, vice the old .first and .second members variables:

template < typename F, typename S >
inline std::ostream & operator << ( std::ostream & o, std::pair< F, S > const & p ) {
	o << "{ " << std::get< 0 >( p ) << ", " << std::get< 1 >( p ) << " }";
	return o;
}

Lastly, as we also mentioned before, std::string is just a typedef for std::basic_string< char, std::char_traits< char >, std::allocator< char > > … so std::string has the exact same form as std::set … which means that our new-fangled variadic template will match std::string just like it matches std::set. While one could argue that std::string is just a container of chars, we definitely didn’t intend for our variadic template to match std::string … and the output of our variadic template for std::string would be “interesting” (i.e. for "abc", we’d get { a, b, c }, vice the more traditional abc … if it could compile). As you might have also guessed, the string header file already has a templated operator << () that prints classes like std::string … so if we throw in one more template that trys to print std::string, then things will get all kinds of ambiguous … and we’ll get compiler errors like:

main.cpp: In function 'void write(std::ostream&, const C&) [with C = std::forward_list<std::basic_string<char> >, std::ostream = std::basic_ostream<char>]':
main.cpp:28:2:   instantiated from 'std::ostream& operator<<(std::ostream&, const C<T1, T2, T_PACK ...>&) [with C = std::forward_list, T1 = std::basic_string<char>, T2 = std::allocator<std::basic_string<char> >, T_PACK = {}, std::ostream = std::basic_ostream<char>]'
main.cpp:52:25:   instantiated from here
main.cpp:18:3: error: ambiguous overload for 'operator<<' in 'std::operator<< [with _Traits = std::char_traits<char>]((* & o), 32) << i.std::_Fwd_list_const_iterator<_Tp>::operator* [with _Tp = std::basic_string<char>, std::_Fwd_list_const_iterator<_Tp>::reference = const std::basic_string<char>&]()'
main.cpp:18:3: note: candidates are:
main.cpp:27:23: note: std::ostream& operator<<(std::ostream&, const C<T1, T2, T_PACK ...>&) [with C = std::basic_string, T1 = char, T2 = std::char_traits<char>, T_PACK = {std::allocator<char>}, std::ostream = std::basic_ostream<char>]
c:\mingw\bin\../lib/gcc/mingw32/4.6.2/include/c++/ostream:581:5: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::basic_string<char>] <near match>
c:\mingw\bin\../lib/gcc/mingw32/4.6.2/include/c++/ostream:581:5: note:   no known conversion for argument 1 from 'std::basic_ostream<char>' to 'std::basic_ostream<char>&&'
c:\mingw\bin\../lib/gcc/mingw32/4.6.2/include/c++/bits/basic_string.h:2693:5: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]

Which is … of course … a lot to take in … but there are probably a gagillion more lines like this … just to make things that much more unreadable. What to do?! All we need to do is bypass all this template stuff with a straight, to-the-point function call … no ambiguity … the compiler will always use this operator << () for std::string:

inline std::ostream & operator << ( std::ostream & o, std::string const & s ) {
	auto i = std::begin( s );
	auto end = std::end( s );
	for ( ; i != end; ++i )
		o << *i;
	return o;
}

So … after all that … if we have the following main.cpp:

#include <iostream>
#include <string>
#include <deque>
#include <forward_list>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

template < typename C >
inline void write ( std::ostream & o, C const & c ) {
	auto i = std::begin( c );
	auto end = std::end( c );
	o << '{';
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
}

template < template < typename, typename, typename ... > class C, typename T1, typename T2, typename ... T_PACK >
inline std::ostream & operator << ( std::ostream & o, C< T1, T2, T_PACK ... > const & c ) {
	write( o, c );
	return o;
}

template < typename F, typename S >
inline std::ostream & operator << ( std::ostream & o, std::pair< F, S > const & p ) {
	o << "{ " << std::get< 0 >( p ) << ", " << std::get< 1 >( p ) << " }";
	return o;
}

inline std::ostream & operator << ( std::ostream & o, std::string const & s ) {
	auto i = std::begin( s );
	auto end = std::end( s );
	for ( ; i != end; ++i )
		o << *i;
	return o;
}

int main () {

	std::deque< int > d = { 1, 2, 3};
	std::cout << "d: " << d << '\n';

	std::forward_list< std::string > fl = { "c", "b", "a" };
	std::cout << "fl: " << fl << '\n';

	std::list< std::string > l = { "a", "b", "c" };
	std::cout << "l: " << l << '\n';

	std::vector< double > v = { 1.1, 2.2, 3.3 };
	std::cout << "v: " << v << '\n';

	std::vector< bool > b = { true, false, true };
	std::cout << "b: " << b << '\n';

	std::map< std::string, int > m = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "m: " << m << '\n';

	std::multimap< std::string, int > mm = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "mm: " << mm << '\n';

	std::set< double > s = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "s: " << s << '\n';

	std::multiset< double > ms = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "ms: " << ms << '\n';

	std::unordered_map< std::string, int > um = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "um: " << um << '\n';

	std::unordered_multimap< std::string, int > umm = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "umm: " << umm << '\n';

	std::unordered_set< double > us = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "us: " << us << '\n';

	std::unordered_multiset< double > ums = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "ums: " << ums << '\n';

	return 0;
}

And we executed the following command:

g++ -o main.exe main.cpp -std=c++0x -march=native -O3 -Wall -Wextra -Werror && ./main.exe

Then we would get the following output:

d: { 1, 2, 3 }
fl: { c, b, a }
l: { a, b, c }
v: { 1.1, 2.2, 3.3 }
b: { 1, 0, 1 }
m: { { a, 1 }, { b, 2 }, { c, 4 } }
mm: { { a, 1 }, { b, 2 }, { c, 4 }, { c, 3 } }
s: { 1.1, 2.2, 3.3 }
ms: { 1.1, 2.2, 3.3, 3.3 }
um: { { c, 4 }, { a, 1 }, { b, 2 } }
umm: { { c, 4 }, { c, 3 }, { a, 1 }, { b, 2 } }
us: { 3.3, 2.2, 1.1 }
ums: { 3.3, 3.3, 2.2, 1.1 }

While all this might seem good to go, we’ve already seen a conflict with std::string … so even though it works now, the more complex our classes get, the more likely an ambiguity might pop up unexpectedly. To minimize the debugging that would need to be done, we might want to ditch this variadic-template method of printing in favor of a little more type-ity type. If you haven’t already figured out the unabridged solution, I’ll detail it a few posts from now … after we cover printing the Container Adaptors and std::tuple.

 

From my “Printing Standard Library Sequence and Associative Containers in C++ 2011″ post, let’s discuss the very first template:

template < typename C >
inline void write ( std::ostream & o, C const & c ) {
	auto i = std::begin( c );
	auto end = std::end( c );
	o << '{';
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
}

In the days of C++ 1998/2003, the Standard Library containers adopted functionality and syntax from arrays and …  Standard Library iterators adopted functionality and syntax from pointers. This adoption allowed us to create the following similar code for both arrays and Standard Library containers (like std::vector):

#include <iostream>
#include <vector>

int main () {

	int a[] = { 1, 2, 3 };

	std::cout << "a: ";
	std::cout << '{';
	int const * a_i = &a[ 0 ];
	int const * a_end = &a[ sizeof( a ) / sizeof( int ) ];
	if ( a_i != a_end ) {
		std::cout << ' ' << *a_i;
		for ( ++a_i; a_i != a_end; ++a_i )
			std::cout << ", " << *a_i;
		std::cout << ' ';
	}
	std::cout << '}';
	std::cout << '\n';

	std::vector< int > v;
	v.push_back( 1 );
	v.push_back( 2 );
	v.push_back( 3 );

	std::cout << "v: ";
	std::cout << '{';
	std::vector< int >::const_iterator v_i = v.begin();
	std::vector< int >::const_iterator v_end = v.end();
	if ( v_i != v_end ) {
		std::cout << ' ' << *v_i;
		for ( ++v_i; v_i != v_end; ++v_i )
			std::cout << ", " << *v_i;
		std::cout << ' ';
	}
	std::cout << '}';
	std::cout << '\n';

	return 0;
}

Unfortunately, the code didn’t have the same implementation even though it had the same structure and used the same ideas. Arrays didn’t have a begin() or end() method … although &a[ 0 ] and &a[ sizeof( a ) / sizeof( type of a ) ] are the same ideas. Arrays also didn’t have a const_iterator typedef … although a pointer to the const version of the type of a is the same idea.  There were many interesting ways around this disparity … all involving a lot of type-ity type.  In C++ 2011, std::begin(), std::end(), and auto allow this structure and these ideas to be realized in the same implementation. Moreover, the ideas behind array initialization have been applied to most Standard Library containers and implemented using the same syntax (using std::initializer_list) … so now we can have the following main.cpp:

#include <iostream>
#include <vector>

template < typename C >
inline void write ( std::ostream & o, C const & c ) {
	auto i = std::begin( c );
	auto end = std::end( c );
	o << '{';
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
}

int main () {

	int a[] = { 1, 2, 3 };

	std::cout << "a: ";
	write( std::cout, a );
	std::cout << '\n';

	std::vector< int > v = { 1, 2, 3 };

	std::cout << "v: ";
	write( std::cout, v );
	std::cout << '\n';

	return 0;
}

Once we execute the following command line:

g++ -o main.exe main.cpp -std=c++0x -march=native -O3 -Wall -Wextra -Werror && ./main.exe

We get the following output:

a: { 1, 2, 3 }
v: { 1, 2, 3 }

This “write” template prints all the values within the Standard Library containers in an ”initializer list” syntax.

In the next post, we’ll discuss how the following traditional operator << function gets a variadic-template makeover and uses “write” to do its dirty work:

template < template < typename, typename, typename ... > class C, typename T1, typename T2, typename ... T_PACK >
inline std::ostream & operator << ( std::ostream & o, C< T1, T2, T_PACK ... > const & c ) {
	write( o, c );
	return o;
}
 

In my last post, I wrote about printing Standard Library Sequence Containers (i.e. std::deque, std::list, and std::vector) in C++ 1998/2003 by matching two different patterns: C< T, A< T > > … or more simply, C< T, A > … two type parameters … where “C” stood for container, “T” stood for type, and “A” stood for allocator.

In C++ 2011, there are a few more Standard Library Sequence Containers, specifically std::array and std::forward_list.  While std::forward_list matches those patterns, std::array does not … so we’ll need to address that.

Additionally, the Standard Library Associative Containers (i.e. std::map, std::multimap, std::set, and std::multiset) use larger patterns.  For instance, if we continue with the previous letters and use “K” for key and “P” for comparison, then std::map would have the following pattern: C< K, T, P< K >, A< std::pair< K, T > > > … or more simply, C< K, T, P, A > … four type parameters.  Moreover, std::set would have the following pattern: C< K, P< K >, A< K > > … or more simply, C< K, P, A > … three type parameters.

In C++ 2011, there are also a few more associative containers … the Standard Library Unordered Associative Containers (i.e. std::unordered_map, std::unordered_multimap, std::unordered_set, and std::unordered_multiset).  These unordered associative containers are just the hash table versions of associative containers; therefore, they all have the same pattern of their associative container counterparts.

Regardless of C++ version, std::map and std::multimap use std::pair to get things done; hence, std::pair will also need to be addressed … and while we’re at it, we might as well as address std::tuple.

Now for the variadic templates:  In C++ 2011, we can use a template parameter pack.  Per 14.5.3[1] of the C++ 2011 Standard, a template parameter pack is a template parameter that accepts zero or more template arguments.  This is exactly what we need to capture standard containers with two, three, or four type parameters.

There is only one thing: std::string is very similar to std::set.  Since std::string is a typedef for std::basic_string< char, std::char_traits< char >, std::allocator< char> >, then std::string has an identical template pattern to std::set (i.e. C< K, P< K >, A< K > > … or more simply, C< K, P, A > … where C = std::basic_string, K = char, P = std::char_traits, and A = std:allocator.  If we provide an explicit operator << for std::string, then there shouldn’t be any ambiguity.

Per 14.1[11] of the C++ 2011 Standard, a template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced or has a default argument. The example from 14.1[11] includes the following lines:

// U cannot be deduced or specified
template<class... T, class... U> void f() { }
template<class... T, class U> void g() { }

Moreover, if a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter. Because of this, creating a single function template that can capture a variety of nested class templates of any depth can get a tad tricky as we have to fight the Standard, so let’s just stick with matching the C< T, A >, C< K, P, A >, and the C< K, T, P, A > versions of the standard containers … and to make this all a little more “C++ 2011″-ish, let’s use auto, initializer lists, std::begin, and std::end in our code.  So, if we have the following main.cpp:

#include <iostream>
#include <string>
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <stack>
#include <tuple>

template < typename C >
inline void write ( std::ostream & o, C const & c ) {
	auto i = std::begin( c );
	auto end = std::end( c );
	o << '{';
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
}

template < template < typename, typename, typename ... > class C, typename T1, typename T2, typename ... T_PACK >
inline std::ostream & operator << ( std::ostream & o, C< T1, T2, T_PACK ... > const & c ) {
	write( o, c );
	return o;
}

template < typename T, size_t N >
inline std::ostream & operator << ( std::ostream & o, std::array< T, N > const & a ) {
	o << '{';
	write( o, a );
	o << '}';
	return o;
}

template < typename F, typename S >
inline std::ostream & operator << ( std::ostream & o, std::pair< F, S > const & p ) {
	o << "{ " << std::get< 0 >( p ) << ", " << std::get< 1 >( p ) << " }";
	return o;
}

inline std::ostream & operator << ( std::ostream & o, std::string const & s ) {
	auto i = s.begin();
	auto end = s.end();
	for ( ; i != end; ++i )
		o << *i;
	return o;
}

template < template < typename, typename, typename ... > class A, typename T, typename C, typename ... P_PACK >
struct std__adaptor : public A< T, C, P_PACK ... > {
	friend inline std::ostream & operator << ( std::ostream & o, std__adaptor< A, T, C, P_PACK ... > const & a ) {
		o << a.A< T, C, P_PACK ... >::c;
		return o;
	}
};

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::queue< T, C > const & q ) {
	o << static_cast< std__adaptor< std::queue, T, C > const & >( q );
	return o;
}

template < typename T, typename C, typename P >
inline std::ostream & operator << ( std::ostream & o, std::priority_queue< T, C, P > const & p ) {
	o << static_cast< std__adaptor< std::priority_queue, T, C, P > const & >( p );
	return o;
}

template < typename T, typename C >
inline std::ostream & operator << ( std::ostream & o, std::stack< T, C > const & s ) {
	o << static_cast< std__adaptor< std::stack, T, C > const & >( s );
	return o;
}

template < size_t i, size_t n, typename ... T_PACK >
struct std__tuple {
	static inline void write ( std::ostream & o, std::tuple< T_PACK ... > const & t ) {
		o << ", " << std::get< i >( t );
		std__tuple< i + 1, n, T_PACK ... >::write( o, t );
	}
};
template < typename ... T_PACK >
struct std__tuple< 0, 0, T_PACK ... > {
	static inline void write ( std::ostream &, std::tuple< T_PACK ... > const & ) {
		//
	}
};
template < size_t n, typename ... T_PACK >
struct std__tuple< 0, n, T_PACK ... > {
	static inline void write ( std::ostream & o, std::tuple< T_PACK ... > const & t ) {
		o << ' ' << std::get< 0 >( t );
		std__tuple< 1, n, T_PACK ... >::write( o, t );
	}
};
template < size_t n, typename ... T_PACK >
struct std__tuple< n, n, T_PACK ... > {
	static inline void write ( std::ostream & o, std::tuple< T_PACK ... > const & ) {
		o << ' ';
	}
};

template < typename ... T_PACK >
inline std::ostream & operator << ( std::ostream & o, std::tuple< T_PACK ... > const & t ) {
	o << '{';
	std__tuple< 0, sizeof...( T_PACK ), T_PACK ... >::write( o, t );
	o << '}';
	return o;
}

int main () {

	std::array< std::string, 3 > a = {{ "a", "b", "c" }};
	std::cout << "a: " << a << '\n';

	std::deque< int > d = { 1, 2, 3};
	std::cout << "d: " << d << '\n';

	std::forward_list< std::string > fl = { "c", "b", "a" };
	std::cout << "fl: " << fl << '\n';

	std::list< std::string > l = { "a", "b", "c" };
	std::cout << "l: " << l << '\n';

	std::vector< double > v = { 1.1, 2.2, 3.3 };
	std::cout << "v: " << v << '\n';

	std::vector< bool > b = { true, false, true };
	std::cout << "b: " << b << '\n';

	std::map< std::string, int > m = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "m: " << m << '\n';

	std::multimap< std::string, int > mm = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "mm: " << mm << '\n';

	std::set< double > s = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "s: " << s << '\n';

	std::multiset< double > ms = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "ms: " << ms << '\n';

	std::unordered_map< std::string, int > um = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "um: " << um << '\n';

	std::unordered_multimap< std::string, int > umm = { { "a", 1 }, { "b", 2 }, { "c", 4 }, { "c", 3 } };
	std::cout << "umm: " << umm << '\n';

	std::unordered_set< double > us = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "us: " << us << '\n';

	std::unordered_multiset< double > ums = { 1.1, 2.2, 3.3, 3.3 };
	std::cout << "ums: " << ums << '\n';

	std::queue< int > q;
	q.push( 1 );
	q.push( 2 );
	q.push( 3 );
	std::cout << "q: " << q << '\n';

	std::priority_queue< double > pq;
	pq.push( 1.1 );
	pq.push( 2.2 );
	pq.push( 3.3 );
	std::cout << "pq: " << pq << '\n';

	std::stack< int > stk;
	stk.push( 1 );
	stk.push( 2 );
	stk.push( 3 );
	std::cout << "stk: " << stk << '\n';

	auto t0 = std::make_tuple();
	std::cout << "t0: " << t0 << '\n';

	auto t1 = std::make_tuple( 1 );
	std::cout << "t1: " << t1 << '\n';

	auto t2 = std::make_tuple( 1, 2.2 );
	std::cout << "t2: " << t2 << '\n';

	auto t3 = std::make_tuple( 1, 2.2, "three" );
	std::cout << "t3: " << t3 << '\n';

	return 0;
}

And we executed the following command:

g++ -o main.exe main.cpp -std=c++0x -O3 -march=native -Wall -Wextra -Werror && ./main.exe

Then we would get the following output:

a: {{ a, b, c }}
d: { 1, 2, 3 }
fl: { c, b, a }
l: { a, b, c }
v: { 1.1, 2.2, 3.3 }
b: { 1, 0, 1 }
m: { { a, 1 }, { b, 2 }, { c, 4 } }
mm: { { a, 1 }, { b, 2 }, { c, 4 }, { c, 3 } }
s: { 1.1, 2.2, 3.3 }
ms: { 1.1, 2.2, 3.3, 3.3 }
um: { { c, 4 }, { a, 1 }, { b, 2 } }
umm: { { c, 4 }, { c, 3 }, { a, 1 }, { b, 2 } }
us: { 3.3, 2.2, 1.1 }
ums: { 3.3, 3.3, 2.2, 1.1 }
q: { 1, 2, 3 }
pq: { 3.3, 1.1, 2.2 }
stk: { 1, 2, 3 }
t0: {}
t1: { 1 }
t2: { 1, 2.2 }
t3: { 1, 2.2, three }

Over the next couple of posts, we will discuss why it works … and if there is a better way … even if it has more type-ity type.

 

In C++ 1998/2003, the Standard Library Sequence Containers (i.e. deque, list, and vector) could be printed using the following template:

template < template < typename, typename > class C, typename T, template < typename > class A >
inline std::ostream & operator << ( std::ostream & o, C< T, A< T > > const & c ) {
	o << '{';
	typename C< T, A< T > >::const_iterator i = c.begin();
	typename C< T, A< T > >::const_iterator end = c.end();
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
	return o;
}

For instance, if we had the following main.cpp:

#include <iostream>
#include <string>
#include <deque>
#include <list>
#include <vector>

template < template < typename, typename > class C, typename T, template < typename > class A >
inline std::ostream & operator << ( std::ostream & o, C< T, A< T > > const & c ) {
	o << '{';
	typename C< T, A< T > >::const_iterator i = c.begin();
	typename C< T, A< T > >::const_iterator end = c.end();
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
	return o;
}

int main () {

	std::deque< int > d;
	d.push_back( 1 );
	d.push_back( 2 );
	d.push_back( 3 );
	std::cout << "d: " << d << '\n';

	std::list< std::string > l;
	l.push_back( "a" );
	l.push_back( "b" );
	l.push_back( "c" );
	std::cout << "l: " << l << '\n';

	std::vector< double > v;
	v.push_back( 1.1 );
	v.push_back( 2.2 );
	v.push_back( 3.3 );
	std::cout << "v: " << v << '\n';

	return 0;
}

And we executed the following command:

g++ -o main.exe main.cpp -march=native -O3 -Wall -Wextra -Werror && ./main.exe

Then we would get the following output:

d: { 1, 2, 3 }
l: { a, b, c }
v: { 1.1, 2.2, 3.3 }

NOTE: Even though “C” stood for container, “T” stood for type, and “A” stood for allocator in the template, any class or struct that had the form Type1< Type2, Type3< Type2 > > would instantiate this template … and then ::const_iterator, .begin(), and .end() would need to make sense for that class or struct.

Of course, if we provided an allocator that didn’t match the form A<T>, then the template above wouldn’t be instantiated; therefore, the Standard Library Sequence Containers could also be printed using the following simpler template in C++ 1998/2003:

template < template < typename, typename > class C, typename T, typename A >
inline std::ostream & operator << ( std::ostream & o, C< T, A > const & c ) {
	o << '{';
	typename C< T, A >::const_iterator i = c.begin();
	typename C< T, A >::const_iterator end = c.end();
	if ( i != end ) {
		o << ' ' << *i;
		for ( ++i; i != end; ++i )
			o << ", " << *i;
		o << ' ';
	}
	o << '}';
	return o;
}

The revised main.cpp would be compiled and the resulting main.exe executed similarly.

Unfortunately, we needed to write another template for Standard Library Associative Containers … unless “typelist” templates were used.

In C++ 2011, variadic templates can be used to print both Standard Library Sequence and Associative Containers using one template … as we will see in the next post.

 

The C++ 2011 Standard (ISO/IEC 14882:2011(E)) … $285:
http://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2fIEC+14882%3a2011

The Last Working Draft of the C++ 2011 Standard (N3291) … FREE:
http://www.joshuaburkholder.com/documents/n3291.pdf
http://www.open-std.org/jtc1/sc22/wg21/prot/14882fdis/n3291.pdf

The C++ Standards Committee Papers:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/

Bjarne Stroustrup’s C++ 2011 FAQ:
http://www.research.att.com/~bs/C++0xFAQ.html

C++ 2011 Compiler Support:
http://wiki.apache.org/stdcxx/C++0xCompilerSupport

C++ 2011 Compiler Support in GCC:
http://gcc.gnu.org/projects/cxx0x.html

C++ 2011 Standard Library Support in GCC:
http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.200x

Minimalist GNU for Windows (MinGW) … GCC on Windows:
http://sourceforge.net/projects/mingw/
http://www.mingw.org/

 

main.cpp:

#include <iostream>

int main () {
    std::cout << "Hello, World!!!\n";
    return 0;
}
© 2012 Joshua Burkholder Suffusion theme by Sayontan Sinha