首页 > 解决方案 > 由于未定义的引用,无法调用可变参数函数

问题描述

!!!下面的解决方案!


我正在尝试扩展 OpenCL 包装器CL/cl2.hpp,这样我就可以通过一个调用来设置任意数量的Kernel参数,如下所示:

setArgs(kernel, arg1, arg2, arg3,...);

为此,我实现了这些可变参数模板setArgs

// recursion end
void setArgs(const cl::Kernel &kernel, uint i) {}

// actual recursive function
template<typename ArgumentType, typename... ArgumentTypes>
void setArgs(const cl::Kernel &kernel, uint i, ArgumentType arg, ArgumentTypes... args) {
    kernel.setArg(i, arg);
    setArgs(kernel, i++, args...);
}

// split the argument list into head and tail
template<typename ArgumentType, typename... ArgumentTypes>
void setArgs(const cl::Kernel &kernel, ArgumentType arg, ArgumentTypes... args) {
    setArgs(kernel, 0, arg, args...);
}

// I think this should be the first called function
template<typename... ArgumentTypes>
void setArgs(const cl::Kernel &kernel, ArgumentTypes... args) {
    setArgs(kernel, 0, args...);
}

像这样调用它会setArgs(kernel, bufferIn, bufferOut, (cl_double)3.14)导致此错误:

undefined reference to void setArgs<cl::Buffer, cl::Buffer, double>(cl::Kernel const&, cl::Buffer, cl::Buffer, double)

非常抱歉,但我对 C++11 可变参数函数还是很陌生,我不明白为什么它不起作用。我试着摆弄,但没有任何效果。上面的代码是我能想到的最合乎逻辑的。

PS:我知道这只是语法糖,但我会大大减少代码中的冗余,并且理解可变参数模板可能很好;)


!!!解决方案!!!

1. 将模板移至.hpp

模板只能被实现——即在头文件中声明和定义。所以我需要将模板移动到.hpp文件中,并将递归结束函数定义拆分到.cpp文件中。

感谢@NathanOliver 的解释。

2.修复递归歧义

考虑以下方法声明:

// A
void setArgs(const cl::Kernel &kernel, ArgumentType arg, ArgumentTypes... args)
// B
void setArgs(const cl::Kernel &kernel, uint i, ArgumentType arg, ArgumentTypes... args)

调用setArgs(kernel, 42, argA, argB)可以同时匹配 A 和 B。因此递归扩展到不断增加的大小。这导致以下错误消息:

void setArgs(cl::Kernel&, ArgumentType, ArgumentTypes ...) [with ArgumentType = int; ArgumentTypes = {int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int...
fatal error: template instantiation depth exceeds maximum of 900 (use ‘-ftemplate-depth=’ to increase the maximum)

使用i作为第一个参数解决了这种歧义。现在我得到了以下代码:

kernel.hpp(摘录):

void setArgs(uint i, cl::Kernel &kernel);

template<typename ArgumentType, typename... ArgumentTypes>
void setArgs(uint i, cl::Kernel &kernel, ArgumentType arg, ArgumentTypes... args) {
    kernel.setArg(i, arg);
    setArgs(i + 1, kernel, args...);
}

template<typename ArgumentType, typename... ArgumentTypes>
void setArgs(cl::Kernel &kernel, ArgumentType arg, ArgumentTypes... args) {
    setArgs(0, kernel, arg, args...);
}

template<typename ArgumentType, typename... ArgumentTypes>
void setArgs(cl::Kernel &kernel, ArgumentType arg, ArgumentTypes... args) {
    setArgs(0, kernel, arg, args...);
}

kernel.cpp(摘录):

#include "kernel.hpp"

void setArgs(uint i, cl::Kernel &kernel) {}

更优雅的解决方案

kernel.hpp(摘录):

class Kernel : public cl::Kernel {
public:
    using cl::Kernel::Kernel;

    template<typename... ArgumentTypes>
    Kernel setArgs(ArgumentTypes ... args) {
        return _setArgs(args...);
    }

private :
    uint i = 0;

    template<typename ArgumentType, typename... ArgumentTypes>
    Kernel _setArgs(ArgumentType arg, const ArgumentTypes ... args) {
        setArg(i, arg);
        i++;
        return _setArgs(args...);
    }

    Kernel _setArgs();
};

kernel.cpp(摘录):

#include "kernel.hpp"

Kernel Kernel::_setArgs() { return *this; }

调用函数

Kernel kernel = Kernel(device, kernelName)
    .setArgs(argIn, argOut)
    .setArgs(moreParams);

标签: c++openclvariadic-templates

解决方案


推荐阅读