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