美文网首页
【cxx-prettyprint源码学习】print_conta

【cxx-prettyprint源码学习】print_conta

作者: 长不胖的Garfield | 来源:发表于2016-12-22 15:13 被阅读0次

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

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

    知识点

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

    相关文章

      网友评论

          本文标题:【cxx-prettyprint源码学习】print_conta

          本文链接:https://www.haomeiwen.com/subject/vethvttx.html