我有一些带有参数的opencl内核包装器对象,目前我按照这样运行它们:
kernel.setArg<0>(count);
kernel.setArg<1>(data);
但如果我能像这样运行它会更清洁:
kernel.setArgs(count, data);
我希望此类型安全,因为参数只能是int
或buffer&
。
我的第一个想法是可变参数模板,我让它工作,但没有类型安全:
#include <iostream>
struct buffer {};
class kernel {
template<int arg, int numArg, typename T>
void setArgs_(T& data) {
setKernelArg<numArg>(data);
}
template<int arg, int numArg, typename T, typename... Args>
void setArgs_(T& data, Args&&... params) {
setKernelArg<arg>(data);
setArgs_<arg+1, numArg, Args...>(params...);
}
template<int argNum>
void setKernelArg(buffer& data) {
std::cout <<"setting arg " << argNum << " to buffer" << std::endl;
//set arg argNum to data, long function
}
template<int argNum>
void setKernelArg(int data) {
std::cout <<"setting arg " << argNum << " to int" << std::endl;
//set arg argNum to data, long function
}
public:
template<typename T, typename... Args>
void setArgs(T& data, Args&&... params) {
setArgs_<0, sizeof...(params), T, Args...>(data, params...);
}
};
int main(int argc, char** argv) {
buffer a,b,c;
int i,j,k;
kernel kern;
kern.setArgs(a,i,b,j,c,k);
return 0;
}
在编译时,应该支持的唯一类型是int
和buffer&
,但我无法按照自己的意愿运行它。我试过std::enable_if
:
template<typename T, typename... Args>
typename std::enable_if<std::is_same<T, buffer&>::value> setArgs(T& data, Args&&... params) {
setArgs_<0, sizeof...(params), buffer&, Args...>(data, params...);
}
template<typename T, typename... Args>
typename std::enable_if<std::is_same<T, int>::value> setArgs(T& data, Args&&... params) {
setArgs_<0, sizeof...(params), int, Args...>(data, params...);
}
然后我得到了
varargs_typesafe.cpp:41:29: error: call of overloaded ‘setArgs(buffer&, int&, buffer&, int&, buffer&, int&)’ is ambiguous
kern.setArgs(a,i,b,j,c,k);
^
varargs_typesafe.cpp:28:62: note: candidate: std::enable_if<std::is_same<T, buffer&>::value> kernel::setArgs(T&, Args&& ...) [with T = buffer; Args = {int&, buffer&, int&, buffer&, int&}]
typename std::enable_if<std::is_same<T, buffer&>::value> setArgs(T& data, Args&&... params) {
^
varargs_typesafe.cpp:32:58: note: candidate: std::enable_if<std::is_same<T, int>::value> kernel::setArgs(T&, Args&& ...) [with T = buffer; Args = {int&, buffer&, int&, buffer&, int&}]
typename std::enable_if<std::is_same<T, int>::value> setArgs(T& data, Args&&... params) {
感觉像我的std::enable_if
没有正常工作或我错过了什么。
编辑:
我通过给我的工作代码给它一个bool
作为参数来尝试这个并且它有效,但是std::string
失败了。这是因为bool
可以转换为int
吗?我可以以某种方式禁止这样的演员吗?
答案 0 :(得分:2)
你快到了。如果您想接受 <{strong> buffer&
和int
,则可以使用此功能:
template<typename T, typename... Args>
std::enable_if_t<std::is_same<T, buffer>::value>
setArgs(T &data, Args&&... params) {
setArgs_<0, sizeof...(params), T, Args...>(data, std::forward<Args>(params)...);
}
template<typename T, typename... Args>
std::enable_if_t<std::is_same<T, int>::value>
setArgs(T data, Args&&... params) {
setArgs_<0, sizeof...(params), T, Args...>(data, std::forward<Args>(params)...);
}
或者,如果您计划仅接受两种类型的左值引用:
template<typename T, typename... Args>
std::enable_if_t<std::is_same<T, buffer>::value or std::is_same<T, int>::value>
setArgs(T& data, Args&&... params) {
setArgs_<0, sizeof...(params), T, Args...>(data, std::forward<Args>(params)...);
}
如果您想接受这两种类型的任何类型的引用,甚至可以这样:
template<typename T, typename... Args>
std::enable_if_t<std::is_same<std::decay_t<T>, buffer>::value or std::is_same<std::decay_t<T>, int>::value>
setArgs(T&& data, Args&&... params) {
setArgs_<0, sizeof...(params), T, Args...>(std::forward<T>(data), std::forward<Args>(params)...);
}
在这种情况下,我会在你的课程的其他部分使用转发引用和std::forward
,尤其是对于参数。
答案 1 :(得分:0)
您还可以添加无法使用static_assert编译的通用 setKernelArg 函数:
template<int argNum, typename T>
void setKernelArg(T& data) {
static_assert(sizeof(T) == 0,
"Arguement type not supported");
}
使用这种方法,您可以稍后添加其他参数类型,而不会获得过长的 std :: is_same 语句。这也适用于c ++ 11。
BTW:在第7行的示例代码中,您应该传递 arg 而不是 numArg 。