soi-header/include/bits/prettyprint.hpp

497 lines
16 KiB
C++
Raw Normal View History

2019-10-06 16:45:29 +02:00
// 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++
//
// The global operator<< overload hs been removed
#ifndef SOI_PRETTY_PRINT
#define SOI_PRETTY_PRINT
2019-10-06 16:45:29 +02:00
#include <cstddef>
#include <iterator>
#include <memory>
#include <ostream>
#include <set>
#include <tuple>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <valarray>
2019-10-06 18:37:37 +02:00
namespace soi {
2019-10-06 18:38:23 +02:00
template <typename T, typename TChar, typename TCharTraits>
2019-10-06 18:37:37 +02:00
std::basic_ostream<TChar, TCharTraits> &
2019-10-06 18:38:23 +02:00
print(std::basic_ostream<TChar, TCharTraits> &stream, const T &x);
2019-10-06 18:37:37 +02:00
2019-10-06 18:38:23 +02:00
namespace detail {
2019-10-06 18:37:37 +02:00
// SFINAE type trait to detect whether T::const_iterator exists.
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
struct sfinae_base {
using yes = char;
using no = yes[2];
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T> struct has_const_iterator : private sfinae_base {
2019-10-06 18:37:37 +02:00
private:
2019-10-06 18:38:23 +02:00
template <typename C> static yes &test(typename C::const_iterator *);
template <typename C> static no &test(...);
2019-10-06 18:37:37 +02:00
public:
2019-10-06 18:38:23 +02:00
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
using type = T;
2019-10-06 18:37:37 +02:00
};
2019-10-06 18:38:23 +02:00
template <typename T> struct has_begin_end : private sfinae_base {
2019-10-06 18:37:37 +02:00
private:
2019-10-06 18:38:23 +02:00
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 *);
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename C> static no &f(...);
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
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 *);
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename C> static no &g(...);
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
public:
2019-10-06 18:38:23 +02:00
static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
} // namespace detail
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Holds the delimiter values for a specific character type
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename TChar> struct delimiters_values {
using char_type = TChar;
const char_type *prefix;
const char_type *delimiter;
const char_type *postfix;
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Defines the delimiter values for a specific container and character type
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T, typename TChar> struct delimiters {
using type = delimiters_values<TChar>;
static const type values;
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Functor to pretty_print containers. You can use this directly if you want
// to specificy a non-default delimiters type. The pretty_printing logic can
// be customized by specializing the nested template.
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T, typename TChar = char,
2019-10-06 18:37:37 +02:00
typename TCharTraits = ::std::char_traits<TChar>,
typename TDelimiters = delimiters<T, TChar>>
2019-10-06 18:38:23 +02:00
struct pretty_print_container_helper {
using delimiters_type = TDelimiters;
using ostream_type = std::basic_ostream<TChar, TCharTraits>;
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename U> struct pretty_printer {
static void pretty_print_body(const U &c, ostream_type &stream) {
using std::begin;
using std::end;
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
auto it = begin(c);
const auto the_end = end(c);
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
if (it != the_end) {
for (;;) {
::soi::print(stream, *it);
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
if (++it == the_end)
break;
if (delimiters_type::values.delimiter != NULL)
::soi::print(stream, delimiters_type::values.delimiter);
}
}
2019-10-06 18:37:37 +02:00
}
2019-10-06 18:38:23 +02:00
};
pretty_print_container_helper(const T &container) : container_(container) {}
inline void operator()(ostream_type &stream) const {
if (delimiters_type::values.prefix != NULL)
::soi::print(stream, delimiters_type::values.prefix);
pretty_printer<T>::pretty_print_body(container_, stream);
if (delimiters_type::values.postfix != NULL)
::soi::print(stream, delimiters_type::values.postfix);
}
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
private:
2019-10-06 18:38:23 +02:00
const T &container_;
2019-10-06 18:37:37 +02:00
};
// Specialization for pairs
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T, typename TChar, typename TCharTraits,
typename TDelimiters>
2019-10-06 18:37:37 +02:00
template <typename T1, typename T2>
2019-10-06 18:38:23 +02:00
struct pretty_print_container_helper<
T, TChar, TCharTraits, TDelimiters>::pretty_printer<std::pair<T1, T2>> {
using ostream_type =
typename pretty_print_container_helper<T, TChar, TCharTraits,
TDelimiters>::ostream_type;
static void pretty_print_body(const std::pair<T1, T2> &c,
ostream_type &stream) {
::soi::print(stream, c.first);
if (pretty_print_container_helper<T, TChar, TCharTraits,
TDelimiters>::delimiters_type::values
.delimiter != NULL)
::soi::print(
stream,
pretty_print_container_helper<T, TChar, TCharTraits,
TDelimiters>::delimiters_type::values
.delimiter);
::soi::print(stream, c.second);
}
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Specialization for tuples
2019-10-06 18:38:23 +02:00
template <typename T, typename TChar, typename TCharTraits,
typename TDelimiters>
template <typename... Args>
struct pretty_print_container_helper<
T, TChar, TCharTraits, TDelimiters>::pretty_printer<std::tuple<Args...>> {
using ostream_type =
typename pretty_print_container_helper<T, TChar, TCharTraits,
TDelimiters>::ostream_type;
using element_type = std::tuple<Args...>;
template <std::size_t I> struct Int {};
static void pretty_print_body(const element_type &c, ostream_type &stream) {
tuple_pretty_print(c, stream, Int<0>());
}
static void tuple_pretty_print(const element_type &, ostream_type &,
Int<sizeof...(Args)>) {}
static void
tuple_pretty_print(const element_type &c, ostream_type &stream,
typename std::conditional<sizeof...(Args) != 0, Int<0>,
std::nullptr_t>::type) {
::soi::print(stream, std::get<0>(c));
tuple_pretty_print(c, stream, Int<1>());
}
template <std::size_t N>
static void tuple_pretty_print(const element_type &c, ostream_type &stream,
Int<N>) {
if (pretty_print_container_helper<T, TChar, TCharTraits,
TDelimiters>::delimiters_type::values
.delimiter != NULL)
::soi::print(
stream,
pretty_print_container_helper<T, TChar, TCharTraits,
TDelimiters>::delimiters_type::values
.delimiter);
::soi::print(stream, std::get<N>(c));
tuple_pretty_print(c, stream, Int<N + 1>());
}
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Pretty_Prints a pretty_print_container_helper to the specified stream.
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T, typename TChar, typename TCharTraits,
typename TDelimiters>
inline std::basic_ostream<TChar, TCharTraits> &
pretty_print(std::basic_ostream<TChar, TCharTraits> &stream,
const pretty_print_container_helper<T, TChar, TCharTraits,
TDelimiters> &helper) {
helper(stream);
return stream;
2019-10-06 18:37:37 +02:00
}
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
// Basic is_container template; specialize to derive from std::true_type for all
// desired container types
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T>
2019-10-06 18:38:23 +02:00
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> {};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, std::size_t N>
2019-10-06 18:38:23 +02:00
struct is_container<T[N]> : std::true_type {};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <std::size_t N> struct is_container<char[N]> : std::false_type {};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T> struct is_container<std::valarray<T>> : std::true_type {};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T1, typename T2>
2019-10-06 18:38:23 +02:00
struct is_container<std::pair<T1, T2>> : std::true_type {};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename... Args>
struct is_container<std::tuple<Args...>> : std::true_type {};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Default delimiters
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
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"]"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Delimiters for (multi)set and unordered_(multi)set
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::set<T, TComp, TAllocator>, char> {
static const delimiters_values<char> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<char>
delimiters<::std::set<T, TComp, TAllocator>, char>::values = {"{", ", ",
"}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::set<T, TComp, TAllocator>, wchar_t> {
static const delimiters_values<wchar_t> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<wchar_t>
delimiters<::std::set<T, TComp, TAllocator>, wchar_t>::values = {
L"{", L", ", L"}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::multiset<T, TComp, TAllocator>, char> {
static const delimiters_values<char> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<char> delimiters<::std::multiset<T, TComp, TAllocator>,
char>::values = {"{", ", ", "}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::multiset<T, TComp, TAllocator>, wchar_t> {
static const delimiters_values<wchar_t> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename TComp, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<wchar_t>
delimiters<::std::multiset<T, TComp, TAllocator>, wchar_t>::values = {
L"{", L", ", L"}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::unordered_set<T, THash, TEqual, TAllocator>, char> {
static const delimiters_values<char> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<char> delimiters<
::std::unordered_set<T, THash, TEqual, TAllocator>, char>::values = {
"{", ", ", "}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t> {
static const delimiters_values<wchar_t> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<wchar_t> delimiters<
::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t>::values = {
L"{", L", ", L"}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::unordered_multiset<T, THash, TEqual, TAllocator>,
char> {
static const delimiters_values<char> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<char> delimiters<
::std::unordered_multiset<T, THash, TEqual, TAllocator>, char>::values = {
"{", ", ", "}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
struct delimiters<::std::unordered_multiset<T, THash, TEqual, TAllocator>,
wchar_t> {
static const delimiters_values<wchar_t> values;
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename THash, typename TEqual, typename TAllocator>
2019-10-06 18:38:23 +02:00
const delimiters_values<wchar_t>
delimiters<::std::unordered_multiset<T, THash, TEqual, TAllocator>,
wchar_t>::values = {L"{", L", ", L"}"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Delimiters for pair and tuple
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
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")"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
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")"};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// Type-erasing helper class for easy use of custom delimiters.
2019-10-06 18:38:23 +02:00
// Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t,
// and MyDelims needs to be defined for TChar. Usage: "cout <<
// pretty_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;
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename T, typename Delims>
2019-10-06 18:38:23 +02:00
struct custom_delims_wrapper : custom_delims_base {
custom_delims_wrapper(const T &t_) : t(t_) {}
std::ostream &stream(std::ostream &s) {
return pretty_print(
s,
pretty_print_container_helper<T, char, std::char_traits<char>, Delims>(
t));
}
std::wostream &stream(std::wostream &s) {
return pretty_print(
s, pretty_print_container_helper<T, wchar_t, std::char_traits<wchar_t>,
Delims>(t));
}
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
private:
2019-10-06 18:38:23 +02:00
const T &t;
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename Delims> struct custom_delims {
template <typename Container>
custom_delims(const Container &c)
: base(new custom_delims_wrapper<Container, Delims>(c)) {}
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
std::unique_ptr<custom_delims_base> base;
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
template <typename TChar, typename TCharTraits, typename Delims>
2019-10-06 18:38:23 +02:00
inline std::basic_ostream<TChar, TCharTraits> &
pretty_print(std::basic_ostream<TChar, TCharTraits> &s,
const custom_delims<Delims> &p) {
return p.base->stream(s);
2019-10-06 18:37:37 +02:00
}
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
// A wrapper for a C-style array given as pointer-plus-size.
// Usage: std::cout << pretty_pretty_print_array(arr, n) << std::endl;
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T> struct array_wrapper_n {
typedef const T *const_iterator;
typedef T value_type;
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
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; }
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
private:
2019-10-06 18:38:23 +02:00
const T *const _array;
size_t _n;
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
// A wrapper for hash-table based containers that offer local iterators to each
// bucket. Usage: std::cout << bucket_pretty_print(m, 4) << std::endl;
// (Pretty_Prints bucket 5 of container m.)
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T> struct bucket_pretty_print_wrapper {
typedef typename T::const_local_iterator const_iterator;
typedef typename T::size_type size_type;
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
const_iterator begin() const { return m_map.cbegin(n); }
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
const_iterator end() const { return m_map.cend(n); }
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
bucket_pretty_print_wrapper(const T &m, size_type bucket)
: m_map(m), n(bucket) {}
2019-10-06 16:45:29 +02:00
2019-10-06 18:37:37 +02:00
private:
2019-10-06 18:38:23 +02:00
const T &m_map;
const size_type n;
2019-10-06 18:37:37 +02:00
};
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
} // namespace soi
2019-10-06 16:45:29 +02:00
/*
// Global accessor functions for the convenience wrappers
template<typename T>
2019-10-06 18:38:23 +02:00
inline pretty_pretty_print::array_wrapper_n<T> pretty_pretty_print_array(const T
* const a, size_t n)
2019-10-06 16:45:29 +02:00
{
2019-10-06 18:37:37 +02:00
return pretty_pretty_print::array_wrapper_n<T>(a, n);
2019-10-06 16:45:29 +02:00
}
2019-10-06 18:37:37 +02:00
template <typename T> pretty_pretty_print::bucket_pretty_print_wrapper<T>
bucket_pretty_print(const T & m, typename T::size_type n)
2019-10-06 16:45:29 +02:00
{
2019-10-06 18:37:37 +02:00
return pretty_pretty_print::bucket_pretty_print_wrapper<T>(m, n);
2019-10-06 16:45:29 +02:00
}
// Main magic entry point: An overload snuck into namespace std.
// Can we do better?
2019-10-06 18:37:37 +02:00
*/
2019-10-06 18:38:23 +02:00
namespace soi {
2019-10-06 18:37:37 +02:00
// Pretty_Prints a container to the stream using default delimiters
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
template <typename T, typename TChar, typename TCharTraits>
inline typename std::enable_if<::soi::is_container<T>::value,
std::basic_ostream<TChar, TCharTraits> &>::type
pretty_print(std::basic_ostream<TChar, TCharTraits> &stream,
const T &container) {
return ::soi::print(
stream,
::soi::pretty_print_container_helper<T, TChar, TCharTraits>(container));
2019-10-06 16:45:29 +02:00
}
2019-10-06 18:38:23 +02:00
} // namespace soi
2019-10-06 16:45:29 +02:00
2019-10-06 18:38:23 +02:00
#endif // SOI_PRETTY_PRETTY_PRINT