版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
};