print_container_helper的作用
print_container_helper
即为输出容器内容的实现模板类
分隔符处理
在输出容器内容是,需要有分隔符来隔开不同元素,并且容器内容要有前导符和后置符,譬如对一个std::vector
,输出的结果格式可以为[0,1,2,3,....]
,还有可能对每种容器的输出格式不一样,因而需要按照容器类型配置分隔符;
首先是分隔符的存储结构体:
template
struct delimiters_values
{
using char_type = TChar;
const char_type * prefix;
const char_type * delimiter;
const char_type * postfix;
};
然后是根据类型的分隔符配置:
template
struct delimiters
{
using type = delimiters_values;
static const type values;
};
注意保存的配置是静态常量。
printer模板类
template
struct printer
{
using delimiters_type = delimiters;
static void print_body(const C& c,std::ostream& 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 != nullptr)
{
stream << delimiters_type::values.delimiter;
}
}
}
}
}
在向ostream输出内容时,需要用到分隔符,因而使用delimiters_type
表示其配置;然后是使用for循环向ostream输出容器元素。
考虑到能够应用到其他输出流类型,可以将模板类做一下调整:
template>
struct printer
然后调整一下ostream:
using stream_type = std::basic_ostream;
这样实现的printer类就可以用来输出容器内容:
printer::print_body(container,std::ostream);
print_container_helper模板类
printer只是输出了容器内部信息,因而做一下封装,输出前导符和后置符:
template ,
typename TDelimiters = delimiters>
struct print_container_helper
{
using delimiters_type = TDelimiters;
using ostream_type = std::basic_ostream;
template
struct printer
{
//内容省略
};
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_;
};
经过封装后,使用如下方式即可输出容器内容:
print_container_type(container)(ostream)
然后在外部提供<<重载:
template
inline std::basic_ostream & operator<<(
std::basic_ostream & stream,
const print_container_helper & helper)
{
helper(stream);
return stream;
}
这样使用方法如下:
ostream<(container);
嵌入到std命名空间
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);
}
}
支持打印pair
由于只是输出容器,如果需要支持pair类型,可以偏特化pair版本的print_container_helper
:
template
template
struct print_container_helper::printer>
{
using ostream_type = typename 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;
}
};
并放开is_container对pair的限制:
template
struct is_container> : std::true_type { };
支持打印tuple
tuple是可变的,因而需要可变参数模板支持,下面看一下是如何使用可变参数模板输出tuple内容的:
template
template
struct print_container_helper::printer>
{
using ostream_type = typename print_container_helper::ostream_type;
using element_type = std::tuple;
template struct Int { };
static void print_body(const element_type & c, ostream_type & stream)
{
tuple_print(c, stream, Int<0>());
}
//函数模板1
static void tuple_print(const element_type &, ostream_type &, Int)
{
}
//函数模板2
static void tuple_print(const element_type & c, ostream_type & stream,
typename std::conditional, std::nullptr_t>::type)
{
stream << std::get<0>(c);
tuple_print(c, stream, Int<1>());
}
//函数模板3
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());
}
};
那么假设tuple为tuple
,那么三个函数模板分别为
- 函数模板1
tuple_print(const element_type &, ostream_type &, Int<3>)
当第三个参数为Int<3>
时,会使用函数模板1 - 函数模板2
tuple_print(const element_type &, ostream_type &, Int<0>)
当第三个参数为Int<0>
时,会使用函数模板2 - 函数模板3
template
static void tuple_print(const element_type & c, ostream_type & stream, Int)
当第三个参数为Int
时,会使用函数模板3
根据上述过程,print_body
会被展开成4个函数模板调用:
- Int<0> 函数模板2
- Int<1> 函数模板3
- Int<2> 函数模板3
- Int<3> 函数模板1
然后放开is_container对tuple的限制:
template
struct is_container> : std::true_type { };
定义分隔符
使用模板的偏特化,初始化不同情况下的分隔符:
- 默认分隔符
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"]" };
- 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"}" };
- pair的分隔符
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")" };
知识点
- 可变参数模板
- 模板偏特化