Obviously you can't save a list of types in this way
using type = Arg;
where Arg
is a variadic list of types.
But you can save they in a type container and std::tuple
can do this works too. So I suggest to modify the method_traits
specialization as follows
template <typename T>
struct method_traits;
template <typename T, typename Ret, typename... Args>
struct method_traits<Ret(T::*)(Args...)>
{ using tTypes = std::tuple<Args...>; };
and rewrite argument_types
to intercept the std::tuple
template <typename T>
using tTypes = typename method_traits<T>::tTypes;
Now you can use the default template value and partial specialization trick defining node
template <typename T, typename TArgs = tTypes<decltype(&T::process)>>
struct Node;
In this way, instantiating a Node<T>
object, you effectively get a Node<T, tTypes<decltype(&T::process)>
that is a Node<T, std::tuple<Args...>>
with the wanted Args...
.
So you can simply define the following partial specialization of Node
as follows
template <typename T, typename ... Args>
struct Node<T, std::tuple<Args...>>
{
T t;
Node (Input<Args> ... inputs)
{ /* do something */ }
};
The following is a full working example
#include <tuple>
#include <type_traits>
template <typename T>
struct tWrapper
{ using type = T; };
template <typename T>
using Input = typename tWrapper<T>::type;
template <typename T>
struct method_traits;
template <typename T, typename Ret, typename... Args>
struct method_traits<Ret(T::*)(Args...)>
{ using tTypes = std::tuple<Args...>; };
template <typename T>
using tTypes = typename method_traits<T>::tTypes;
template <typename T, typename TArgs = tTypes<decltype(&T::process)>>
struct Node;
template <typename T, typename ... Args>
struct Node<T, std::tuple<Args...>>
{
T t;
Node (Input<Args> ... inputs)
{ /* do something */ }
};
struct foo
{
float process (float a, int b)
{ return a+b; }
};
int main ()
{
Node<foo> nf(1.0f, 2);
}
-- EDIT --
As pointed by Julius (and the OP themselves) this solution require an additional template type with a default template value.
In this simplified case isn't a problem but I can imagine circumstances where this additional template argument can't be added (by example: if Node
receive a variadic list of template arguments).
In those cases, Julius propose a way that complicate a little the solution but permit to avoid the additional template parameter for Node
: to add a template base class, that receive the TArgs
arguments, and to works with constructor inheritance.
That is: defining a NodeBase
as follows
template <typename, typename>
struct NodeBase;
template <typename T, typename ... Args>
struct NodeBase<T, std::tuple<Args...>>
{
T t;
NodeBase (Input<Args> ...)
{ /* do something */ }
};
there is no need for an additional template parameter, for Node
, that can simply written as
template <typename T>
struct Node
: public NodeBase<T, tTypes<decltype(&T::process)>>
{ using NodeBase<T, tTypes<decltype(&T::process)>>::NodeBase; };
Julius, following this idea, prepared a solution that (IMHO) is even better and interesting.
Node
only depends on a single typeT
and not on multiple types – GemmagemmateNode
stores theInput
s in a tuple so I guess making the constructor a variadic template would work. – GemmagemmateInput
? Is that important to the question? – Uncourtlystd::is_function
at en.cppreference.com/w/cpp/types/is_function for a hint on how it would need to be done properly. – Marmara