请注意本文末尾的更新 .
Update: I have created a public project on GitHub for this library!
我希望有一个模板,一劳永逸地通过 operator<<
来打印所有STL容器 . 在伪代码中,我正在寻找这样的东西:
template<container C, class T, String delim = ", ", String open = "[", String close = "]">
std::ostream & operator<<(std::ostream & o, const C<T> & x)
{
o << open;
// for (typename C::const_iterator i = x.begin(); i != x.end(); i++) /* Old-school */
for (auto i = x.begin(); i != x.end(); i++)
{
if (i != x.begin()) o << delim;
o << *i;
}
o << close;
return o;
}
现在我已经在SO上看到了很多我从未想过可能的模板魔法,所以我想知道是否有人可以建议一些能匹配所有容器C的东西 . 也许某些特性可以判断出某些东西是否有必要的迭代器?
非常感谢!
Update (and solution)
在Channel 9上再次提出这个问题之后,我从Sven Groot得到了一个很棒的答案,结合了一点SFINAE类型的特征,似乎以一种完全通用和可嵌套的方式解决了这个问题 . 分隔符可以是单独的专用,包括std :: set的示例特化,以及使用自定义分隔符的示例 .
帮助程序"wrap_array()"可用于打印原始C数组 . 更新:对和元组可用于打印;默认分隔符是圆括号 .
enable-if类型特征需要C 0x,但经过一些修改后,应该可以制作C 98版本 . 元组需要可变参数模板,因此需要C 0x .
我已经要求Sven在这里发布解决方案以便我可以接受它,但与此同时我想自己发布代码以供参考 . (更新:Sven现在已经在下面发布了他的代码,我做出了接受的答案 . 我自己的代码使用容器类型特征,这对我有用,但可能会导致非容器类提供迭代器的意外行为 . )
Headers (prettyprint.h):
#ifndef H_PRETTY_PRINT
#define H_PRETTY_PRINT
#include <type_traits>
#include <iostream>
#include <utility>
#include <tuple>
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.
template<typename T, typename TTraits, typename TAllocator> class set;
}
namespace pretty_print
{
// SFINAE type trait to detect a container based on whether T::const_iterator exists.
// (Improvement idea: check also if begin()/end() exist.)
template<typename T>
struct is_container_helper
{
private:
template<typename C> static char test(typename C::const_iterator*);
template<typename C> static int test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
// 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, is_container_helper<T>::value> { };
// Holds the delimiter values for a specific character type
template<typename TChar>
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<typename T, typename TChar>
struct delimiters
{
typedef delimiters_values<TChar> type;
static const type values;
};
// 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 set
template<typename T, typename TTraits, typename TAllocator> struct delimiters< ::std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters< ::std::set<T, TTraits, TAllocator>, char>::values = { "{", ", ", "}" };
template<typename T, typename TTraits, typename TAllocator> struct delimiters< ::std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters< ::std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
// Delimiters for pair (reused for tuple, see below)
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")" };
// Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type.
template<typename T, typename TChar = char, typename TCharTraits = ::std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar>>
struct print_container_helper
{
typedef TChar char_type;
typedef TDelimiters delimiters_type;
typedef std::basic_ostream<TChar, TCharTraits> & ostream_type;
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;
for (typename T::const_iterator beg = _container.begin(), end = _container.end(), it = beg; it != end; ++it)
{
if (it != beg && delimiters_type::values.delimiter != NULL)
stream << delimiters_type::values.delimiter;
stream << *it;
}
if (delimiters_type::values.postfix != NULL)
stream << delimiters_type::values.postfix;
}
private:
const T & _container;
};
// 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 : public custom_delims_base
{
custom_delims_wrapper(const T & t) : t(t) { }
::std::ostream & stream(::std::ostream & stream)
{
return stream << ::pretty_print::print_container_helper<T, char, ::std::char_traits<char>, Delims>(t);
}
::std::wostream & stream(::std::wostream & stream)
{
return stream << ::pretty_print::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)) { }
~custom_delims() { delete base; }
custom_delims_base * base;
};
} // namespace pretty_print
template <typename TChar, typename TCharTraits, typename Delims>
inline std::basic_ostream<TChar, TCharTraits> & operator<<(std::basic_ostream<TChar, TCharTraits> & stream, const pretty_print::custom_delims<Delims> & p)
{
return p.base->stream(stream);
}
// Template aliases for char and wchar_t delimiters
// Enable these if you have compiler support
//
// Implement as "template<T, C, A> const sdelims::type sdelims<std::set<T,C,A>>::values = { ... }."
//template<typename T> using pp_sdelims = pretty_print::delimiters<T, char>;
//template<typename T> using pp_wsdelims = pretty_print::delimiters<T, wchar_t>;
namespace std
{
// Prints a print_container_helper to the specified stream.
template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream,
const ::pretty_print::print_container_helper<T, TChar, TCharTraits, TDelimiters> & helper)
{
helper(stream);
return stream;
}
// 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);
}
// Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>.
template<typename T1, typename T2, typename TChar, typename TCharTraits>
inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream, const pair<T1, T2> & value)
{
if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.prefix != NULL)
stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.prefix;
stream << value.first;
if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.delimiter != NULL)
stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.delimiter;
stream << value.second;
if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.postfix != NULL)
stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.postfix;
return stream;
}
} // namespace std
// Prints a tuple to the stream using delimiters from delimiters<std::pair<tuple_dummy_t, tuple_dummy_t>>.
namespace pretty_print
{
struct tuple_dummy_t { }; // Just if you want special delimiters for tuples.
typedef std::pair<tuple_dummy_t, tuple_dummy_t> tuple_dummy_pair;
template<typename Tuple, size_t N, typename TChar, typename TCharTraits>
struct pretty_tuple_helper
{
static inline void print(::std::basic_ostream<TChar, TCharTraits> & stream, const Tuple & value)
{
pretty_tuple_helper<Tuple, N - 1, TChar, TCharTraits>::print(stream, value);
if (delimiters<tuple_dummy_pair, TChar>::values.delimiter != NULL)
stream << delimiters<tuple_dummy_pair, TChar>::values.delimiter;
stream << std::get<N - 1>(value);
}
};
template<typename Tuple, typename TChar, typename TCharTraits>
struct pretty_tuple_helper<Tuple, 1, TChar, TCharTraits>
{
static inline void print(::std::basic_ostream<TChar, TCharTraits> & stream, const Tuple & value) { stream << ::std::get<0>(value); }
};
} // namespace pretty_print
namespace std
{
template<typename TChar, typename TCharTraits, typename ...Args>
inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream, const tuple<Args...> & value)
{
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;
::pretty_print::pretty_tuple_helper<const tuple<Args...> &, sizeof...(Args), 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;
return stream;
}
} // namespace std
// A wrapper for raw C-style arrays. Usage: int arr[] = { 1, 2, 4, 8, 16 }; std::cout << wrap_array(arr) << ...
namespace pretty_print
{
template <typename T, size_t N>
struct array_wrapper
{
typedef const T * const_iterator;
typedef T value_type;
array_wrapper(const T (& a)[N]) : _array(a) { }
inline const_iterator begin() const { return _array; }
inline const_iterator end() const { return _array + N; }
private:
const T * const _array;
};
} // namespace pretty_print
template <typename T, size_t N>
inline pretty_print::array_wrapper<T, N> pretty_print_array(const T (& a)[N])
{
return pretty_print::array_wrapper<T, N>(a);
}
#endif
用法示例:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <map>
#include <set>
#include <array>
#include <tuple>
#include <utility>
#include <string>
#include "prettyprint.h"
// Specialization for a particular container
template<> const pretty_print::delimiters_values<char> pretty_print::delimiters<std::vector<double>, char>::values = { "|| ", " : ", " ||" };
// Custom delimiters for one-off use
struct MyDel { static const delimiters_values<char> values; };
const delimiters_values<char> MyDel::values = { "<", "; ", ">" };
int main(int argc, char * argv[])
{
std::string cs;
std::unordered_map<int, std::string> um;
std::map<int, std::string> om;
std::set<std::string> ss;
std::vector<std::string> v;
std::vector<std::vector<std::string>> vv;
std::vector<std::pair<int, std::string>> vp;
std::vector<double> vd;
v.reserve(argc - 1);
vv.reserve(argc - 1);
vp.reserve(argc - 1);
vd.reserve(argc - 1);
std::cout << "Printing pairs." << std::endl;
while (--argc)
{
std::string s(argv[argc]);
std::pair<int, std::string> p(argc, s);
um[argc] = s;
om[argc] = s;
v.push_back(s);
vv.push_back(v);
vp.push_back(p);
vd.push_back(1./double(i));
ss.insert(s);
cs += s;
std::cout << " " << p << std::endl;
}
std::array<char, 5> a{{ 'h', 'e', 'l', 'l', 'o' }};
std::cout << "Vector: " << v << std::endl
<< "Incremental vector: " << vv << std::endl
<< "Another vector: " << vd << std::endl
<< "Pairs: " << vp << std::endl
<< "Set: " << ss << std::endl
<< "OMap: " << om << std::endl
<< "UMap: " << um << std::endl
<< "String: " << cs << std::endl
<< "Array: " << a << std::endl
;
// Using custom delimiters manually:
std::cout << pretty_print::print_container_helper<std::vector<std::string>, char, std::char_traits<char>, MyDel>(v) << std::endl;
// Using custom delimiters with the type-erasing helper class
std::cout << pretty_print::custom_delims<MyDel>(v) << std::endl;
// Pairs and tuples and arrays:
auto a1 = std::make_pair(std::string("Jello"), 9);
auto a2 = std::make_tuple(1729);
auto a3 = std::make_tuple("Qrgh", a1, 11);
auto a4 = std::make_tuple(1729, 2875, std::pair<double, std::string>(1.5, "meow"));
int arr[] = { 1, 4, 9, 16 };
std::cout << "C array: " << wrap_array(arr) << std::endl
<< "Pair: " << a1 << std::endl
<< "1-tuple: " << a2 << std::endl
<< "n-tuple: " << a3 << std::endl
<< "n-tuple: " << a4 << std::endl
;
}
进一步改进的想法:
-
以与std :: pair <S,T>相同的方式实现std :: tuple <...>的输出 . 更新:现在是separate question on SO! Upupdate:感谢Xeo,现在已经实现了!
-
添加名称空间,以便辅助类不会渗透到全局名称空间中 . 完成
-
添加模板别名(或类似的东西)以便于制作自定义分隔符类,或者可能是预处理器宏?
最近更新:
-
我删除了自定义输出迭代器,转而使用print函数中的简单for循环 .
-
所有实现细节现在都在
pretty_print
命名空间中 . 只有全局流运算符和pretty_print_array
包装器位于全局命名空间中 . -
修复了命名空间,以便
operator<<
现在正确地位于std
中 .
笔记:
-
删除输出迭代器意味着无法使用
std::copy()
进行漂亮的打印 . 如果这是一个理想的功能,我可能会恢复漂亮的迭代器,但下面的Sven代码有实现 . -
这是一个有意识的设计决定,使分隔符编译时常量而不是对象常量 . 这意味着您无法在运行时动态提供分隔符,但这也意味着下面有's no unneeded overhead. An object-based delimiter configuration has been proposed by Dennis Zickefoose in a comment to Sven'代码 . 如果需要,这可以作为替代功能实现 .
-
目前尚不清楚如何自定义嵌套容器分隔符 .
-
请记住,此库的目的是允许快速容器打印设备,您需要零编码 . 它不是一个通用的格式化库,而是一个开发工具,以减少编写容器检查的样板代码的需要 .
谢谢所有贡献的人!
注意:如果您正在寻找一种快速部署自定义分隔符的方法,这里有一种使用类型擦除的方法 . 我们假设你已经构造了一个分隔符类,比如 MyDel
,就像这样:
struct MyDel { static const pretty_print::delimiters_values<char> values; };
const pretty_print::delimiters_values<char> MyDel::values = { "<", "; ", ">" };
现在我们希望能够使用这些分隔符为某个容器 v
编写 std::cout << MyPrinter(v) << std::endl;
. MyPrinter
将是一个类型擦除类,如下所示:
struct wrapper_base
{
virtual ~wrapper_base() { }
virtual std::ostream & stream(std::ostream & o) = 0;
};
template <typename T, typename Delims>
struct wrapper : public wrapper_base
{
wrapper(const T & t) : t(t) { }
std::ostream & stream(std::ostream & o)
{
return o << pretty_print::print_container_helper<T, char, std::char_traits<char>, Delims>(t);
}
private:
const T & t;
};
template <typename Delims>
struct MyPrinter
{
template <typename Container> MyPrinter(const Container & c) : base(new wrapper<Container, Delims>(c)) { }
~MyPrinter() { delete base; }
wrapper_base * base;
};
template <typename Delims>
std::ostream & operator<<(std::ostream & o, const MyPrinter<Delims> & p) { return p.base->stream(o); }
7 回答
这是一个工作库,作为一个完整的工作程序,我刚刚一起攻击:
它目前仅适用于
vector
和set
,但只需扩展IsContainer
特化,即可与大多数容器一起使用 . 我没有立即想到任何我可以删除多余的东西 .EDIT: 仅仅是因为踢,我包括一个处理数组的版本 . 我不得不排除char数组以避免进一步的模糊;
wchar_t[]
可能仍会遇到麻烦 .我将在这里添加另一个答案,因为我已经提出了与前一个方法不同的方法,那就是使用区域设置方面 .
基础是here
基本上你做的是:
创建一个派生自
std::locale::facet
的类 . 稍微缺点是你需要一个编译单元来保存它的id . 让's call it MyPrettyVectorPrinter. You' d可能给它一个更好的名字,并为对和 Map 创建一个 .在您的信息流功能中,您检查
std::has_facet< MyPrettyVectorPrinter >
如果返回true,则使用
std::use_facet< MyPrettyVectorPrinter >( os.getloc() )
将其解压缩您的facet对象将具有分隔符的值,您可以阅读它们 . 如果方面不是发现,您的打印功能(
operator<<
)提供默认值 . 请注意,您可以为读取矢量执行相同的操作 .我喜欢这种方法,因为您可以使用默认打印,同时仍然可以使用自定义覆盖 .
如果在多个项目中使用(不仅仅是 Headers ),还需要注意创建新语言环境对象的费用这一事实的缺点是需要一个库 .
我把它写成一个新的解决方案,而不是修改我的另一个,因为我相信这两种方法都是正确的,你可以选择 .
我的解决方案是simple.h,它是scc包的一部分 . 所有标准容器, Map ,集合,c阵列都是可打印的 .
这个解决方案的灵感来自Marcelo的解决方案,但有一些变化:
与Marcelo的版本一样,它使用is_container类型特征,该特征必须专用于所有要支持的容器 . 也许可以使用特征来检查
value_type
,const_iterator
,begin()
/end()
,但我建议,因为它可能匹配符合这些条件但实际上不是容器的东西,例如std::basic_string
. 与Marcelo的版本一样,它使用专门用于指定要使用的分隔符的模板 .主要区别在于我已经围绕
pretty_ostream_iterator
构建了我的版本,其工作方式与std::ostream_iterator
类似,但在最后一项之后不会打印分隔符 . 格式化容器由print_container_helper
完成,可以直接用于打印没有is_container特征的容器,或者指定不同的分隔符类型 .我还定义了is_container和delimiters,因此它适用于具有非标准谓词或分配器的容器,以及char和wchar_t . operator <<函数本身也被定义为与char和wchar_t流一起使用 .
最后,我使用了
std::enable_if
,它作为C 0x的一部分提供,适用于Visual C 2010和g 4.3(需要-std = c 0x标志)及更高版本 . 这种方式不依赖于Boost .这已被编辑了几次,我们决定调用包装集合RangePrinter的主类
一旦编写了一次性操作符<< overload,这应该可以自动与任何集合一起使用,除了您需要一个特殊的映射来打印该对,并且可能想要在那里自定义分隔符 .
您还可以在项目上使用特殊的“打印”功能,而不是直接输出 . 有点像STL算法允许您传递自定义谓词 . 使用map,您可以使用这种方式,使用std :: pair的自定义打印机 .
您的“默认”打印机只会将其输出到流 .
好的,让我们开始使用自定义打印机 . 我将我的外部类更改为RangePrinter . 所以我们有2个迭代器和一些分隔符,但没有自定义如何打印实际项目 .
现在默认情况下它只适用于 Map ,只要键和值类型都是可打印的,你可以放入你自己的特殊项目打印机,当它们不是(你可以使用任何其他类型),或者如果你不想要=作为分隔符 .
我正在移动自由函数来创建这些到现在为止:
一个自由函数(迭代器版本)看起来像这样,你甚至可以有默认值:
然后你可以将它用于std :: set by
您还可以编写采用自定义打印机的自由功能版本和采用两个迭代器的自由功能版本 . 在任何情况下,他们都会为您解析模板参数,您将能够将它们作为临时文件传递出去 .
这里的目标是使用ADL来定制我们如何打印 .
传入格式化程序标记,并覆盖标记命名空间中的4个函数(之前,之后,之间和之后) . 这会改变格式化程序在迭代容器时打印“装饰”的方式 .
一个默认格式化程序,用于映射
{(a->b),(c->d)}
,用于元组的(a,b,c)
,用于字符串的"hello"
,用于包含其他所有内容的[x,y,z]
.它应该“正常工作”第三方可迭代类型(并将它们视为“其他所有”) .
如果您想要第三方迭代的自定义装饰,只需创建自己的标签即可 . 处理 Map 下降需要一些工作(您需要重载
pretty_print_descend( your_tag
以返回pretty_print::decorator::map_magic_tag<your_tag>
) . 也许有一种更清洁的方法来做到这一点,不确定 .一个小库来检测迭代性和元组:
一个允许我们访问可迭代或元组类型对象内容的库:
一个漂亮的打印库:
测试代码:
live example
这确实使用了C 14特性(一些
_t
别名和auto&&
lambdas),但没有一个是必不可少的 .这个代码现在已经好几次被证明是有用的了,我觉得进入定制的费用非常低 . 因此,我决定在 MIT 许可下发布它,并提供一个GitHub存储库,其中可以下载 Headers 和一个小示例文件 .
http://djmuw.github.io/prettycc
0.前言和措辞
根据此答案, 'decoration' 是一组前缀字符串,分隔符字符串和后缀字符串 . 前缀字符串插入到流之前和后缀字符串之后的容器值(请参阅2.目标容器) . 分隔符字符串插入相应容器的值之间 .
注意:实际上,这个答案没有解决100%的问题,因为装饰不是严格编译的时间常数,因为需要运行时检查来检查自定义装饰是否已应用于当前流 . 不过,我认为它有一些不错的功能 .
注2:可能有小错误,因为它尚未经过充分测试 .
1.一般想法/用法
使用所需的零附加代码
它应该像保持一样简单
轻松定制......
...关于特定流对象
或者对于所有流:
粗略描述
该代码包括一个类模板,为任何类型提供默认装饰
可以专门用于更改(a)某些类型的默认装饰,它是
使用
ios_base
提供的私有存储使用xalloc
/pword
,以便保存指向pretty::decor
对象的指针,该对象专门装饰某个流上的某种类型 .如果未明确设置此流的
pretty::decor<T>
对象,则调用pretty::defaulted<T, charT, chartraitT>::decoration()
以获取给定类型的默认装饰 . 类pretty::defaulted
将专门用于自定义默认装饰 .2.目标对象/容器
此代码的'pretty decoration'的目标对象
obj
是具有其中任何一个的对象重载
std::begin
和std::end
已定义(包括C样式数组),通过ADL提供
begin(obj)
和end(obj)
,的类型
std::tuple
或类型
std::pair
.该代码包括用于识别具有范围特征的类的特征(
begin
/end
) . (但是没有包括检查,begin(obj) == end(obj)
是否是一个有效的表达式 . )该代码在全局命名空间中提供
operator<<
,仅适用于没有更专用的operator<<
版本的类 . 因此,例如,虽然具有有效的begin
/end
对,但在此代码中不使用运算符打印std::string
.3.利用率和自定义
可以为每种类型(除了不同的
tuple
)和流(不是流类型!)单独强加装饰 . (即,std::vector<int>
可以为不同的流对象提供不同的装饰 . )A)默认装饰
默认前缀是
""
(无),默认后缀是默认前缀,而默认前缀是", "
(逗号空格) .B)通过专门化pretty :: defaulted类模板来定制类型的默认装饰
struct defaulted
具有静态成员函数decoration()
,返回decor
对象,其中包含给定类型的默认值 .使用数组的示例:
自定义默认阵列打印:
打印arry数组:
为char流使用PRETTY_DEFAULT_DECORATION(TYPE,PREFIX,DELIM,POSTFIX,...)宏
宏扩大到
使上述部分专业化能够被重写
或插入完整的专业化
包含了
wchar_t
流的另一个宏:PRETTY_DEFAULT_WDECORATION
.C)在溪流上施加装饰
函数
pretty::decoration
用于在特定流上强加装饰 . 有重载采取 - 一个字符串参数是分隔符(从默认类采用前缀和后缀) - 或三个字符串参数组装完整的装饰给定类型和流的完整装饰
为给定流定制分隔符
4. std :: tuple的特殊处理
此代码不是允许对每个可能的元组类型进行特化,而是将
std::tuple<void*>
可用的任何装饰应用于所有类型的std::tuple<...>
.5.从流中删除自定义装饰
要返回给定类型的默认装饰,请在流
s
上使用pretty::clear
功能模板 .5.更多例子
使用换行符分隔符打印“矩阵状”
打印
在ideone / KKUebZ上看到它
6.代码