// Copyright Louis Delacroix 2010 - 2014. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // A pretty printing library for C++ // // Usage: // Include this header, and operator<< will "just work". #ifndef H_PRETTY_PRINT #define H_PRETTY_PRINT #include <cstddef> #include <iterator> #include <memory> #include <ostream> #include <set> #include <tuple> #include <type_traits> #include <unordered_set> #include <utility> #include <valarray> namespace pretty_print { namespace detail { // SFINAE type trait to detect whether T::const_iterator exists. struct sfinae_base { using yes = char; using no = yes[2]; }; template <typename T> struct has_const_iterator : private sfinae_base { private: template <typename C> static yes & test(typename C::const_iterator*); template <typename C> static no & test(...); public: static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes); using type = T; }; template <typename T> struct has_begin_end : private sfinae_base { private: template <typename C> static yes & f(typename std::enable_if< std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)), typename C::const_iterator(C::*)() const>::value>::type *); template <typename C> static no & f(...); template <typename C> static yes & g(typename std::enable_if< std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)), typename C::const_iterator(C::*)() const>::value, void>::type*); template <typename C> static no & g(...); public: static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes); static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes); }; } // namespace detail // Holds the delimiter values for a specific character type template <typename TChar> struct delimiters_values { using char_type = TChar; const char_type * prefix; const char_type * delimiter; const char_type * postfix; }; // Defines the delimiter values for a specific container and character type template <typename T, typename TChar> struct delimiters { using type = delimiters_values<TChar>; static const type values; }; // Functor to print containers. You can use this directly if you want // to specificy a non-default delimiters type. The printing logic can // be customized by specializing the nested template. template <typename T, typename TChar = char, typename TCharTraits = ::std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar>> struct print_container_helper { using delimiters_type = TDelimiters; using ostream_type = std::basic_ostream<TChar, TCharTraits>; template <typename U> struct printer { static void print_body(const U & c, ostream_type & stream) { using std::begin; using std::end; auto it = begin(c); const auto the_end = end(c); if (it != the_end) { for ( ; ; ) { stream << *it; if (++it == the_end) break; if (delimiters_type::values.delimiter != NULL) stream << delimiters_type::values.delimiter; } } } }; print_container_helper(const T & container) : container_(container) { } inline void operator()(ostream_type & stream) const { if (delimiters_type::values.prefix != NULL) stream << delimiters_type::values.prefix; printer<T>::print_body(container_, stream); if (delimiters_type::values.postfix != NULL) stream << delimiters_type::values.postfix; } private: const T & container_; }; // Specialization for pairs template <typename T, typename TChar, typename TCharTraits, typename TDelimiters> template <typename T1, typename T2> struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::pair<T1, T2>> { using ostream_type = typename print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type; static void print_body(const std::pair<T1, T2> & c, ostream_type & stream) { stream << c.first; if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL) stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter; stream << c.second; } }; // Specialization for tuples template <typename T, typename TChar, typename TCharTraits, typename TDelimiters> template <typename ...Args> struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::tuple<Args...>> { using ostream_type = typename print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type; using element_type = std::tuple<Args...>; template <std::size_t I> struct Int { }; static void print_body(const element_type & c, ostream_type & stream) { tuple_print(c, stream, Int<0>()); } static void tuple_print(const element_type &, ostream_type &, Int<sizeof...(Args)>) { } static void tuple_print(const element_type & c, ostream_type & stream, typename std::conditional<sizeof...(Args) != 0, Int<0>, std::nullptr_t>::type) { stream << std::get<0>(c); tuple_print(c, stream, Int<1>()); } template <std::size_t N> static void tuple_print(const element_type & c, ostream_type & stream, Int<N>) { if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL) stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter; stream << std::get<N>(c); tuple_print(c, stream, Int<N + 1>()); } }; // Prints a print_container_helper to the specified stream. template<typename T, typename TChar, typename TCharTraits, typename TDelimiters> inline std::basic_ostream<TChar, TCharTraits> & operator<<( std::basic_ostream<TChar, TCharTraits> & stream, const print_container_helper<T, TChar, TCharTraits, TDelimiters> & helper) { helper(stream); return stream; } // Basic is_container template; specialize to derive from std::true_type for all desired container types template <typename T> struct is_container : public std::integral_constant<bool, detail::has_const_iterator<T>::value && detail::has_begin_end<T>::beg_value && detail::has_begin_end<T>::end_value> { }; template <typename T, std::size_t N> struct is_container<T[N]> : std::true_type { }; template <std::size_t N> struct is_container<char[N]> : std::false_type { }; template <typename T> struct is_container<std::valarray<T>> : std::true_type { }; template <typename T1, typename T2> struct is_container<std::pair<T1, T2>> : std::true_type { }; template <typename ...Args> struct is_container<std::tuple<Args...>> : std::true_type { }; // 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 (multi)set and unordered_(multi)set template <typename T, typename TComp, typename TAllocator> struct delimiters< ::std::set<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; }; template <typename T, typename TComp, typename TAllocator> const delimiters_values<char> delimiters< ::std::set<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" }; template <typename T, typename TComp, typename TAllocator> struct delimiters< ::std::set<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; }; template <typename T, typename TComp, typename TAllocator> const delimiters_values<wchar_t> delimiters< ::std::set<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" }; template <typename T, typename TComp, typename TAllocator> struct delimiters< ::std::multiset<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; }; template <typename T, typename TComp, typename TAllocator> const delimiters_values<char> delimiters< ::std::multiset<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" }; template <typename T, typename TComp, typename TAllocator> struct delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; }; template <typename T, typename TComp, typename TAllocator> const delimiters_values<wchar_t> delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" }; template <typename T, typename THash, typename TEqual, typename TAllocator> struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; }; template <typename T, typename THash, typename TEqual, typename TAllocator> const delimiters_values<char> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" }; template <typename T, typename THash, typename TEqual, typename TAllocator> struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; }; template <typename T, typename THash, typename TEqual, typename TAllocator> const delimiters_values<wchar_t> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" }; template <typename T, typename THash, typename TEqual, typename TAllocator> struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; }; template <typename T, typename THash, typename TEqual, typename TAllocator> const delimiters_values<char> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" }; template <typename T, typename THash, typename TEqual, typename TAllocator> struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; }; template <typename T, typename THash, typename TEqual, typename TAllocator> const delimiters_values<wchar_t> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" }; // Delimiters for pair and tuple 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")" }; template <typename ...Args> struct delimiters<std::tuple<Args...>, char> { static const delimiters_values<char> values; }; template <typename ...Args> const delimiters_values<char> delimiters<std::tuple<Args...>, char>::values = { "(", ", ", ")" }; template <typename ...Args> struct delimiters< ::std::tuple<Args...>, wchar_t> { static const delimiters_values<wchar_t> values; }; template <typename ...Args> const delimiters_values<wchar_t> delimiters< ::std::tuple<Args...>, wchar_t>::values = { L"(", L", ", L")" }; // Type-erasing helper class for easy use of custom delimiters. // Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t, and MyDelims needs to be defined for TChar. // Usage: "cout << pretty_print::custom_delims<MyDelims>(x)". struct custom_delims_base { virtual ~custom_delims_base() { } virtual std::ostream & stream(::std::ostream &) = 0; virtual std::wostream & stream(::std::wostream &) = 0; }; template <typename T, typename Delims> struct custom_delims_wrapper : custom_delims_base { custom_delims_wrapper(const T & t_) : t(t_) { } std::ostream & stream(std::ostream & s) { return s << print_container_helper<T, char, std::char_traits<char>, Delims>(t); } std::wostream & stream(std::wostream & s) { return s << print_container_helper<T, wchar_t, std::char_traits<wchar_t>, Delims>(t); } private: const T & t; }; template <typename Delims> struct custom_delims { template <typename Container> custom_delims(const Container & c) : base(new custom_delims_wrapper<Container, Delims>(c)) { } std::unique_ptr<custom_delims_base> base; }; template <typename TChar, typename TCharTraits, typename Delims> inline std::basic_ostream<TChar, TCharTraits> & operator<<(std::basic_ostream<TChar, TCharTraits> & s, const custom_delims<Delims> & p) { return p.base->stream(s); } // A wrapper for a C-style array given as pointer-plus-size. // Usage: std::cout << pretty_print_array(arr, n) << std::endl; template<typename T> struct array_wrapper_n { typedef const T * const_iterator; typedef T value_type; array_wrapper_n(const T * const a, size_t n) : _array(a), _n(n) { } inline const_iterator begin() const { return _array; } inline const_iterator end() const { return _array + _n; } private: const T * const _array; size_t _n; }; // A wrapper for hash-table based containers that offer local iterators to each bucket. // Usage: std::cout << bucket_print(m, 4) << std::endl; (Prints bucket 5 of container m.) template <typename T> struct bucket_print_wrapper { typedef typename T::const_local_iterator const_iterator; typedef typename T::size_type size_type; const_iterator begin() const { return m_map.cbegin(n); } const_iterator end() const { return m_map.cend(n); } bucket_print_wrapper(const T & m, size_type bucket) : m_map(m), n(bucket) { } private: const T & m_map; const size_type n; }; } // namespace pretty_print // Global accessor functions for the convenience wrappers template<typename T> inline pretty_print::array_wrapper_n<T> pretty_print_array(const T * const a, size_t n) { return pretty_print::array_wrapper_n<T>(a, n); } template <typename T> pretty_print::bucket_print_wrapper<T> bucket_print(const T & m, typename T::size_type n) { return pretty_print::bucket_print_wrapper<T>(m, n); } // Main magic entry point: An overload snuck into namespace std. // Can we do better? namespace std { // Prints a container to the stream using default delimiters template<typename T, typename TChar, typename TCharTraits> inline typename enable_if< ::pretty_print::is_container<T>::value, basic_ostream<TChar, TCharTraits> &>::type operator<<(basic_ostream<TChar, TCharTraits> & stream, const T & container) { return stream << ::pretty_print::print_container_helper<T, TChar, TCharTraits>(container); } } #endif // H_PRETTY_PRINT