diff --git a/ppdemo.cpp b/ppdemo.cpp index 5e5eba3..817e5c8 100644 --- a/ppdemo.cpp +++ b/ppdemo.cpp @@ -84,10 +84,9 @@ int main(int argc, char * argv[]) ; /* Demo: Here we use our reusable delimiter class MyDelims by directly accessing some interna. */ - std::cout << pretty_print::print_container_helper, char, std::char_traits, MyDelims>(v) << std::endl; - - /* Demo: Here we achieve the same using a type-erasing helper class. */ - std::cout << pretty_print::custom_delims(v) << std::endl; + std::cout << "Reusable delimiters: " + << pretty_print::print_container_helper, char, std::char_traits, MyDelims>(v) + << std::endl; /* Demo: We can pretty-print std::pair and std::tuple. (You already saw pairs in the associative containers above.) @@ -97,6 +96,13 @@ int main(int argc, char * argv[]) auto a3 = std::make_tuple("Qrgh", a1, 11); auto a4 = std::make_tuple(1729, 2875, std::pair(1.5, "abc")); + /* Demo: Here we achieve the same using a type-erasing helper class. */ + std::cout << "Custom, type-erased delimiters: " + << pretty_print::custom_delims(v) << std::endl; + std::cout << "Custom, type-erased delimiters: " + << pretty_print::custom_delims(a2) << std::endl; + std::cout << "Custom, type-erased delimiters: " + << pretty_print::custom_delims(a3) << std::endl; /* Demo: raw arrays can be printed with a helper wrapper. */ int arr[] = { 1, 4, 9, 16 }; @@ -104,9 +110,11 @@ int main(int argc, char * argv[]) std::cout << "Static C array: " << arr << std::endl << "Static C array: " << err << std::endl + << "Static C array with length: " << pretty_print_array(arr + 1, 2) << std::endl << "Pair: " << a1 << std::endl << "1-tuple: " << a2 << std::endl << "n-tuple: " << a3 << std::endl << "n-tuple: " << a4 << std::endl + << "Hashmap bucket: " << bucket_print(um, 0) << std::endl ; } diff --git a/prettyprint.hpp b/prettyprint.hpp index 6ef1fb7..1cc5023 100644 --- a/prettyprint.hpp +++ b/prettyprint.hpp @@ -2,209 +2,119 @@ // 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 +#include #include #include -#include -#include -#include #include +#include +#include #include +#include #include namespace pretty_print { - - // SFINAE type trait to detect whether T::const_iterator exists. - - template - struct has_const_iterator + namespace detail { - private: - typedef char yes; - typedef struct { char array[2]; } no; - - template static yes test(typename C::const_iterator*); - template static no test(...); - public: - static const bool value = sizeof(test(0)) == sizeof(yes); - typedef T type; - }; - - // SFINAE type trait to detect whether "T::const_iterator T::begin/end() const" exist. - - template - struct has_begin_end_OLD - { - struct Dummy { typedef void const_iterator; }; - typedef typename std::conditional::value, T, Dummy>::type TType; - typedef typename TType::const_iterator iter; - - struct Fallback { iter begin() const; iter end() const; }; - struct Derived : TType, Fallback { }; - - template struct ChT; - - template static char (&f(ChT*))[1]; - template static char (&f(...))[2]; - template static char (&g(ChT*))[1]; - template static char (&g(...))[2]; - - static bool const beg_value = sizeof(f(0)) == 2; - static bool const end_value = sizeof(g(0)) == 2; - }; - - template - struct has_begin_end - { - template static char (&f(typename std::enable_if< - std::is_same(&C::begin)), - typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; - - template static char (&f(...))[2]; + // SFINAE type trait to detect whether T::const_iterator exists. - template static char (&g(typename std::enable_if< - std::is_same(&C::end)), - typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; - - template static char (&g(...))[2]; + struct sfinae_base + { + using yes = char; + using no = yes[2]; + }; - static bool const beg_value = sizeof(f(0)) == 1; - static bool const end_value = sizeof(g(0)) == 1; - }; + template + struct has_const_iterator : private sfinae_base + { + private: + template static yes & test(typename C::const_iterator*); + template static no & test(...); + public: + static const bool value = sizeof(test(nullptr)) == sizeof(yes); + using type = T; + }; + + template + struct has_begin_end : private sfinae_base + { + private: + template + static yes & f(typename std::enable_if< + std::is_same(&C::begin)), + typename C::const_iterator(C::*)() const>::value>::type *); - // Basic is_container template; specialize to derive from std::true_type for all desired container types + template static no & f(...); - template struct is_container : public ::std::integral_constant::value && has_begin_end::beg_value && has_begin_end::end_value> { }; + template + static yes & g(typename std::enable_if< + std::is_same(&C::end)), + typename C::const_iterator(C::*)() const>::value, void>::type*); - template struct is_container : public ::std::true_type { }; + template static no & g(...); - template struct is_container : public ::std::false_type { }; + public: + static bool const beg_value = sizeof(f(nullptr)) == sizeof(yes); + static bool const end_value = sizeof(g(nullptr)) == sizeof(yes); + }; - template struct is_container< ::std::valarray> : public ::std::true_type { }; + } // namespace detail // Holds the delimiter values for a specific character type - template + template struct delimiters_values { - typedef TChar char_type; - const TChar * prefix; - const TChar * delimiter; - const TChar * postfix; + 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 + template struct delimiters { - typedef delimiters_values type; + using type = delimiters_values; static const type values; }; - // Default delimiters - - template struct delimiters { static const delimiters_values values; }; - template const delimiters_values delimiters::values = { "[", ", ", "]" }; - template struct delimiters { static const delimiters_values values; }; - template const delimiters_values delimiters::values = { L"[", L", ", L"]" }; - - - // Delimiters for (multi)set and unordered_(multi)set - - template - struct delimiters< ::std::set, char> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::set, char>::values = { "{", ", ", "}" }; - - template - struct delimiters< ::std::set, wchar_t> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::set, wchar_t>::values = { L"{", L", ", L"}" }; - - template - struct delimiters< ::std::multiset, char> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::multiset, char>::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 - struct delimiters< ::std::multiset, wchar_t> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::multiset, wchar_t>::values = { L"{", L", ", L"}" }; - - template - struct delimiters< ::std::unordered_set, char> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::unordered_set, char>::values = { "{", ", ", "}" }; - - template - struct delimiters< ::std::unordered_set, wchar_t> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::unordered_set, wchar_t>::values = { L"{", L", ", L"}" }; - - template - struct delimiters< ::std::unordered_multiset, char> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::unordered_multiset, char>::values = { "{", ", ", "}" }; - - template - struct delimiters< ::std::unordered_multiset, wchar_t> { static const delimiters_values values; }; - - template - const delimiters_values delimiters< ::std::unordered_multiset, wchar_t>::values = { L"{", L", ", L"}" }; - - - // Delimiters for pair (reused for tuple, see below) - - template struct delimiters< ::std::pair, char> { static const delimiters_values values; }; - template const delimiters_values delimiters< ::std::pair, char>::values = { "(", ", ", ")" }; - template struct delimiters< ::std::pair, wchar_t> { static const delimiters_values values; }; - template const delimiters_values delimiters< ::std::pair, 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 TDelimiters = delimiters> + template , + typename TDelimiters = delimiters> struct print_container_helper { - typedef TChar char_type; - typedef TDelimiters delimiters_type; - typedef std::basic_ostream ostream_type; - - print_container_helper(const T & container) - : _container(container) - { - } + using delimiters_type = TDelimiters; + using ostream_type = std::basic_ostream; - inline void operator()(ostream_type & stream) const + template + struct printer { - if (delimiters_type::values.prefix != NULL) - stream << delimiters_type::values.prefix; - + static void print_body(const U & c, ostream_type & stream) { using std::begin; using std::end; - auto it = begin(_container); - const auto the_end = end(_container); + auto it = begin(c); + const auto the_end = end(c); if (it != the_end) { @@ -219,187 +129,240 @@ namespace pretty_print } } } + }; + + 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::print_body(container_, stream); if (delimiters_type::values.postfix != NULL) stream << delimiters_type::values.postfix; } private: - const T & _container; + const T & container_; }; + // Specialization for pairs - // Type-erasing helper class for easy use of custom delimiters. - // Requires TCharTraits = std::char_traits and TChar = char or wchar_t, and MyDelims needs to be defined for TChar. - // Usage: "cout << pretty_print::custom_delims(x)". - - struct custom_delims_base + template + template + struct print_container_helper::printer> { - virtual ~custom_delims_base() { } - virtual ::std::ostream & stream(::std::ostream &) = 0; - virtual ::std::wostream & stream(::std::wostream &) = 0; + using ostream_type = print_container_helper::ostream_type; + + static void print_body(const std::pair & c, ostream_type & stream) + { + stream << c.first; + if (print_container_helper::delimiters_type::values.delimiter != NULL) + stream << print_container_helper::delimiters_type::values.delimiter; + stream << c.second; + } }; - template - struct custom_delims_wrapper : public custom_delims_base + // Specialization for tuples + + template + template + struct print_container_helper::printer> { - custom_delims_wrapper(const T & t_) : t(t_) { } + using ostream_type = print_container_helper::ostream_type; + using element_type = std::tuple; - ::std::ostream & stream(::std::ostream & s) + template struct Int { }; + + static void print_body(const element_type & c, ostream_type & stream) { - return s << ::pretty_print::print_container_helper, Delims>(t); + tuple_print(c, stream, Int<0>()); } - ::std::wostream & stream(::std::wostream & s) + + static void tuple_print(const element_type &, ostream_type &, Int) { - return s << ::pretty_print::print_container_helper, Delims>(t); } - private: - const T & t; + static typename std::enable_if::type + tuple_print(const element_type & c, ostream_type & stream, Int<0>) + { + stream << std::get<0>(c); + tuple_print(c, stream, Int<1>()); + } + + template + static void tuple_print(const element_type & c, ostream_type & stream, Int) + { + if (print_container_helper::delimiters_type::values.delimiter != NULL) + stream << print_container_helper::delimiters_type::values.delimiter; + + stream << std::get(c); + + tuple_print(c, stream, Int()); + } }; - template - struct custom_delims + // Prints a print_container_helper to the specified stream. + + template + inline std::basic_ostream & operator<<( + std::basic_ostream & stream, + const print_container_helper & helper) { - template - custom_delims(const Container & c) - : base(new custom_delims_wrapper(c)) { } + helper(stream); + return stream; + } - std::unique_ptr base; - }; -} // namespace pretty_print + // Basic is_container template; specialize to derive from std::true_type for all desired container types + template + struct is_container : public std::integral_constant::value && + detail::has_begin_end::beg_value && + detail::has_begin_end::end_value> { }; -template -inline std::basic_ostream & operator<<(std::basic_ostream & s, const pretty_print::custom_delims & p) -{ - return p.base->stream(s); -} + template + struct is_container : std::true_type { }; + template + struct is_container : std::false_type { }; -// Template aliases for char and wchar_t delimiters -// Enable these if you have compiler support -// -// Implement as "template const sdelims::type sdelims>::values = { ... }." + template + struct is_container> : std::true_type { }; -//template using pp_sdelims = pretty_print::delimiters; -//template using pp_wsdelims = pretty_print::delimiters; + template + struct is_container> : std::true_type { }; + template + struct is_container> : std::true_type { }; -namespace std -{ - // Prints a print_container_helper to the specified stream. - template - inline basic_ostream & operator<<(basic_ostream & stream, - const ::pretty_print::print_container_helper & helper) - { - helper(stream); - return stream; - } + // Default delimiters - // Prints a container to the stream using default delimiters + template struct delimiters { static const delimiters_values values; }; + template const delimiters_values delimiters::values = { "[", ", ", "]" }; + template struct delimiters { static const delimiters_values values; }; + template const delimiters_values delimiters::values = { L"[", L", ", L"]" }; - template - inline typename enable_if< ::pretty_print::is_container::value, basic_ostream&>::type - operator<<(basic_ostream & stream, const T & container) - { - return stream << ::pretty_print::print_container_helper(container); - } - // Prints a pair to the stream using delimiters from delimiters>. - template - inline basic_ostream & operator<<(basic_ostream & stream, const pair & value) - { - if (::pretty_print::delimiters, TChar>::values.prefix != NULL) - stream << ::pretty_print::delimiters, TChar>::values.prefix; + // Delimiters for (multi)set and unordered_(multi)set + + template + struct delimiters< ::std::set, char> { static const delimiters_values values; }; - stream << value.first; + template + const delimiters_values delimiters< ::std::set, char>::values = { "{", ", ", "}" }; - if (::pretty_print::delimiters, TChar>::values.delimiter != NULL) - stream << ::pretty_print::delimiters, TChar>::values.delimiter; + template + struct delimiters< ::std::set, wchar_t> { static const delimiters_values values; }; - stream << value.second; + template + const delimiters_values delimiters< ::std::set, wchar_t>::values = { L"{", L", ", L"}" }; - if (::pretty_print::delimiters, TChar>::values.postfix != NULL) - stream << ::pretty_print::delimiters, TChar>::values.postfix; + template + struct delimiters< ::std::multiset, char> { static const delimiters_values values; }; - return stream; - } -} // namespace std + template + const delimiters_values delimiters< ::std::multiset, char>::values = { "{", ", ", "}" }; -// Prints a tuple to the stream using delimiters from delimiters>. + template + struct delimiters< ::std::multiset, wchar_t> { static const delimiters_values values; }; -namespace pretty_print -{ - struct tuple_dummy_t { }; // Just if you want special delimiters for tuples. + template + const delimiters_values delimiters< ::std::multiset, wchar_t>::values = { L"{", L", ", L"}" }; - typedef std::pair tuple_dummy_pair; + template + struct delimiters< ::std::unordered_set, char> { static const delimiters_values values; }; - template - struct pretty_tuple_helper - { - static inline void print(::std::basic_ostream & stream, const Tuple & value) - { - pretty_tuple_helper::print(stream, value); + template + const delimiters_values delimiters< ::std::unordered_set, char>::values = { "{", ", ", "}" }; - if (delimiters::values.delimiter != NULL) - stream << delimiters::values.delimiter; + template + struct delimiters< ::std::unordered_set, wchar_t> { static const delimiters_values values; }; - stream << std::get(value); - } - }; + template + const delimiters_values delimiters< ::std::unordered_set, wchar_t>::values = { L"{", L", ", L"}" }; + + template + struct delimiters< ::std::unordered_multiset, char> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::unordered_multiset, char>::values = { "{", ", ", "}" }; + + template + struct delimiters< ::std::unordered_multiset, wchar_t> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::unordered_multiset, wchar_t>::values = { L"{", L", ", L"}" }; + + + // Delimiters for pair and tuple - template - struct pretty_tuple_helper + template struct delimiters, char> { static const delimiters_values values; }; + template const delimiters_values delimiters, char>::values = { "(", ", ", ")" }; + template struct delimiters< ::std::pair, wchar_t> { static const delimiters_values values; }; + template const delimiters_values delimiters< ::std::pair, wchar_t>::values = { L"(", L", ", L")" }; + + template struct delimiters, char> { static const delimiters_values values; }; + template const delimiters_values delimiters, char>::values = { "(", ", ", ")" }; + template struct delimiters< ::std::tuple, wchar_t> { static const delimiters_values values; }; + template const delimiters_values delimiters< ::std::tuple, wchar_t>::values = { L"(", L", ", L")" }; + + + // Type-erasing helper class for easy use of custom delimiters. + // Requires TCharTraits = std::char_traits and TChar = char or wchar_t, and MyDelims needs to be defined for TChar. + // Usage: "cout << pretty_print::custom_delims(x)". + + struct custom_delims_base { - static inline void print(::std::basic_ostream & stream, const Tuple & value) - { - stream << ::std::get<0>(value); - } + virtual ~custom_delims_base() { } + virtual std::ostream & stream(::std::ostream &) = 0; + virtual std::wostream & stream(::std::wostream &) = 0; }; -} // namespace pretty_print + template + struct custom_delims_wrapper : custom_delims_base + { + custom_delims_wrapper(const T & t_) : t(t_) { } -/* The following macros allow us to write "template std::tuple" - * uniformly in C++0x compilers and in MS Visual Studio 2010. - * Credits to STL: http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-6-of-n - */ + std::ostream & stream(std::ostream & s) + { + return s << print_container_helper, Delims>(t); + } -#if defined(_MSC_VER) && _MSC_VER == 1600 - #define TUPLE_PARAMS \ - typename T0, typename T1, typename T2, typename T3, typename T4, \ - typename T5, typename T6, typename T7, typename T8, typename T9 - #define TUPLE_ARGS T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 -#else - #define TUPLE_PARAMS typename ...Args - #define TUPLE_ARGS Args... -#endif + std::wostream & stream(std::wostream & s) + { + return s << print_container_helper, Delims>(t); + } + private: + const T & t; + }; -namespace std -{ - template - inline basic_ostream & operator<<(basic_ostream & stream, const tuple & value) + template + struct custom_delims { - if (::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.prefix != NULL) - stream << ::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.prefix; + template + custom_delims(const Container & c) : base(new custom_delims_wrapper(c)) { } - ::pretty_print::pretty_tuple_helper &, tuple_size>::value, TChar, TCharTraits>::print(stream, value); - - if (::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.postfix != NULL) - stream << ::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.postfix; + std::unique_ptr base; + }; - return stream; + template + inline std::basic_ostream & operator<<(std::basic_ostream & s, const custom_delims & p) + { + return p.base->stream(s); } -} // namespace std -// A wrapper for raw C-style arrays. Usage: int arr[] = { 1, 2, 4, 8, 16 }; std::cout << wrap_array(arr) << ... + // A wrapper for a C-style array given as pointer-plus-size. + // Usage: std::cout << pretty_print_array(arr, n) << std::endl; -namespace pretty_print -{ template struct array_wrapper_n { @@ -414,20 +377,11 @@ namespace pretty_print const T * const _array; size_t _n; }; -} // namespace pretty_print -template -inline pretty_print::array_wrapper_n pretty_print_array(const T * const a, size_t n) -{ - return pretty_print::array_wrapper_n(a, 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.) -// 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.) - -namespace pretty_print -{ template struct bucket_print_wrapper { @@ -450,7 +404,17 @@ namespace pretty_print const T & m_map; const size_type n; }; -} // namespace pretty_print + +} // namespace pretty_print + + +// Global accessor functions for the convenience wrappers + +template +inline pretty_print::array_wrapper_n pretty_print_array(const T * const a, size_t n) +{ + return pretty_print::array_wrapper_n(a, n); +} template pretty_print::bucket_print_wrapper bucket_print(const T & m, typename T::size_type n) @@ -459,4 +423,22 @@ bucket_print(const T & m, typename T::size_type n) } -#endif +// 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 + inline typename enable_if< ::pretty_print::is_container::value, + basic_ostream &>::type + operator<<(basic_ostream & stream, const T & container) + { + return stream << ::pretty_print::print_container_helper(container); + } +} + + + +#endif // H_PRETTY_PRINT