问题引入
现在想实现这样一个模板类,将不定参通过commit函数提交到工作队列,然后由一个线程从队列中取出数据,调用process方法对输入进行处理。如下面的类所定义。
template<class ...Args>
class AsyncProcess {
public:
virtual void commit(Args...args) {
jobQueue.emplace(make_tuple(args));
};
protected:
struct Job {
tuple<Args...> args;
};
queue<Job> jobQueue;
virtual void process(Args...args) = 0;
};
但是遇到一个问题,我们用tuple保存不定参数在队列中进行传输,但是我们没用一个通用的办法直接将tuple里面的值一一传递到process的不定参数列表中。
正常的,我们要将数组的值赋值到不定参数列表中,可以使用std::get方法,如下所示:
template<class ...Args>
void process(Args...) {
}
void callProcess(tuple<int, int, float> tp) {
process(get<0>(tp), get<1>(tp), get<2>(tp));
}
问题提出
那么问题来了,对于不定参数,我们无法知道我们需要调用多少次std::get才能将tuple内的值全部取出,赋值到不定参数列表中。
引入templete< size_t…Is >模板
为了解决上面问题,我们需要知道展开不定参数模板一个特性,下面这段代码中,我们调用了callProcess<0, 1, 2>,则get<Is>(tp)...
实际会被展开为get<0>(tp), get<1>(tp), get<2>(tp)
,这似乎里我们解决问题又进了一大步,但是我们要如何获取<0,1,2,3…>,也就是不定参数模板的索引呢?
template<size_t ...Is>
void callProcess(tuple<int, int, float> tp) {
process(get<Is>(tp)...);
}
int main() {
callProcess<0, 1, 2>(make_tuple(1, 1, 2.f));
}
获取不定参数模板的索引模板
下面的代码为我们提供了获取不定参数索引模板的方法,我们调用callProcess的时候就能将tuple自动展开到process方法的参数列表中。
template<class ...Args>
class Tmp {
template<size_t...>
struct tuple_idx {
};
template<size_t N, size_t... Is>
struct tuple_bind : tuple_bind<N - 1, N - 1, Is...> {
};
template<size_t... Is>
struct tuple_bind<0, Is...> {
typedef tuple_idx<Is...> type;
};
public:
virtual void process(Args... args) = 0;
template<size_t...Is>
void extendTupleToProcess(tuple<Args...> t, tuple_idx<Is...> &&) {
process(get<Is>(t)...);
}
void callProcess(tuple<Args...> t) {
extendTupleToProcess(t, typename tuple_bind<sizeof ...(Args)>::type());
}
};
其原理如下:
我们定义tuple_bind<3>
的时候,实际创建了这样一个模板
tuple_bind<3>:tuple_bind<2,2>:tuple_bind<1,1,2>:tuple_bind<0,0,1,2>
,这时候,tuple_bind<0, Is...>
的Is就会被解释为<0,1,2>
,也就是我们想要的。
那么如何取出Is呢?我们在tuple_bind<0, Is...>
内部定义了typedef tuple_idx<Is...> type;
,因此我们只要向extendTupleToProcess
方法中传入一个tuple_idx<Is...>
就能将template<size_t...Is> void extendTupleToProcess
的Is定义为我们想要的结果,从而用于展开tuple。