Dynamic dispatching of template functions?

前端 未结 5 791
挽巷
挽巷 2020-12-10 14:02

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

template
struct A {
    static void foo() {/*...*/}
};

vo         


        
5条回答
  •  没有蜡笔的小新
    2020-12-10 14:33

    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 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
        void
        operator()(T const& t) const
        { foo(t); } // the real work is in template 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
    struct indices {
        typedef indices next;
    };
    
    template
    struct build_indices {
        typedef typename build_indices::type::next type;
    };
    
    template<>
    struct build_indices<0> {
        typedef indices<> type;
    };
    
    template
    void
    bar(int i, indices)
    {
        static void (*lookup[])() = { &A::f... };
        lookup[i]();
    }
    

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

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

    which is called as bar(i).

提交回复
热议问题