开发者

Dynamic dispatching of template functions?

Is it possible to decide in run-time which template function to call? Som开发者_Python百科ething like:

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

void bar(int i) {
    A<i>::f();   // <-- ???
}


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).


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.


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).


NO
Templates implement compile time polymorphism not run time polymorphism.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜