zoukankan      html  css  js  c++  java
  • 第27课 可变参数模板(8)_TupleHelper

    1. TupleHelper的主要功能

    (1)打印:由于tuple中的元素是可变参数模板,外部并不知道内部到底是什么数据,有时调试时需要知道其具体值,希望能打印出tuple中所有的元素值。

    (2)根据元素值获取索引位置:tuple接口中有根据索引位置获取元素的接口,根据元素值来获取索引位置是相反的做法。

    (3)获取索引:在运行期根据索引获取索引位置的元素。

    (4)遍历tuple:类似于std::for_each算法,可以函数对象应用于tuple的每个元素

    (5)反转tuple:将tuple中的元素逆序。

    (6)应用于函数:将tuple中的元素进行一定的转换,使之成为函数的入参

    2. 打印tuple

    (1)通过模板特化递归来展开打印tuple

      ①tuple内部的元素个数和类型是不固定的,需要需要用std::get<N>(tuple)来获取元素。

      ②模板特化和递归调用展开tuple的本质就是通过整型模板参数递减来实现的。TuplePrint<decltype(tup), N>::print(tup);通用N来控制递归调用,直到N递减为1.

      ③PrintTuple2(tup)是辅助函数,可以减少外面调用的入参,方面调用TuplePrinter。

    (2)通过索引序列来展开并打印tuple

      ①根据tuple的参数Args…,创建一个可变的索引序列:make_index<Args…>::type

      ②根据索引序列来获取tuple中对应位置的元素,并转化为另一种可供print_impl函数使用的参数包:std::get<Indexes>(tup)…

      ③本质上是将tuple通过可变索引序列,转化为一个可变参数模板参数包

    【编程实验】tuple的打印

    //TpIndex.hpp

    #ifndef _TP_INDEXES_H_
    #define _TP_INDEXES_H_
    
    namespace TupleHelper
    {
    
    //******tuple参数的索引序列****************/
    
    //*************递增整数序列*******************
    //第1种方法
    template<int...>
    struct IndexTuple{};
    
    //前向声明
    template<int I, typename IndexTuple, typename... Types>
    struct make_indexes_impl;
    
    //定义递归函数(产生递增的整数序列)
    //各参数:I用于控制递归次数,Indexes:当前己产生的整数序列,T和Types...:将Types包分解T和Types...两部分
    template<int I, int... Indexes, typename T, typename... Types>
    struct make_indexes_impl<I, IndexTuple<Indexes...>, T, Types...>
    {
        //由于Types参数包被分解和T和Types...两部分,所以Types...的参数个数会逐渐减少
        //同时IndexTuple<Indexs...,I>会在当前Indexes整数序列的后面加上一个递增的I,即IndexTuple<Indexes..., I>
        //因此,是一个递增的整数序列
        using type = typename make_indexes_impl<I+1, IndexTuple<Indexes..., I>, Types...>::type;
    };
    
    //递归终止
    template<int I, int... Indexes>
    struct make_indexes_impl<I, IndexTuple<Indexes...>>
    {
        using type = IndexTuple<Indexes...>;
    };
    
    //类型萃取
    //调用方法如:make_indexes<double, char, int>
    template<typename... Types> 
    struct make_indexes : make_indexes_impl<0, IndexTuple<>, Types...>
    {};
    
    //第2种方法:
    template<int N, int... Indexes>
    struct make_indexes2 : make_indexes2<N-1, N-1, Indexes...>{};
    
    //termination condition
    template<int... Indexes>
    struct make_indexes2<0, Indexes...>
    {
        using type = IndexTuple<Indexes...>;
    };
    
    //第3种方法
    template<int end, int cur, int... Indexes>
    struct make_indexes3 : make_indexes3<end, cur + 1, Indexes..., cur>{};
    
    //cur == end, the list has been built
    template<int end, int... Indexes>
    struct make_indexes3<end, end, Indexes...>
    {
        using type = IndexTuple<Indexes...>;
    };
    
    //*************递减整数序列*******************
    //前向声明
    template<int I, typename IndexTuple, typename... Types>
    struct make_indexes_reverse_impl;
    
    //产生递减整数序列
    template<int I, int... Indexes, typename T, typename... Types>
    struct make_indexes_reverse_impl<I, IndexTuple<Indexes...>, T, Types...>
    {
        using type = typename make_indexes_reverse_impl<I-1, IndexTuple<Indexes..., I-1>, Types...>::type;
    };
    
    //递归终止
    template<int I, int... Indexes>
    struct make_indexes_reverse_impl<I, IndexTuple<Indexes...>>
    {
        using type = IndexTuple<Indexes...>;
    };
    
    //类型萃取
    //调用方法如:make_indexes<double, char, int>
    template<typename... Types> 
    struct make_reverse_indexes : make_indexes_reverse_impl<sizeof...(Types), IndexTuple<>, Types...>
    {};
        
    }
    
    #endif

    //TpPrint.hpp

    #ifndef _TP_PRINT_H_
    #define _TP_PRINT_H_
    
    #include <tuple>
    #include <iostream>
    #include "TpIndexes.hpp"
    
    namespace TupleHelper
    {
    //*************tuple的打印*******************
    
    //第1种方法:根据索引序列打印
    template<typename Last>
    void print_impl(Last&& last)
    {
        std::cout << " " << last << std::endl;
    }
    
    template<typename Head, typename... Tail>
    void print_impl(Head&& head, Tail&&... tail)
    {
        std::cout << " " << head;
        print_impl(tail...);
    }
    
    //万能转换,将tuple转成Args...
    template<typename... Args, int... Indexes>
    void tuple_print_impl(IndexTuple<Indexes...>, std::tuple<Args...>&& tup)
    {
        //取出tuple中的每个元素,并转成供print_imple使用的另一种参数包
        print_impl(std::forward<Args>(std::get<Indexes>(tup))...);
    }
    
    //辅助函数
    template<typename... Args>
    void PrintTuple(const std::tuple<Args...>& tup) //左值版本
    {
        tuple_print_impl(typename make_indexes<Args...>::type(),
                         std::tuple<Args...>(tup));
    }
    
    template<typename... Args>
    void PrintTuple(std::tuple<Args...>&& tup) //右值版本
    {
        tuple_print_impl(typename make_indexes<Args...>::type(), 
                         std::forward<std::tuple<Args...>>(tup));
    }
    
    //第2种方法:通过模板特化和递归来展开并打印tuple
    template<class Tuple, std::size_t N>
    struct TuplePrinter
    {
        static void print(const Tuple& t)
        {
            TuplePrinter<Tuple, N-1>::print(t); //深度递归
            std::cout << ", " << std::get<N-1>(t);
        }
    };
    
    template<class Tuple>
    struct TuplePrinter<Tuple, 1>
    {
        static void print(const Tuple& t)
        {
            std::cout << ", " << std::get<0>(t);
        }
    };
    //辅助函数
    template<typename... Args>
    void PrintTuple2(const std::tuple<Args...>& tup)
    {
        std::cout << "(";
        TuplePrinter<decltype(tup), sizeof...(Args)>::print(tup);
        std::cout <<")" << std::endl;
    }
    
    //第3种方法:
    template<typename T, int... Indexes>
    void print_impl3(const T& tup, IndexTuple<Indexes...>)
    {
        //利用初始化列表
        int a[] = {(std::cout << std::get<Indexes>(tup) << " ",0)...};
        (void)a;
        std::cout << std::endl;
    }
    
    template<typename... Args>
    void PrintTuple3(const std::tuple<Args...>& tup)
    {
        typedef typename make_indexes<Args...>::type index_type;
        print_impl3(tup, index_type());
    }
        
    }
    
    #endif

    //TestPrint.cpp

    #include <iostream>
    #include <tuple>
    #include "TpPrint.hpp"
    
    using namespace std;
    using namespace TupleHelper;
    
    int main()
    {
        //1. tuple的打印
        using Tuple = std::tuple<int,short, double, char, string>;
        Tuple tp = std::make_tuple(1, 2, 0.5f, 'a', "ok");
        PrintTuple(tp);
        PrintTuple2(tp);
        PrintTuple3(tp);
        
        return 0;
    }
    /*输出结果
    e:StudyC++1127>g++ -std=c++11 test_tuple_helper.cpp
    e:StudyC++1127>a.exe
     1 2 0.5 a ok
    (, 1, 2, 0.5, a, ok)
    1 2 0.5 a ok
    */

    3. 根据元素值获取索引位置

    (1)遍历tuple并判断当前元素值是否与给定的值相等,如果相等,则返回当前索引。否则直到遍历终止时仍没找到,则返回-1。

    (2)equal_val<N>(tuple, val)用于判断std::get<N>(tuple)是否等于val。

    (3)查找是从tuple的最后一个元素开始的,并返回第1个匹配元素的索引位置。

    (4)find_index用于递归查找。findIndex是个辅助函数,便于简化调用。

    4. 在运行期根据索引位置获取元素

    (1)std::get<N>(tuple):用于获取tuple中的第N个元素,但是N只能是个编译期的常量,不能是个变量(如int i)

    (2)可以采用映射的方法,将运行期变量“映射”为编译期常量。(见getArgByIndex函数)。通过自增编译期常量k,将k与运行期变量index比较,当两者相等时表示映射成功,这时调用std::get<k>(tuple)来获取第k个元素值。

    5. 遍历tuple

    (1)先将tuple展开为可变参数模板,然后用展开可变参数模板的方法遍历tuple类

    (2)遍历函数:tuple_for_each(func, tup);由于tuple中的元素类型是变化的因此func不能用lambda表达式,需要用一个泛型函数来处理tuple中的元素(见Functor仿函数)。

    6. 反转tuple

    (1)生成一个逆序的索引序列,目的是为了从最后一个tuple元素开始,将前面的元素一个一个取出来组成一个新的tuple。

    (2)tuple_reverse可接受左右值类型的tuple参数对象。

    7. 应用于函数

    (1)tuple应用于函数的目的:是将tuple展开作为某个函数的入参(可能有多个参数)。

    (2)实现思路:先将tuple展开转换为可变参数模板,然后这个可变参数模板应用于某个函数。

    (3)本例未实现当函数返回void的情况,这可重载apply_helper函数。

    8. 合并tuple

    (1)将两个tuple合起来,前一个tuple中的每一个元素为key后一个tuple中的每个元素为value,组成一个pair集合。

    (2)利用索引序列,将两个tuple分别展开,再将这些元素组合成一个pair集合。

    【编程实验】tupleHelper的综合应用

    //TpApply.hpp

    #ifndef _TP_APPLY_H_
    #define _TP_APPLY_H_
    
    #include <tuple>
    #include "TpIndexes.hpp"
    
    namespace TupleHelper
    {
    
    //1. 根据元素值获取索引位置
    //对于可转换的类型,则直接比较
    template<size_t N, typename Tuple, typename T>
    static typename std::enable_if<std::is_convertible<typename std::tuple_element<N, Tuple>::type, T>::value ||
        std::is_convertible<T, typename std::tuple_element<N, Tuple>::type>::value, bool>::type
        equal_val(const Tuple& tp, const T& val)
    {
        return std::get<N>(tp) == val;
    }
    
    //不能互转的类型,则直接返回false
    template<size_t N, typename Tuple, typename T>
    static typename std::enable_if<!(std::is_convertible<typename std::tuple_element<N, Tuple>::type, T>::value ||
        std::is_convertible<T, typename std::tuple_element<N, Tuple>::type>::value), bool>::type
        equal_val(const Tuple& tp, const T& val)
    {
        return false;
    }
    
    //根据值查找索引
    template<int I, typename T, typename... Args> //I用于控制递归调用的次数,I-1为元素索引位置
    struct find_index
    {
        static int comp(const std::tuple<Args...>& tup, T&& val)
        {
            using U = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
            using V = typename std::tuple_element<I - 1, std::tuple<Args...>>::type;
            bool bflag = std::is_convertible<U, V>::value || std::is_convertible<V, U>::value;
            return (bflag && equal_val<I - 1>(tup, val)) ? 
                             I - 1 : //从tuple最后一个元素开始查找,当前I-1为元素的索引
                             find_index<I - 1, T, Args...>::comp(tup, std::forward<T>(val));
        }
    };
    
    template<typename T, typename... Args>
    struct find_index<0, T, Args...>
    {
        static int comp(const std::tuple<Args...>& tup, T&& val)
        {
            using U = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
            using V = typename std::tuple_element<0, std::tuple<Args...>>::type;
            bool bflag = std::is_convertible<U, V>::value || std::is_convertible<V, U>::value;
            //递归终止,如果找到则返回0,否则返回-1
            return (bflag && equal_val<0>(tup, val)) ? 0 : -1;
        }
    };
    
    //辅助函数,简化调用
    template<typename T, typename... Args>
    int findIndex(const std::tuple<Args...>& tup, T&& val)
    {
        return find_index<sizeof...(Args), T, Args...>::comp(tup, std::forward<T>(val));
    }
    
    //2. 在运行期根据索引位置获取元素
    //第1种方法:将编译期常量和运行期变量进行映射
    template<size_t k, typename Tuple>
    typename std::enable_if<(k==std::tuple_size<Tuple>::value)>::type //void
    getArgByIndex(size_t index, const Tuple& tp)
    {
        throw std::invalid_argument("arg index out of range");
    }
    
    template<size_t k = 0, typename Tuple>
    typename std::enable_if<(k<std::tuple_size<Tuple>::value)>::type
    getArgByIndex(size_t index, const Tuple& tp)
    {
        if(k == index){
            std::cout << std::get<k>(tp)<< " ";
        }else{
            getArgByIndex<k + 1>(index, tp);//通过自增k,使得当k==index时输出
        }
    }
    
    //第2种方法:通过逐步展开参数包
    void getArgByIndex2(size_t index, std::tuple<>& tp){}
    
    template<typename Arg, typename... Args>
    void getArgByIndex2(size_t index, std::tuple<Arg, Args...>& tp) 
    {
        if(index < 0 || index >=std::tuple_size<std::tuple<Arg, Args...>>::value)
            throw std::invalid_argument("index is not valid");
        
        if(index > 0)
            getArgByIndex2(index-1, (std::tuple<Args...>&)tp); //tp的父类为std::tuple<Args...>
                                                               //注意:父子类对象内存模型
        else
            std::cout << std::get<0>(tp) << " ";
    }
    
    template<typename Arg>  //特化:当tuple只有一个参数时
    void getArgByIndex2(size_t index, std::tuple<Arg>& tp)
    {
        std::cout << std::get<0>(tp) << " ";
    }
    
    //3. 遍历tuple
    template<typename Func, typename Last>
    void for_each_impl(Func&& f, Last&& last)
    {
        f(std::forward<Last>(last));
    }
    
    template<typename Func, typename First, typename... Rest>
    void for_each_impl(Func&& f, First&& first, Rest&&... rest)
    {
        f(std::forward<First>(first));
        for_each_impl(std::forward<Func>(f), std::forward<Rest>(rest)...); //rest为tuple中的各个元素组成的参数包
    }
    
    template<typename Func, int... Indexes, typename... Args>
    void for_each_helper(Func&& f, IndexTuple<Indexes...>, std::tuple<Args...>&& tup)
    {
        for_each_impl(std::forward<Func>(f), 
                      std::forward<Args>(std::get<Indexes>(tup))...); //将tuple展开为可变参数模板的参数包!
    }
    
    template<typename Func, int... Indexes, typename... Args>
    void for_each_helper(Func&& f, IndexTuple<Indexes...>, std::tuple<Args...>& tup)
    {
        for_each_impl(std::forward<Func>(f), 
                      std::forward<Args>(std::get<Indexes>(tup))...); //将tuple展开为可变参数模板的参数包!
    }
    
    //tuple_for_each
    template<typename Func, typename... Args>
    void tuple_for_each(Func&& f, std::tuple<Args...>& tup)
    {
        for_each_helper(std::forward<Func>(f), 
                        typename make_indexes<Args...>::type(),
                        tup);
    }
    
    template<typename Func, typename... Args>
    void tuple_for_each(Func&& f, std::tuple<Args...>&& tup)
    {
        for_each_helper(std::forward<Func>(f), 
                        typename make_indexes<Args...>::type(),
                        std::forward<std::tuple<Args...>>(tup));
    }
    
    //4. 反转tuple
    template<class... Args, int... Indexes>
    auto reverse_impl(std::tuple<Args...>& tup, IndexTuple<Indexes...>&&) ->
    decltype(std::make_tuple(std::get<Indexes>(std::forward<std::tuple<Args...>>(tup))...))
    {
        return std::make_tuple(std::get<Indexes>(tup)...);
    }
    
    template<class... Args, int... Indexes>
    auto reverse_impl(std::tuple<Args...>&& tup, IndexTuple<Indexes...>&&) ->
    decltype(std::make_tuple(std::get<Indexes>(std::forward<std::tuple<Args...>>(tup))...))
    {
        return std::make_tuple(std::get<Indexes>(std::forward<std::tuple<Args...>>(tup))...);
    }
    
    template<class... Args>
    auto tuple_reverse(std::tuple<Args...>&& tup)->
    decltype(reverse_impl(std::forward<std::tuple<Args...>>(tup),typename make_reverse_indexes<Args...>::type()))
    {
        return reverse_impl(std::forward<std::tuple<Args...>>(tup),typename make_reverse_indexes<Args...>::type());
    }
    
    template<class... Args>
    auto tuple_reverse(std::tuple<Args...>& tup)->
    decltype(reverse_impl(std::forward<std::tuple<Args...>>(tup),typename make_reverse_indexes<Args...>::type()))
    {
        return reverse_impl(tup, typename make_reverse_indexes<Args...>::type());
    }
    
    //5. tuple应用于函数(将tuple中的元素展开成一个个参数,并传给f函数)。
    template<typename F, typename Tuple, int... Indexes>
    auto apply_helper(F&& f, IndexTuple<Indexes...>&& in, Tuple&& tup)->
    decltype(std::forward<F>(f)(std::get<Indexes>(tup)...))
    {
        return std::forward<F>(f)(std::get<Indexes>(tup)...);
    }
    
    template<class F, class... Args>
    typename std::result_of<F(Args...)>::type
    apply(F&& f, const std::tuple<Args...>& tup)
    {
        return apply_helper(std::forward<F>(f), typename make_indexes<Args...>::type(), tup);
    }
    
    //6. 合并tuple
    template<std::size_t N, typename T1, typename T2>
    using pair_type = std::pair<typename std::tuple_element<N, T1>::type, typename std::tuple_element<N, T2>::type>;
    
    template<std::size_t N, typename T1, typename T2>
    pair_type<N, T1, T2> pairs(const T1& tup1, const T2& tup2)
    {
        return std::make_pair(std::get<N>(tup1), std::get<N>(tup2)); //tup1为key, tup2为value
    }
    
    template<int... Indexes, typename T1, typename T2>
    auto pairs_helper(IndexTuple<Indexes...>, const T1& tup1, const T2& tup2)->
    decltype(std::make_tuple(pairs<Indexes>(tup1, tup2)...))
    {
        return std::make_tuple(pairs<Indexes>(tup1, tup2)...);
    }
    
    template<typename Tuple1, typename Tuple2>
    auto Zip(Tuple1 tup1, Tuple2 tup2)->
    decltype(pairs_helper(typename make_indexes2<std::tuple_size<Tuple1>::value>::type(), tup1, tup2))
    {
        static_assert(std::tuple_size<Tuple1>::value == std::tuple_size<Tuple2>::value, "tuples should be the same size.");
        return pairs_helper(typename make_indexes2<std::tuple_size<Tuple1>::value>::type(), tup1, tup2);
    }
    
    }
    #endif

    //test_tuple_helper.cpp

    #include <iostream>
    #include <tuple>
    #include "TpPrint.hpp"
    #include "TpApply.hpp"
    
    using namespace std;
    using namespace TupleHelper;
    
    struct Functor
    {
        template<typename T>
        void operator()(T&& t) const
        {
            cout << t << " ";
        }
    };
    
    struct PairFunctor
    {
        template<typename T>
        void operator()(T&& t) const
        {
            cout << "key = " << t.first << " , value = " << t.second << endl;
        }
    };
    
    int main()
    {    
        using Tuple = std::tuple<int,short, double, char, string>;
        Tuple tp = std::make_tuple(1, 2, 0.5f, 'a', "ok");
        
        //1. 根据元素值获取索引位置
        cout << "findIndex(tp, "ok")..."<< endl;
        int index = findIndex(tp, "ok");
        cout << index << endl;
        
        //2. 在运行期根据索引位置获取元素
        cout << "getArgByIndex(i, tp)..."<< endl;
        int len = std::tuple_size<Tuple>::value;
        for(int i=0; i<len; i++)
        {
            getArgByIndex(i, tp); //getArgByIndex<0>(i, tp);
        }
        cout << endl;
        
        cout << "getArgByIndex2(i, tp)..."<< endl;
        //getArgByIndex(5, tp); //error, 索引超出范围,将抛出异常
        for(int i=0; i<len; i++)
        {
            getArgByIndex2(i, tp); //getArgByIndex<0>(i, tp);
        }
        cout << endl ;
        
        cout << "Traverse tuple..."<< endl;
        //3. 遍历tuple
        tuple_for_each(Functor(), std::make_tuple(1,3,4,'b',2.0));
        cout << endl;
        
        cout << "tuple_reverse(tp)..."<< endl;
        //4. 反转tuple
        auto tp2 = tuple_reverse(tp);
        PrintTuple(tp2);
        PrintTuple(tp);
        
        //5. 将tuple应用于函数
        cout << "apply..."<< endl;
        apply([](int a, int b){cout << a + b << endl; return a + b;}, std::make_tuple(1, 2));
        
        //6. 合并tuple
        cout << "merge..."<< endl;
        auto tp3 = std::make_tuple<int, short, double, char>(1, 2, 2.5, 'a');
        auto tp4 = std::make_tuple<double, short, double, char>(1.5, 2, 2.5, 'z');
        auto mypairs = Zip(tp3, tp4);
        tuple_for_each(PairFunctor(), mypairs); 
        
        return 0;
    }
    /*输出结果
    e:StudyC++1127>g++ -std=c++11 test_tuple_helper.cpp
    e:StudyC++1127>a.exe
    findIndex(tp, "ok")...
    4
    getArgByIndex(i, tp)...
    1 2 0.5 a ok
    getArgByIndex2(i, tp)...
    1 2 0.5 a ok
    Traverse tuple...
    1 3 4 b 2
    tuple_reverse(tp)...
     ok a 0.5 2 1
     1 2 0.5 a ok
    apply...
    3
    merge...
    key = 1 , value = 1.5
    key = 2 , value = 2
    key = 2.5 , value = 2.5
    key = a , value = z
    */
  • 相关阅读:
    LeetCode Notes_#20 Valid Parentheses
    LeetCode Notes_#14 Longest Common Prefix
    牛客21天刷题_day#3
    牛客21天刷题_day#2
    牛客21天刷题_day#1
    WebGL编程指南
    《Redis 设计与实现》
    《女士品茶》
    《Java应用架构设计:模块化模式与OSGi》
    《编译与反编译技术实战》
  • 原文地址:https://www.cnblogs.com/5iedu/p/7858378.html
Copyright © 2011-2022 走看看