How to detect the first and the last argument in the variadic templates?
For the 1st argument it is easy (just compare sizeof...(T)
with 0), but is there a way to detect the last element?
The example :
#include <iostream>
#include <typeinfo>
template < class... T >
struct A
{
int foo(int k){ return k; };
};
template < class T1, class... T >
struct A< T1, T... >
{
A() :a()
{
std::cout<<"A i="<<sizeof...(T)<<std::endl
<<" a type = " << typeid(T1).name()<<std::endl;
}
int foo(int k){ return anotherA.foo( a.foo(k) ); };
T1 a;
A< T... > anotherA;
};
struct B1
{
B1(){ std::cout<<"b1"<<std::endl; };
int foo(int k){ std::cout<<"b1::foo() k="<<k<<std::endl; return k+1; };
};
struct B2
{
B2(){ std::cout<<"b2"<<std::endl; };
int foo(int k){ std::cout<<"b2::foo() k="<<k<<std::endl; return k+2; };
};
struct B3
{
B3(){ std::cout<<"b3"<<std::endl; };
int foo(int k){ std::cout<<"b3::foo() k="<<k<<std::endl; return k+3; };
};
int main ()
{
A< B3, B2, B1 > a;
std::cout<<"the value is "
<<a.foo(5)
<< std::endl;
}
I'm not positive if this is what you want. But here are two utilities named first
and last
that take variadic templates and typedef the first and last type respectively:
#include <iostream>
#include <typeinfo>
template <class T1, class ...T>
struct first
{
typedef T1 type;
};
template <class T1, class ...T>
struct last
{
typedef typename last<T...>::type type;
};
template <class T1>
struct last<T1>
{
typedef T1 type;
};
template <class ...T>
struct A
{
typedef typename first<T...>::type first;
typedef typename last<T...>::type last;
};
struct B1 {};
struct B2 {};
struct B3 {};
int main()
{
typedef A<B1, B2, B3> T;
std::cout << typeid(T::first).name() << '\n';
std::cout << typeid(T::last).name() << '\n';
}
Here's another set of code with a convenience function return_type
that you could use to access any type at a specific index in a varadic template list ... you could then adapt the call to return_type
so that you get the first and the last arguments (i.e., the first argument will be at 0, and the last argument will be at sizeof...(TypeList)
):
template<typename T>
struct type_id_struct
{
typedef T type;
T object_instance;
};
template<int N, typename... TypeList>
struct reduce {};
template<int N, typename T1, typename... TypeList>
struct reduce<N, T1, TypeList...>
{
typedef typename reduce<N - 1, TypeList... >::type type;
};
template<typename T1, typename... TypeList>
struct reduce<0, T1, TypeList...>
{
typedef T1 type;
};
//convenience function
template<int N, typename... TypeList>
type_id_struct<typename reduce<N, TypeList...>::type> return_type()
{
return type_id_struct<typename reduce<N, TypeList...>::type>();
}
Here's an example of using the convenience function return_type
in actual code to determine the Nth template argument in a variadic template:
int main()
{
auto type_returned = return_type<2, int, double, char>();
std::cout << typeid(type_returned.object_instance).name() << std::endl;
return 0;
}
In this case, since the int
template argument to return_type
is 2
, you'll get the char
type as the output. Any number over 2
will cause an overflow that will create a compile rather than runtime error. As noted, you could adapt it so that it's wrapped inside a function in a structure that will allow you to access the types in the variadic template for that specific structure instance using the sizeof...(TypeList) - 1
applied to an enum. For instance:
template<typename... TypeList>
struct an_object
{
enum { first = 0, last = (sizeof...(TypeList) - 1) };
template<int N>
auto wrapper() -> decltype(return_type<N, TypeList...>())
{
return return_type<N, TypeList...>();
}
};
//...more code
int main()
{
an_object<int, double, char> a;
auto r_type1 = a.wrapper<an_object<int, double, char>::first>();
std::cout << typeid(r_type1.object_instance).name() << std::endl;
auto r_type2 = a.wrapper<an_object<int, double, char>::last>();
std::cout << typeid(r_type2.object_instance).name() << std::endl;
return 0;
}