16

I wonder what is the easiest and more common way to get the N-th parameter of a variadic templated class at compile-time (The returned value has to be as a static const for the compiler in order to do some optimizations). Here is the form of my templated class :

template<unsigned int... T> MyClass
{
    // Compile-time function to get the N-th value of the variadic template ?
};

Thank you very much.

EDIT : As MyClass will contain more than 200 functions, I can't specialize it. But I can specialize a struct or a function inside MyClass.

EDIT : Final solution derived from the validated answer :

#include <iostream>

template<unsigned int... TN> class MyClass
{
    // Helper
    template<unsigned int index, unsigned int... remPack> struct getVal;
    template<unsigned int index, unsigned int In, unsigned int... remPack> struct getVal<index, In,remPack...>
    {
        static const unsigned int val = getVal<index-1, remPack...>::val;
    };
    template<unsigned int In, unsigned int...remPack> struct getVal<1,In,remPack...>
    {
        static const unsigned int val = In;
    };

    // Compile-time validation test
    public:
        template<unsigned int T> inline void f() {std::cout<<"Hello, my value is "<<T<<std::endl;}
        inline void ftest() {f<getVal<4,TN...>::val>();} // <- If this compile, all is OK at compile-time
};
int main()
{
    MyClass<10, 11, 12, 13, 14> x;
    x.ftest();
    return 0;
}
Vincent
  • 57,703
  • 61
  • 205
  • 388
  • 1
    "Design by induction" should work nicely here, you need a template with a partial specialization for the base case. – Ben Voigt Aug 04 '12 at 19:36
  • The template I refer to in my comment is not necessarily the template class in your question, but a helper. – Ben Voigt Aug 04 '12 at 19:42

4 Answers4

10

"Design by induction" should come out something like this:

template<unsigned int N, unsigned int Head, unsigned int... Tail>
struct GetNthTemplateArgument : GetNthTemplateArgument<N-1,Tail...>
{
};


template<unsigned int Head, unsigned int... Tail>
struct GetNthTemplateArgument<0,Head,Tail...>
{
    static const unsigned int value = Head;
};

template<unsigned int... T> 
class MyClass
{
     static const unsigned int fifth = GetNthTemplateArgument<4,T...>::value;
};
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I tried It and I think this is a good way to do it but I got the following error on GCC 4.6.2 : sorry, unimplemented: cannot expand 'Tail ...' into a fixed-length argument list – Vincent Aug 04 '12 at 19:59
  • 4
    @Vincent: That means your compiler isn't new enough. – Ben Voigt Aug 04 '12 at 21:08
6

Here is another way to do it:

template<unsigned int index, unsigned int In, unsigned int... remPack> struct getVal
{
    static const unsigned int val = getVal<index-1, remPack...>::val;
};
template<unsigned int In, unsigned int...remPack> struct getVal<0,In,remPack...>
{
    static const unsigned int val = In;
};

template<unsigned int... T> struct MyClass
{
    //go to any arg by : getVal<Some_Unsigned_Index, T...>::val;
};

Test: http://liveworkspace.org/code/4a1a9ed4edcf931373e7ab0bf098c847

and if you get sting by "cannot expand 'T...' into a fixed-length argument list" http://ideone.com/YF4UJ

Mr.Anubis
  • 5,132
  • 6
  • 29
  • 44
  • Same problem as the other answer : sorry, unimplemented: cannot expand 'remPack ...' into a fixed-length argument list. Any trick to avoid this ? – Vincent Aug 04 '12 at 20:03
  • @Vincent did you try last test url http://liveworkspace.org/code/e44f694c0ed618c3ad493dcc9bf3d347? – Mr.Anubis Aug 04 '12 at 20:04
  • Perfect ! Works like a charm, and all at compile time ! Thank you very much – Vincent Aug 04 '12 at 20:06
  • Confused why you decided to add an identical answer, when mine also works just fine with up-to-date g++: http://liveworkspace.org/code/1ed1244b61a6689935eea8e33e4fb873 – Ben Voigt Aug 04 '12 at 21:12
  • A bit strange to make the first index 1 instead of 0, if I'm reading this correctly. – GManNickG Aug 04 '12 at 21:49
  • @BenVoigt My intention was to post another solution which is diff from yours, isn't it? – Mr.Anubis Aug 05 '12 at 04:01
  • @Mr.Anubis: It is, slightly, because you didn't use inheritance. – Ben Voigt Aug 05 '12 at 05:55
5

Here is what you can also do

template<int N, typename T, T ... Ts>
struct GetN {
  constexpr T values[sizeof...(Ts)] = { Ts... };
  static const T value = values[N];
};

template<int N, typename T, T ... Ts>
constexpt T GetN<N, T, Ts...>::values[sizeof...(Ts)];

Then you can simply do

template<int N, unsigned int... T> struct MyClass {
  static const unsigned int value = GetN<N, unsigned int, T...>::value;
};
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • This does not compile with the ICC (version 15.0.2). It raises the error: *"constexpr" is not valid here*. If I make the values `static`, then another error: *a member of type "const T []" cannot have an in-class initializer*. – eold Feb 25 '15 at 21:51
3

Yet another simple method:

#include <array>

template<int... Args>
class Foo {
 public:
  static constexpr int Element(int index) {
    return std::array<int, sizeof...(Args)>{ Args... }[index];
  }

 int First = Element(0);
 int Second = Element(1);
};


int main() {
  return Foo<0, 1>::Element(0);  
}

// or just
int Nth = std::array<int, sizeof...(Args)>{ Args... }[N];

BTW, here is a general method of extracting N-th argument of any variadic template:

#include <tuple>

template<typename... Args>
class Foo {
 public:
  template <int N>
  using Nth = typename std::remove_reference<decltype(std::get<N>(std::declval<std::tuple<Args...>>()))>::type;
};


int main() {
  Foo<int, float>::Nth<1> val = 3.14159f;
  return 0; 
}
markhor
  • 2,235
  • 21
  • 18