c++ 带类型检查的可变参数模板

inn6fuwd  于 2022-12-15  发布在  其他
关注(0)|答案(2)|浏览(167)

我有一些带参数的opencl内核 Package 器对象,目前我是这样运行它们的:

kernel.setArg<0>(count);
kernel.setArg<1>(data);

但如果我能这样运行它会干净得多:

kernel.setArgs(count, data);

我希望这个类型是安全的,因为参数只能是intbuffer&
我的第一个想法是可变模板,我让它工作,但没有类型安全:

#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;
}

在编译时,应该支持的类型只有intbuffer&,但我无法让它像我想要的那样工作。

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吗?我可以以某种方式禁止这样的强制类型转换吗?

wpx232ag

wpx232ag1#

您几乎已经完成了。如果您只想接受***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,尤其是参数。

j2cgzkjk

j2cgzkjk2#

您还可以添加一个无法使用static_assert编译的通用 setKernelArg 函数:

template<int argNum, typename T>
void setKernelArg(T& data) {
            static_assert(sizeof(T) == 0, 
                          "Argument type not supported"); 
}

使用这种方法,你可以在以后添加其他参数类型,而不会得到一个过长的 std::is_same 语句。这也适用于c++11。
顺便说一句:在第7行的示例代码中,您可能应该传递 arg 而不是 numArg

相关问题