Vittorio Romeo
Vittorio Romeo

Reputation: 93274

Pass static member function as template parameter

template<typename T> struct A 
{
    static T x(T, T) { }
    static T y(T, T) { }
};

template<typename T> struct B 
{
    static T x(T, T) { }
    static T y(T, T) { }
};

struct Dispatcher
{
    template<template<typename> class TC, ??? TStaticFunc, 
             typename T1, typename T2> 
    static std::common_type_t<T1, T2> call(T1 mI, T2 mJ)
    {
        return TC<std::common_type_t<T1, T2>>::*TStaticFunc(mI, mJ);
    }
};

Dispatcher::call<A, x>(12.f, 5);
Dispatcher::call<B, x>(1.f, 51);
Dispatcher::call<A, y>(2.f, 25);
Dispatcher::call<B, y>(5.f, 3);

Is it possible to create a templatized function that works like Dispatcher::call? I'd like to separately pass the class type and the static function name.

Or is passing A<..:>::x the only possible way of achieving this kind of dispatch?

Upvotes: 3

Views: 2498

Answers (1)

gwiazdorrr
gwiazdorrr

Reputation: 6329

Well, you can do it, if you really, really, really want to. You just have to define a type, a function or a variable (basically something compile-time unique) for each static function name. This one of the options:

namespace detail
{
    template <typename T>
    struct x
    {
        static decltype(&T::x) get() { return &T::x; }
    };

    template <typename T>
    struct y
    {
        static decltype(&T::y) get() { return &T::y; }
    };

    // etc, you can use macros here to avoid boilerplate
}

Now alter the Dispatcher a bit...

struct Dispatcher
{
    template<template<typename> class TC, template<typename> class FuncGetter,
        typename T1, typename T2> 
        static typename std::common_type<T1, T2>::type call(T1 mI, T2 mJ)
    {
        typedef TC<typename std::common_type<T1, T2>::type> T;
        auto staticMemberFunc = FuncGetter<T>::get();
        return staticMemberFunc(mI, mJ);
    }
};

And now following code:

using namespace detail;
Dispatcher::call<A, x>(12.f, 5);
Dispatcher::call<B, x>(1.f, 51);
Dispatcher::call<A, y>(2.f, 25);
Dispatcher::call<B, y>(5.f, 3);

Works. If you decide to follow this pattern, I recommend you change x and y into x_tag or something along these lines.

Alternatively, you can just write a macro to use instead of Dispatcher::call.

Upvotes: 6

Related Questions