4

I would like to know what is the correct way to retrieve the value of a variadic template constant argument at position N (N is known at compile time). For instance, let's say you have a template that receives a variadic number of function pointers as arguments, and you need to retrieve the second function pointer. For now, all I've been able to come up with is this...

typedef int (*func)(int);

template< func... F >
struct testme
{
 inline int getme(int p) const
 {
  return std::array< func , sizeof... (F) >{F...}[1](p);
 }
};

... which, needless to say, is very hackish. Is there a better way to do this? Thanks.

EDIT:

Based on typedeftemplate's code, I made a version that can accept any type as the variadic template argument. It has been tested to work on an experimental build of GCC 4.6. I figured out it could be useful to somebody else so there it is...

template< std::size_t I, typename T, T... Args >
struct va_lookup;

template< std::size_t I, typename T, T Arg, T... Args >
struct va_lookup< I, T, Arg, Args... >
{
    static_assert(I <= sizeof... (Args), "index is out of bound");
    static constexpr T value = va_lookup< I - 1, T, Args... >::value;
};

template< typename T, T Arg, T... Args >
struct va_lookup< 0, T, Arg, Args... >
{
    static constexpr T value = Arg;
};
pmjobin
  • 813
  • 1
  • 7
  • 15
  • 3
    Your use of the `inline` keyword is redundant. – GManNickG Jan 25 '11 at 01:02
  • @Gman - Thanks for pointing this out. I know that template code is usually inlined by the compiler since it is generated in the same translation unit than that of the code that instantiates the template. But can't the inline keyword be used to "enforce" this behavior? (ie: to prevent the generation of a stand-alone definition of the function) Also, why does the STL implementation of GCC use this keyword all over the place? I'm starting to think of the inline keyword as the placebo of C++, much in the same vein as the register keyword in C. :) – pmjobin Jan 25 '11 at 15:08
  • 2
    The `inline` keyword has very little to do, or even nothing to do to, with actual inlining anymore. Compiler inlining capability is far beyond the need for us silly humans to try to guess what's good to inline, especially with Link-time Code Generation and such. It's best to think of the `inline` keyword as saying, "I'm providing the function definition inline (right here), and you may see it again." That is, to disable the one-definition rule. I can't speak for GCC, but member functions defined in the class definition are implicitly `inline`. – GManNickG Jan 25 '11 at 21:17

2 Answers2

4

You can use something along these lines, I think:

template <int Index, int... Args> struct LookupAtIndex;
template <int Index, int First, int... Rest> struct LookupAtIndex<Index, First, Rest...> {
    static constexpr int result = LookupAtIndex<Index - 1, Rest...>::result;
};
template <int First, int... Rest> struct LookupAtIndex<0, First, Rest...> {
    static constexpr int result = First;
};

I haven't tested this out, but at least intuitively it seems like it should work out correctly.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    You'll want to set an enum rather than class variable. – Edward Strange Jan 24 '11 at 23:44
  • 2
    @Noah Roberts- Any reason for using an enum rather than a static const field? – templatetypedef Jan 25 '11 at 00:54
  • 1
    For one thing, unlike an enum, a static const isn't always a constant expression and can require the variable, which of course requires definition. – Edward Strange Jan 25 '11 at 01:09
  • 1
    @templatetypedef, @Noah: With variadic templates, we're talking C++0x, so it should be possible to use a `constexpr` instead of `enum`. But the code here, using `const`, does suffer from the problems Noah mentions. – Ben Voigt Jan 25 '11 at 03:16
  • @Ben Voigt, @Noah Roberts- Updated the above code to use `constexpr`. Is that valid? – templatetypedef Jan 25 '11 at 06:58
  • 1
    @templatetypedef: If result type isn't an integral type as in the question, the code seems not to be compiled on gcc4.5.1... May I suggest making result a constexpr function like here? http://ideone.com/uAiiv – Ise Wisteria Jan 25 '11 at 09:50
  • @templatetypedef, @Noah Robert, @Ben Voigt, @Ise Wisteria - Thank you all for your invaluable help. Since my usescase requires a non-integral type result as Ise mentions, the constexpr keyword looks like the correct way to go. I've tested it under GCC 4.6 and it works like a charm. – pmjobin Jan 25 '11 at 14:53
1

Here is a variation of your solution that might be more palatable:

typedef int (*func)(int);

template< func... F >
struct testme
{
    static const std::array< func , sizeof... (F) > ptrs_;

    int getme(int p) const
    {
        return ptrs_[1](p);
    }
};

template< func... F >
const std::array< func , sizeof... (F) >
testme<F...>::ptrs_ = {F...};

One of the basic problems with your use case is that function pointers aren't as easy to do template meta-programming with as integral types.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577