494 lines
16 KiB
C++
494 lines
16 KiB
C++
// 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);
|
|
}
|
|
} // namespace std
|
|
|
|
#endif // H_PRETTY_PRINT
|