可变模板参数 和 元组

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/felixking/article/details/52413376

事情的起因是要解决这么个问题, Opengl渲染时需要指定的VBO有多种, 主要是因为顶点格式可自定义导致的, 类似D3D的FVF, 顶点的格式可以是 坐标和法线, 也可以是坐标,法线和纹理坐标等等, 我需要实现一个通用的对Renderable渲染的方法, 当然也需要很好的去设计Renderable的数据结构 ,
为了求效率, 不想用太多的多态, 多态意味着函数不能内联, 也不想用太多的if else, 这会导致CPU流水线重置, 可以尝试使用模板.

尝试1: 使用可变模板参数

struct A{   void f()    {       printf("A::f");     }};
struct B{   void f()    {       printf("B::f");     }};
struct C{   void f()    {       printf("C::f");     }};

template <class T>
T & unpacker(T & t) {   t.f();  return t; }

template <typename ... T>
void DummyWrapper(T... t){};

template<typename ... ARGS>
void foo(ARGS ... args)
{
    DummyWrapper(unpacker(args)...);
}

int main()
{
    foo(A(), B(), C());
    return 0;
}

生成的汇编代码为:

    foo(A(), B(), C());
00B61040 68 08 21 B6 00       push        offset string "C::f" (0B62108h)  
00B61045 E8 C6 FF FF FF       call        printf (0B61010h)  
00B6104A 68 00 21 B6 00       push        offset string "B::f" (0B62100h)  
00B6104F E8 BC FF FF FF       call        printf (0B61010h)  
00B61054 68 F8 20 B6 00       push        offset string "A::f" (0B620F8h)  
00B61059 E8 B2 FF FF FF       call        printf (0B61010h)  

效果非常好, 但 这只是函数的调用, 并不能作为成员存储在Renderable中, 我需要一种方案能在 调用代码时 给定 n 个类型, 并存储起来, 在后续的流程中 去call, 这方案做不到存储的目的

尝试2: 使用 tuple

#include <tuple>
struct A{   void f()    {       printf("A::f"); }};
struct B{   void f()    {       printf("B::f");     }};
struct C{   void f()    {       printf("C::f");     }};

int main()
{
    auto a = std::make_tuple(A(), B(), C());
    std::get<0>(a).f();
    std::get<1>(a).f();
    std::get<2>(a).f();
    return 0;
}

生成的汇编代码为

auto a = std::make_tuple(A(), B(), C());
    std::get<0>(a).f();
0026105E 68 F8 20 26 00       push        offset string "A::f" (2620F8h)  
00261063 E8 A8 FF FF FF       call        printf (261010h)  
    std::get<1>(a).f();
00261068 68 00 21 26 00       push        offset string "B::f" (262100h)  
0026106D E8 9E FF FF FF       call        printf (261010h)  
    std::get<2>(a).f();
00261072 68 08 21 26 00       push        offset string "C::f" (262108h)  
00261077 E8 94 FF FF FF       call        printf (261010h)  

效果一样, 非常好
成创建变量 a 就说明存储没有问题

补充:
问题没有完全解决, 因为不能用简单的for循环去遍历tuple, 需要使用模板递归

template<typename ... ARGS>
struct Renderable
{
    Renderable(ARGS ... args)
    {
        d = std::make_tuple(args...);
    }

    template<size_t N>
    class TT {
    public:
        template<typename ... ARGS>
        static void c(std::tuple<ARGS...> & t)
        {
            std::get<sizeof...(ARGS)-N>(t).f();
            if (N > 0) {
                TT<N - 1>::c<ARGS...>(t);
            }
        }
    };

    template<>
    class TT <1>
    {
    public:
        template<typename ... ARGS>
        static void c(std::tuple<ARGS...> & t)
        {
            std::get<sizeof...(ARGS) - 1>(t).f();
        }
    };

    void call()
    {
        TT<sizeof...(ARGS)>::c<ARGS...>(d);
    }

    std::tuple<ARGS...> d;
};

调用方法如下:

    auto a = Renderable<A, B, C>(A(), B(), C());
    a.call();

最终生成的汇编代码与之前一致

    a.call();
01381040 68 F8 20 38 01       push        offset string "A::f\r\n" (13820F8h)  
01381045 E8 C6 FF FF FF       call        printf (1381010h)  
0138104A 68 00 21 38 01       push        offset string "B::f\r\n" (1382100h)  
0138104F E8 BC FF FF FF       call        printf (1381010h)  
01381054 68 08 21 38 01       push        offset string "C::f\r\n" (1382108h)  
01381059 E8 B2 FF FF FF       call        printf (1381010h)  
0138105E 83 C4 0C             add         esp,0Ch  

补充 1: 由 tuple 元素类型 获得 该类型 在 tuple 中的索引, 注: 若 该类型在 tuple 中有多处, 将返回第一个的索引

template <class T, class Tuple>
struct Index;
template <class T, class... Types>
struct Index<T, std::tuple<T, Types...>> {
    static const std::size_t value = 0;
};
template <class T, class U, class... Types>
struct Index<T, std::tuple<U, Types...>> {
    static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value;
};

猜你喜欢

转载自blog.csdn.net/felixking/article/details/52413376
今日推荐