pprint 源码阅读
上一次一起学习完 TinyFormat
的源码后我们收获很多,那这一次我们同样来看一个用于格式化输出的库 pprint
,也同样是一个 header only
的库。
首先我们同样看一下 pprint
的基础用法(来自 README.md
):
可以看到用法上和 TinyFormat
有一定区别,使用上更贴近 sd::cout<<
而不是 ::printf
。
那么就让我们正式开始吧,首先看一下 PrettyPrinter
的构造方式:
可以看到 PrettyPrinter
接收一个 ostream&
类型的参数用于指明输出的目的流,默认是 std::cout
,另外还设置了行结尾 line_terminator_
, indent_
缩进,以及 quotes_
是否在输出字符串是加上 "
包裹, compact_
这里指的应该是紧凑输出。
紧接着来分析一下 PrettyPrinter::print
函数的实现:
可以看到 print
做了三个重载,第一个是直接调用 print_internal
。第二个接收 std::initializer_list<T>
类型的参数也同样调用 print_internal
,这里为什么要单独调一下原因我们暂时不知道,可以后面再看一下,至于 std::initializer_list
如果大家不清楚的话这里简单说明一下,用大括号 {}
做初始化时实际上就是通过这个类型实现的。
接着是一个接受变长模板参数的重载,首先打印第一个 value
,接着打印分隔符空格,接着递归调用 print
打印剩下的参数。
这里实际上我们知道 print
的实现可以通过折叠表达式优化一下:
怎么样,是不是看上去好多了,唯一的问题就是对于 std::initializer_list
类型的参数我们后续可能还需要特殊处理,那现在先来看一下 print_internal
的实现:
emmm, 我也是惊了,上面我列出的只是一小部分,作者惊人的为每一个内置类型做了单独适配,尤其后面这一大堆类型判断让我属实有点绷不住啊,结合后面的一个给 vector
的一版重载:
还是先简单分析一下这里的用法:
这里 enable_if
的作用是判断 Container
的类型是否为 std::vector
,如果不是那么禁用模板,如果是那么通过 ::type
拓展模板实例的返回类型为 void
,当然 enable_if
只是判断表达式结果为 true_type
还是 false_type
,实际判断是由 is_specialization
实现的:
这是一个比较常见但是经典的实现,由 @Databyte 提出,这里通过特化使得忽略模板参数后类型一致的两个参数可以匹配到特化版本,其 ::type
为 std::true_type
,否则匹配到非特化版本,其 ::type
为 std::false_type
这里举个简单的例子 is_specialization<std::vector<int>, std::vector>
,那么此时我们就可以看到 Ref
的类型为 std::vector
是可以匹配的上的;而对于 is_specialization<std::list<int>, std::vector>
来说就没法匹配特化版本,只能匹配非特化版本此时 Test
为 std::list<int>
, Ref
为 std::vector
。
回到 pprint
,那实际上像这样去实现 print_internal
也就是对每一个可能的情况进行处理是非常不推荐的,更不用说这里用了 enable_if
的方式去禁用模板函数,这样一来一旦 STL 中新增了类型那么除了新增特定的处理外还需要改动上面的 print_internal
声明否则就会导致编译器找到多个函数,至少也应该用特化去实现。
后续的代码那基本也都是一个套路,我们就不继续往下细看了。
下次再会!