11

Is it possible to decide in run-time which template function to call? Something like:

template<int I>
struct A {
    static void foo() {/*...*/}
};

void bar(int i) {
    A<i>::f();   // <-- ???
}
Predrag
  • 1,527
  • 2
  • 13
  • 17

5 Answers5

16

A typical 'trick' to bridge compile time and runtime when dealing with templates is visiting a variant type. That's what the Generic Image Library (available as Boost.GIL or standalone) does for instance. It typically takes the form of:

typedef boost::variant<T, U, V> variant_type;
variant_type variant = /* type is picked at runtime */
boost::apply_visitor(visitor(), variant);

where visitor is a polymorphic functor that simply forwards to the template:

struct visitor: boost::static_visitor<> {
    template<typename T>
    void
    operator()(T const& t) const
    { foo(t); } // the real work is in template<typename T> void foo(T const&);
};

This has the nice design that the list of types that the template will/can be instantiated with (here, the variant_type type synonym) is not coupled to the rest of the code. Metafunctions like boost::make_variant_over also allows computations over the list of types to use.

Since this technique is not available to non-type parameters, you need to 'unroll' the visitation by hand, which unfortunately means the code is not as readable/maintainable.

void
bar(int i) {
    switch(i) {
        case 0: A<0>::f(); break;
        case 1: A<1>::f(); break;
        case 2: A<2>::f(); break;

        default:
            // handle
    }
}

The usual way to deal with the repetition in the above switch is to (ab)use the preprocessor. An (untested) example using Boost.Preprocessor:

#ifndef LIMIT
 #define LIMIT 20 // 'reasonable' default if nothing is supplied at build time
#endif
#define PASTE(rep, n, _) case n: A< n >::f(); break;

void
bar(int i) {
    switch(i) {
        BOOST_PP_REPEAT(LIMIT, PASTE, _)

        default:
            // handle
    }
}

#undef PASTE
#undef LIMIT

Better find good, self-documenting names for LIMIT (wouldn't hurt for PASTE either), and limit the above code-generation to just one site.


Building from David's solution and your comments:

template<int... Indices>
struct indices {
    typedef indices<Indices..., sizeof...(Indices)> next;
};

template<int N>
struct build_indices {
    typedef typename build_indices<N - 1>::type::next type;
};

template<>
struct build_indices<0> {
    typedef indices<> type;
};

template<int... Indices>
void
bar(int i, indices<Indices...>)
{
    static void (*lookup[])() = { &A<Indices>::f... };
    lookup[i]();
}

then to call bar: bar(i, typename build_indices<N>::type()) where N would be your constant-time constant, sizeof...(something). You can add a layer to hide the 'ugliness' of that call:

template<int N>
void
bar(int i)
{ bar(i, typename build_indices<N>::type()); }

which is called as bar<N>(i).

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • Can I make compiler unroll it for me if I know (at compile time) how many cases are there? – Predrag Aug 17 '11 at 08:16
  • Sorry for being pain ... but my knowledge of the number of cases comes from the `size...()` operator. I'm afraid that preprocessor won't help in that case. Can you think of something that can help me with that constraint? – Predrag Aug 17 '11 at 08:40
  • @Predrag Pick an upper limit big enough and that you're comfortable with. – Luc Danton Aug 17 '11 at 08:49
  • Correction for the previous comment: it is `sizeof...()` operator. – Predrag Aug 17 '11 at 08:59
  • @Predrag I wanted to add a comment to David's answer but it wouldn't have fit (or be too cryptic). Again, something untested, but I use this `indices` helper quite a lot to convert from a constant to an integer range. – Luc Danton Aug 17 '11 at 09:09
  • 1
    wow! I finally understood indices/build_indices code. This is the answer to "how to build a parameter pack" question, that I never asked but intended to. Thank you. – Predrag Aug 17 '11 at 12:17
  • how on earth did this answer not get the other 15 upvotes it requires? +1 from me – sehe Nov 02 '11 at 00:04
  • Is `lookup` a function or an array? At first you're initializing it as an array and then you call it as a function... – David G Aug 26 '14 at 03:10
  • @0x499602D2 Good catch, this should fix it. – Luc Danton Aug 26 '14 at 03:34
  • Would it be possible to pass `A` as a template template parameter and call the function with: `bar(i)`? It would allow it to be a bit easier to reuse. – Adrian17 Mar 28 '15 at 16:13
8

Depending on what you want to do exactly (i.e. is there a small number of limited instantiations that you want to use?) you can create a lookup table and then use that dynamically. For a fully manual approach, with options 0, 1, 2, and 3, you could do:

void bar( int i ) {
   static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo };
   lookup[i]();
}

Of course, I chose the simplest option for the example. If the number you need are not consecutive or zero based, you might prefer a std::map<int, void (*)(void) > rather than an array. If the number of different choices you want to use is larger, you might want to add code to automatically intantiate the templates, rather than manually typing all of them... But you would have to consider that each instantiation of the template creates a new function, and you might want to check whether you actually need that.

EDIT: I have written a post implementing the same initialization using only C++03 features, it seemed too long for an answer.

Luc Danton wrote an interesting answer here that includes among other things initialization of the lookup table using C++0x constructs. I don't quite like from that solution that it changes the interface to require an extra argument, but that can easily be solved through an intermediate dispatcher.

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • That is what I was looking for. Can you show me how to automatically initialize array or map (or direct me somewhere)? – Predrag Aug 17 '11 at 07:56
  • @Predrag: How many different values do you want? Are they consecutive? zero-based? Also note that each instantiation will create a new function, and that in turn means that it will make your binary grow... – David Rodríguez - dribeas Aug 17 '11 at 08:03
  • They are zero-based and I know at compile-time how many of them exist. – Predrag Aug 17 '11 at 08:06
  • Would you consider moving my (latest) edit to your answer? It's a way to initialize `lookup` from a compile-time constant. I think it belongs to your answer, which is about a lookup-table, and not mine, which mentions a switch statement. And it's way too long to be a comment. – Luc Danton Aug 17 '11 at 09:14
  • @Luc: I hope that you don't mind that instead of borrowing the implementation I link to yours. Hopefully you will get the credit you deserve. – David Rodríguez - dribeas Aug 17 '11 at 10:20
4

No, templates are a compile-time feature, and i is not known at compile-time so this is impossible. A<I>::foo() should be adapted to something like A::foo(i).

tenfour
  • 36,141
  • 15
  • 83
  • 142
  • I am aware of that. I wander if maybe there is some workaround. – Predrag Aug 17 '11 at 07:39
  • 1
    The workaround is either to make `i` known at compile-time (like @iammilind's solution), or make `A::foo` not require a compile-time argument (my solution). – tenfour Aug 17 '11 at 07:48
  • 5
    There is a third way (if there is a limited number of choices): build a lookup table by instantiating all of the functions at compile time, and then dispatch to one of them at runtime. I have used that before and it is painful, but feasible. – David Rodríguez - dribeas Aug 17 '11 at 07:58
1

NO
Templates implement compile time polymorphism not run time polymorphism.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
1

Template argument must be known at compile time. So there is no way for compiler to pass A<i>::foo().

If you want work around then you have to make bar() also a template:

template<int i>
void bar() {
    A<i>::f();   // ok
}

For that, you must know argument to bar() at compile time.

iammilind
  • 68,093
  • 33
  • 169
  • 336