!!! SOLUTION BELOW !!!
I'm trying to extend the OpenCL wrapper CL/cl2.hpp
so I can set any amount of Kernel
arguments with just one call like this:
setArgs(kernel, arg1, arg2, arg3,...);
To do that I've implemented these variadic templates for 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...);
}
Calling it like setArgs(kernel, bufferIn, bufferOut, (cl_double)3.14)
caused this error:
undefined reference to void setArgs<cl::Buffer, cl::Buffer, double>(cl::Kernel const&, cl::Buffer, cl::Buffer, double)
I'm very sorry but I'm pretty new to C++11 variadic functions and I don't get why it doesn't work. I tried fiddling around but nothing worked. The code above is the most logical I can come up with.
PS: I know it's just syntax sugar but I it would reduce redundancy in my code a lot and it's propably good to understand variadic templates ;)
!!! Solution!!!
1. Move templates to .hpp
Templates can only be implemented - that is declared AND defined in header files. So I needed to move the templates to the .hpp
file and split the recursion end function definition to the .cpp
file.
Thanks @NathanOliver for explaining.
2. Fix recursion abiguity
Consider the following method declarations:
// A
void setArgs(const cl::Kernel &kernel, ArgumentType arg, ArgumentTypes... args)
// B
void setArgs(const cl::Kernel &kernel, uint i, ArgumentType arg, ArgumentTypes... args)
Calling setArgs(kernel, 42, argA, argB)
can match to both A and B. Thus the recursion expands to ever increasing size. This led to the following error message:
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)
Using i
as the first parameter solved this ambiguity. Now I got the following code:
kernel.hpp (excerpt):
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 (excerpt):
#include "kernel.hpp"
void setArgs(uint i, cl::Kernel &kernel) {}
More elegant solution as class
kernel.hpp (excerpt):
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 (excerpt):
#include "kernel.hpp"
Kernel Kernel::_setArgs() { return *this; }
Calling the functions:
Kernel kernel = Kernel(device, kernelName)
.setArgs(argIn, argOut)
.setArgs(moreParams);