c++ - 由于未定义的引用,无法调用可变参数函数
问题描述
!!!下面的解决方案!
我正在尝试扩展 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);
解决方案
推荐阅读
- mule - DataWeave 2 中的空检查
- android - 适用于 iOS 和 Android 应用程序的服务器端数据库
- javascript - HERE Maps - 动态加载标记和缩放问题
- windows - Windows 使用哪种编码方案?
- queue - Nats.io:具有不同主题但具有相同命名队列的订阅者会并行处理消息吗?
- mysql - 我不能在触发器更新 mysql 中删除数据联合吗?
- php - 如何从 PHP (Yii2) 异常中调试 volley error 500?
- r - 需要对 R 代码中的半决赛和决赛比赛给予额外的重视
- python-2.7 - Apache Beam python 管道在 groupbykey() 之后没有继续
- visual-studio-2017 - 导航代码图时无法跳转到源代码