My CUDA version is 9.1, GCC version is 6.4.1, it’s on Fedora 25 system.
The following code is a small example that I came up with to demonstrate the problem:
#include <iostream>
#include <tuple>
template<typename... Args> using MyArgs = std::tuple<Args*...>;
template<typename... Args>
MyArgs<Args...> MakeMyArgs(Args&... args)
{
return MyArgs<Args...>(&args...);
}
template<typename T, class Derived>
class KernelBase
{
public:
template<typename... Args>
void operator()(Args&&... args)
{
SetArgs<0>(args...);
Derived* d = reinterpret_cast<Derived*>(this);
d->dump();
}
protected:
template<int I, typename Q, typename... Args>
void SetArgs(Q&& arg1, Args&& ... args)
{
Derived* ptr = reinterpret_cast<Derived*>(this);
auto myargs = ptr->DefArgs();
*std::get<I>(myargs) = arg1;
SetArgs<I+1>(args...);
}
template<int I>
void SetArgs() {}
};
template<typename T>
class KernelDerived : public KernelBase<T, KernelDerived<T>>
{
public:
float f1;
float f2;
float f3;
MyArgs<T, T, T> DefArgs()
{
return MakeMyArgs(f1, f2, f3);
}
void dump(void)
{
std::cout << f1 << std::endl;
std::cout << f2 << std::endl;
std::cout << f3 << std::endl;
}
};
template<typename T>
void KernelDerivedTest(T f1, T f2, T f3)
{
KernelDerived<T>()(f1, f2, f3);
}
template
void KernelDerivedTest<float>(float f1, float f2, float f3);
int main(void)
{
float f1 = 2.0;
float f2 = 2.1;
float f3 = 2.2;
KernelDerivedTest<float>(f1, f2, f3);
}
If the code is saved into file foo.cu, and if compilation attempted with nvcc:
nvcc -std=c++11 -c foo.cu
it will dump out lengthy error message basically reported deeply into the header:
/usr/include/c++/6.4.1/tuple: In instantiation of ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_MoveConstructibleTuple() [with _UElements = {std::tuple<float*, float*, float*>}; bool <anonymous> = true; _Elements = {float*, float*, float*}]’:
/usr/include/c++/6.4.1/tuple:626:248: required by substitution of ‘template<class ... _UElements, typename std::enable_if<(((std::_TC<(sizeof... (_UElements) == 1), float*, float*, float*>::_NotSameTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_MoveConstructibleTuple<_UElements ...>()) && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && (3ul >= 1)), bool>::type <anonymous> > constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {std::tuple<float*, float*, float*>}; typename std::enable_if<(((std::_TC<(sizeof... (_UElements) == 1), float*, float*, float*>::_NotSameTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_MoveConstructibleTuple<_UElements ...>()) && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && (3ul >= 1)), bool>::type <anonymous> = <missing>]’
foo.cu:30:30: required from ‘void KernelBase<T, Derived>::SetArgs(Q&&, Args&& ...) [with int I = 0; Q = float&; Args = {float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:19:13: required from ‘void KernelBase<T, Derived>::operator()(Args&& ...) [with Args = {float&, float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:62:18: required from ‘void KernelDerivedTest(T, T, T) [with T = float]’
foo.cu:65:67: required from here
/usr/include/c++/6.4.1/tuple:483:67: error: mismatched argument pack lengths while expanding ‘std::is_constructible<_Elements, _UElements&&>’
return __and_<is_constructible<_Elements, _UElements&&>...>::value;
^~~~~
/usr/include/c++/6.4.1/tuple:484:1: error: body of constexpr function ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_MoveConstructibleTuple() [with _UElements = {std::tuple<float*, float*, float*>}; bool <anonymous> = true; _Elements = {float*, float*, float*}]’ not a return-statement
}
^
/usr/include/c++/6.4.1/tuple: In instantiation of ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_ImplicitlyMoveConvertibleTuple() [with _UElements = {std::tuple<float*, float*, float*>}; bool <anonymous> = true; _Elements = {float*, float*, float*}]’:
/usr/include/c++/6.4.1/tuple:626:362: required by substitution of ‘template<class ... _UElements, typename std::enable_if<(((std::_TC<(sizeof... (_UElements) == 1), float*, float*, float*>::_NotSameTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_MoveConstructibleTuple<_UElements ...>()) && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && (3ul >= 1)), bool>::type <anonymous> > constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {std::tuple<float*, float*, float*>}; typename std::enable_if<(((std::_TC<(sizeof... (_UElements) == 1), float*, float*, float*>::_NotSameTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_MoveConstructibleTuple<_UElements ...>()) && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && (3ul >= 1)), bool>::type <anonymous> = <missing>]’
foo.cu:30:30: required from ‘void KernelBase<T, Derived>::SetArgs(Q&&, Args&& ...) [with int I = 0; Q = float&; Args = {float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:19:13: required from ‘void KernelBase<T, Derived>::operator()(Args&& ...) [with Args = {float&, float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:62:18: required from ‘void KernelDerivedTest(T, T, T) [with T = float]’
foo.cu:65:67: required from here
/usr/include/c++/6.4.1/tuple:489:65: error: mismatched argument pack lengths while expanding ‘std::is_convertible<_UElements&&, _Elements>’
return __and_<is_convertible<_UElements&&, _Elements>...>::value;
^~~~~
/usr/include/c++/6.4.1/tuple:490:1: error: body of constexpr function ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_ImplicitlyMoveConvertibleTuple() [with _UElements = {std::tuple<float*, float*, float*>}; bool <anonymous> = true; _Elements = {float*, float*, float*}]’ not a return-statement
}
^
/usr/include/c++/6.4.1/tuple: In instantiation of ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_NonNestedTuple() [with _SrcTuple = const std::tuple<float*, float*, float*>&; bool <anonymous> = true; _Elements = {float*, float*, float*}]’:
/usr/include/c++/6.4.1/tuple:662:419: required by substitution of ‘template<class ... _UElements, class _Dummy, typename std::enable_if<((std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ConstructibleTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyConvertibleTuple<_UElements ...>()) && std::_TC<(std::is_same<_Dummy, void>::value && (1ul == 1)), float*, float*, float*>::_NonNestedTuple<const tuple<_Elements ...>&>()), bool>::type <anonymous> > constexpr std::tuple< <template-parameter-1-1> >::tuple(const std::tuple<_Args1 ...>&) [with _UElements = {float*, float*, float*}; _Dummy = void; typename std::enable_if<((std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ConstructibleTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyConvertibleTuple<_UElements ...>()) && std::_TC<(std::is_same<_Dummy, void>::value && (1ul == 1)), float*, float*, float*>::_NonNestedTuple<const tuple<_Elements ...>&>()), bool>::type <anonymous> = <missing>]’
foo.cu:30:30: required from ‘void KernelBase<T, Derived>::SetArgs(Q&&, Args&& ...) [with int I = 0; Q = float&; Args = {float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:19:13: required from ‘void KernelBase<T, Derived>::operator()(Args&& ...) [with Args = {float&, float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:62:18: required from ‘void KernelDerivedTest(T, T, T) [with T = float]’
foo.cu:65:67: required from here
/usr/include/c++/6.4.1/tuple:495:244: error: wrong number of template arguments (4, should be 2)
return __and_<__not_<is_same<tuple<_Elements...>,
^
/usr/include/c++/6.4.1/type_traits:1558:8: note: provided for ‘template<class _From, class _To> struct std::is_convertible’
struct is_convertible
^~~~~~~~~~~~~~
/usr/include/c++/6.4.1/tuple:502:1: error: body of constexpr function ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_NonNestedTuple() [with _SrcTuple = const std::tuple<float*, float*, float*>&; bool <anonymous> = true; _Elements = {float*, float*, float*}]’ not a return-statement
}
^
/usr/include/c++/6.4.1/tuple: In instantiation of ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_NonNestedTuple() [with _SrcTuple = std::tuple<float*, float*, float*>&&; bool <anonymous> = true; _Elements = {float*, float*, float*}]’:
/usr/include/c++/6.4.1/tuple:686:422: required by substitution of ‘template<class ... _UElements, class _Dummy, typename std::enable_if<((std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_MoveConstructibleTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && std::_TC<(std::is_same<_Dummy, void>::value && (1ul == 1)), float*, float*, float*>::_NonNestedTuple<tuple<_Elements ...>&&>()), bool>::type <anonymous> > constexpr std::tuple< <template-parameter-1-1> >::tuple(std::tuple<_Args1 ...>&&) [with _UElements = {float*, float*, float*}; _Dummy = void; typename std::enable_if<((std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_MoveConstructibleTuple<_UElements ...>() && std::_TC<(1ul == sizeof... (_UElements)), float*, float*, float*>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && std::_TC<(std::is_same<_Dummy, void>::value && (1ul == 1)), float*, float*, float*>::_NonNestedTuple<tuple<_Elements ...>&&>()), bool>::type <anonymous> = <missing>]’
foo.cu:30:30: required from ‘void KernelBase<T, Derived>::SetArgs(Q&&, Args&& ...) [with int I = 0; Q = float&; Args = {float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:19:13: required from ‘void KernelBase<T, Derived>::operator()(Args&& ...) [with Args = {float&, float&, float&}; T = float; Derived = KernelDerived<float>]’
foo.cu:62:18: required from ‘void KernelDerivedTest(T, T, T) [with T = float]’
foo.cu:65:67: required from here
/usr/include/c++/6.4.1/tuple:495:244: error: wrong number of template arguments (4, should be 2)
return __and_<__not_<is_same<tuple<_Elements...>,
^
/usr/include/c++/6.4.1/type_traits:1558:8: note: provided for ‘template<class _From, class _To> struct std::is_convertible’
struct is_convertible
^~~~~~~~~~~~~~
/usr/include/c++/6.4.1/tuple:502:1: error: body of constexpr function ‘static constexpr bool std::_TC<<anonymous>, _Elements>::_NonNestedTuple() [with _SrcTuple = std::tuple<float*, float*, float*>&&; bool <anonymous> = true; _Elements = {float*, float*, float*}]’ not a return-statement
}
^
However, if file renamed to foo.cpp, and compiled using GCC:
g++ -std=c++11 -c foo.cpp
then it would compile fine.
The code in question is actually trying to define a base class for a CUDA kernel (this is KernelBase class), that different kernels implementation classes would then derive from (like KernelDerived class). Kernels would be run through base class operator() (as I removed CUDA code, the kernel arguments will be just printed to std::out here), and whole variadic templates/functions mumbo-jumbo is to make it possible for CUDA kernels implemented by derived classes to actually be run with different numbers of arguments passed (so kernel “implemented” by KernelDerived class in the code above would have three arguments).
If the code changed so that two arguments are used instead of three, it would compile and run properly. And code also builds and run properly on different installation, with CUDA 8 and GCC 4.9 installed.