本文共 3347 字,大约阅读时间需要 11 分钟。
我在阅读seastar的源码时(这并不代表我熟悉seastar),偶然发现了seastar把tuple里的每一个element传给一个函数作为参数,这让我很好奇它是如何实现的,让我不自量力地分析一下。
seastar有个头文件apply.hh包含如下代码:
namespace seastar { templatestruct apply_helper; template struct apply_helper > { static auto apply(Func&& func, Tuple args) { return func(std::get (std::forward (args))...); } }; template inline auto apply(Func&& func, std::tuple && args) { using helper = apply_helper &&, std::index_sequence_for >; return helper::apply(std::forward (func), std::move(args)); } template inline auto apply(Func&& func, std::tuple & args) { using helper = apply_helper &, std::index_sequence_for >; return helper::apply(std::forward (func), args); } template inline auto apply(Func&& func, const std::tuple & args) { using helper = apply_helper &, std::index_sequence_for >; return helper::apply(std::forward (func), args); }
以上这段代码实现的功能是把通过tuple打包的各个element数据拆开,并作为一个个参数去调用其他函数,下面展示如何使用它:
void print(int i, double f){ std::cout << "i:" << i << " f:" << f << '\n';}int main(){ std::tuplevalues(1, 2.3); //对数据1和2.3打包 apply(print, values); //调用 print, 把1和2.3作为调用参数}
以上就是把值1和2.3打包,然后通过apply把这些打包的参数通过apply来调用函数print。
上面seastar中apply_helper的主模板没有定义,只是写了一个声明:
templatestruct apply_helper;
然后就是准对第三个模板参数做了部分特化,这第三个参数用的到就是index_sequence模板实例类型:
templatestruct apply_helper > { static auto apply(Func&& func, Tuple args) { return func(std::get (std::forward (args))...); } };
这个apply静态函数的秘密就是利用index_sequence模板实例化时携带的非类型可变参数列表"I...",利用编译器自动对可变参数列表的展开,把这些参数列表里的一个个参数去调用std::get,而这个get则是获取tuple里由Index指定的element。Index是一个整形值。注意调用的方式,get后面跟随一个...操作符:
std::get(std::forward(args))...
它把index_sequence实例化的参数列表I的每一个参数去分别调用get,由编译器自动重复这个过程。
例如,如果你有index_sequence<1,2,3>这个模板实例定义的类型,那么
func(std::get(std::forward(args))...)
的意思就是:
func(std::get<1>(std::forward(args)), std::get<2>(std::forward(args)), std::get<3>(std::forward(args)))
STL中的index_sequence_for模板的定义
templateusing index_sequence_for = make_index_sequence ;
具体细节我不多讲了,有兴趣可以去看libstdc++头文件的实现,位于utility头文件。
它的作用就是为给定的一个参数列表,实例化模板index_sequence得到一个新的类型,该类型的模板参数是一系列索引号列表:例如 index_sequence_for生成一个实例化类型:
index_sequence<0,1,2>。这样0,1,2这个参数列表就可以用在apply一系列的模板中。由于在apply代码中,模板演绎时tuple始终不丢弃,所以实际上我们在展开这些tuple element作为函数调用的参数时,只需要一系列序号让编译器去重复调用get模板,这就是index_sequence的作用所在。
转载地址:http://ogxsa.baihongyu.com/