【cxx-prettyprint源码学习】print_container_helper

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个函数模板调用:

  1. Int<0> 函数模板2
  2. Int<1> 函数模板3
  3. Int<2> 函数模板3
  4. 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")" };

知识点

  • 可变参数模板
  • 模板偏特化

你可能感兴趣的:(【cxx-prettyprint源码学习】print_container_helper)