Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This solution was inspired by Marcelo's solution, with a few changes:</p> <pre><code>#include &lt;iostream&gt; #include &lt;iterator&gt; #include &lt;type_traits&gt; #include &lt;vector&gt; #include &lt;algorithm&gt; // This works similar to ostream_iterator, but doesn't print a delimiter after the final item template&lt;typename T, typename TChar = char, typename TCharTraits = std::char_traits&lt;TChar&gt; &gt; class pretty_ostream_iterator : public std::iterator&lt;std::output_iterator_tag, void, void, void, void&gt; { public: typedef TChar char_type; typedef TCharTraits traits_type; typedef std::basic_ostream&lt;TChar, TCharTraits&gt; ostream_type; pretty_ostream_iterator(ostream_type &amp;stream, const char_type *delim = NULL) : _stream(&amp;stream), _delim(delim), _insertDelim(false) { } pretty_ostream_iterator&lt;T, TChar, TCharTraits&gt;&amp; operator=(const T &amp;value) { if( _delim != NULL ) { // Don't insert a delimiter if this is the first time the function is called if( _insertDelim ) (*_stream) &lt;&lt; _delim; else _insertDelim = true; } (*_stream) &lt;&lt; value; return *this; } pretty_ostream_iterator&lt;T, TChar, TCharTraits&gt;&amp; operator*() { return *this; } pretty_ostream_iterator&lt;T, TChar, TCharTraits&gt;&amp; operator++() { return *this; } pretty_ostream_iterator&lt;T, TChar, TCharTraits&gt;&amp; operator++(int) { return *this; } private: ostream_type *_stream; const char_type *_delim; bool _insertDelim; }; #if _MSC_VER &gt;= 1400 // Declare pretty_ostream_iterator as checked template&lt;typename T, typename TChar, typename TCharTraits&gt; struct std::_Is_checked_helper&lt;pretty_ostream_iterator&lt;T, TChar, TCharTraits&gt; &gt; : public std::tr1::true_type { }; #endif // _MSC_VER &gt;= 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&lt;typename T, typename TAllocator&gt; class vector; template&lt;typename T, typename TAllocator&gt; class list; template&lt;typename T, typename TTraits, typename TAllocator&gt; class set; template&lt;typename TKey, typename TValue, typename TTraits, typename TAllocator&gt; class map; } // Basic is_container template; specialize to derive from std::true_type for all desired container types template&lt;typename T&gt; struct is_container : public std::false_type { }; // Mark vector as a container template&lt;typename T, typename TAllocator&gt; struct is_container&lt;std::vector&lt;T, TAllocator&gt; &gt; : public std::true_type { }; // Mark list as a container template&lt;typename T, typename TAllocator&gt; struct is_container&lt;std::list&lt;T, TAllocator&gt; &gt; : public std::true_type { }; // Mark set as a container template&lt;typename T, typename TTraits, typename TAllocator&gt; struct is_container&lt;std::set&lt;T, TTraits, TAllocator&gt; &gt; : public std::true_type { }; // Mark map as a container template&lt;typename TKey, typename TValue, typename TTraits, typename TAllocator&gt; struct is_container&lt;std::map&lt;TKey, TValue, TTraits, TAllocator&gt; &gt; : public std::true_type { }; // Holds the delimiter values for a specific character type template&lt;typename TChar&gt; 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&lt;typename T, typename TChar&gt; struct delimiters { static const delimiters_values&lt;TChar&gt; values; }; // Default delimiters template&lt;typename T&gt; struct delimiters&lt;T, char&gt; { static const delimiters_values&lt;char&gt; values; }; template&lt;typename T&gt; const delimiters_values&lt;char&gt; delimiters&lt;T, char&gt;::values = { "{ ", ", ", " }" }; template&lt;typename T&gt; struct delimiters&lt;T, wchar_t&gt; { static const delimiters_values&lt;wchar_t&gt; values; }; template&lt;typename T&gt; const delimiters_values&lt;wchar_t&gt; delimiters&lt;T, wchar_t&gt;::values = { L"{ ", L", ", L" }" }; // Delimiters for set template&lt;typename T, typename TTraits, typename TAllocator&gt; struct delimiters&lt;std::set&lt;T, TTraits, TAllocator&gt;, char&gt; { static const delimiters_values&lt;char&gt; values; }; template&lt;typename T, typename TTraits, typename TAllocator&gt; const delimiters_values&lt;char&gt; delimiters&lt;std::set&lt;T, TTraits, TAllocator&gt;, char&gt;::values = { "[ ", ", ", " ]" }; template&lt;typename T, typename TTraits, typename TAllocator&gt; struct delimiters&lt;std::set&lt;T, TTraits, TAllocator&gt;, wchar_t&gt; { static const delimiters_values&lt;wchar_t&gt; values; }; template&lt;typename T, typename TTraits, typename TAllocator&gt; const delimiters_values&lt;wchar_t&gt; delimiters&lt;std::set&lt;T, TTraits, TAllocator&gt;, wchar_t&gt;::values = { L"[ ", L", ", L" ]" }; // Delimiters for pair template&lt;typename T1, typename T2&gt; struct delimiters&lt;std::pair&lt;T1, T2&gt;, char&gt; { static const delimiters_values&lt;char&gt; values; }; template&lt;typename T1, typename T2&gt; const delimiters_values&lt;char&gt; delimiters&lt;std::pair&lt;T1, T2&gt;, char&gt;::values = { "(", ", ", ")" }; template&lt;typename T1, typename T2&gt; struct delimiters&lt;std::pair&lt;T1, T2&gt;, wchar_t&gt; { static const delimiters_values&lt;wchar_t&gt; values; }; template&lt;typename T1, typename T2&gt; const delimiters_values&lt;wchar_t&gt; delimiters&lt;std::pair&lt;T1, T2&gt;, wchar_t&gt;::values = { L"(", L", ", L")" }; // Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type. template&lt;typename T, typename TChar = char, typename TCharTraits = std::char_traits&lt;TChar&gt;, typename TDelimiters = delimiters&lt;T, TChar&gt; &gt; struct print_container_helper { typedef TChar char_type; typedef TDelimiters delimiters_type; typedef std::basic_ostream&lt;TChar, TCharTraits&gt;&amp; ostream_type; print_container_helper(const T &amp;container) : _container(&amp;container) { } void operator()(ostream_type &amp;stream) const { if( delimiters_type::values.prefix != NULL ) stream &lt;&lt; delimiters_type::values.prefix; std::copy(_container-&gt;begin(), _container-&gt;end(), pretty_ostream_iterator&lt;typename T::value_type, TChar, TCharTraits&gt;(stream, delimiters_type::values.delimiter)); if( delimiters_type::values.postfix != NULL ) stream &lt;&lt; delimiters_type::values.postfix; } private: const T *_container; }; // Prints a print_container_helper to the specified stream. template&lt;typename T, typename TChar, typename TCharTraits, typename TDelimiters&gt; std::basic_ostream&lt;TChar, TCharTraits&gt;&amp; operator&lt;&lt;(std::basic_ostream&lt;TChar, TCharTraits&gt; &amp;stream, const print_container_helper&lt;T, TChar, TDelimiters&gt; &amp;helper) { helper(stream); return stream; } // Prints a container to the stream using default delimiters template&lt;typename T, typename TChar, typename TCharTraits&gt; typename std::enable_if&lt;is_container&lt;T&gt;::value, std::basic_ostream&lt;TChar, TCharTraits&gt;&amp;&gt;::type operator&lt;&lt;(std::basic_ostream&lt;TChar, TCharTraits&gt; &amp;stream, const T &amp;container) { stream &lt;&lt; print_container_helper&lt;T, TChar, TCharTraits&gt;(container); return stream; } // Prints a pair to the stream using delimiters from delimiters&lt;std::pair&lt;T1, T2&gt;&gt;. template&lt;typename T1, typename T2, typename TChar, typename TCharTraits&gt; std::basic_ostream&lt;TChar, TCharTraits&gt;&amp; operator&lt;&lt;(std::basic_ostream&lt;TChar, TCharTraits&gt; &amp;stream, const std::pair&lt;T1, T2&gt; &amp;value) { if( delimiters&lt;std::pair&lt;T1, T2&gt;, TChar&gt;::values.prefix != NULL ) stream &lt;&lt; delimiters&lt;std::pair&lt;T1, T2&gt;, TChar&gt;::values.prefix; stream &lt;&lt; value.first; if( delimiters&lt;std::pair&lt;T1, T2&gt;, TChar&gt;::values.delimiter != NULL ) stream &lt;&lt; delimiters&lt;std::pair&lt;T1, T2&gt;, TChar&gt;::values.delimiter; stream &lt;&lt; value.second; if( delimiters&lt;std::pair&lt;T1, T2&gt;, TChar&gt;::values.postfix != NULL ) stream &lt;&lt; delimiters&lt;std::pair&lt;T1, T2&gt;, TChar&gt;::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&lt;int&gt; v; std::generate_n(std::back_inserter(v), 10, fibonacci()); std::cout &lt;&lt; v &lt;&lt; std::endl; // Example of using pretty_ostream_iterator directly std::generate_n(pretty_ostream_iterator&lt;int&gt;(std::cout, ";"), 20, fibonacci()); std::cout &lt;&lt; std::endl; } </code></pre> <p>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 <code>value_type</code>, <code>const_iterator</code>, <code>begin()</code>/<code>end()</code>, but I'm not sure I'd recommend that since it might match things that match those criteria but aren't actually containers, like <code>std::basic_string</code>. Also like Marcelo's version, it uses templates that can be specialized to specify the delimiters to use.</p> <p>The major difference is that I've built my version around a <code>pretty_ostream_iterator</code>, which works similar to the <code>std::ostream_iterator</code> but doesn't print a delimiter after the last item. Formatting the containers is done by the <code>print_container_helper</code>, which can be used directly to print containers without an is_container trait, or to specify a different delimiters type.</p> <p>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&lt;&lt; function itself is also defined to work with both char and wchar_t streams.</p> <p>Finally, I've used <code>std::enable_if</code>, 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.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload