I have a family of parameterized functions, like:

template<typename T> struct SomeFunc { static void *call(void *arg) { return arg; } }; 

I need to select one of the template specializations of the desired function at runtime depending on the external parameter (of course, all the relevant specializations must be determined at compile time).

In a very primitive implementation, it looks like this:

 template<template<typename> class F> void *choose_from(void *arg, char run_time_type_of_arg) { switch (run_time_type_of_arg) { case 'i': return F<int>::call(arg); case 'l': return F<long>::call(arg); case 'd': return F<double>::call(arg); default: return nullptr; } } 

Called it is very convenient:

 void *test_some_func(void *arg, char run_time_type_of_arg) { return choose_from<SomeFunc>(arg, run_time_type_of_arg); } 

But I am absolutely not satisfied with the lack of extensibility / flexibility, cases hard-coded in the code and the inability to set a different set of specializations for different functions.

Therefore, I want to rewrite this construct using type traits and more general patterns based on them:

 template<typename T> struct TypeTrait { const static char value = 0; }; template<> struct TypeTrait<int> { const static char value = 'i'; }; template<> struct TypeTrait<long> { const static char value = 'l'; }; template<> struct TypeTrait<double> { const static char value = 'd'; }; template<template<typename> class F, typename T1> struct ChooseFrom1 { inline static void *call(void *arg, char run_time_type_of_arg) { if (run_time_type_of_arg == TypeTrait<T1>::value) return F<T1>::call(arg); else return nullptr; } }; template<template<typename> class F, typename T1, typename T2> struct ChooseFrom2 { inline static void *call(void *arg, char run_time_type_of_arg) { if (run_time_type_of_arg == TypeTrait<T1>::value) return F<T1>::call(arg); else return ChooseFrom1<F, T2>::call(arg, run_time_type_of_arg); } }; 

By embedding the functions, all calls are completely removed at the compilation stage, and the result is machine code, almost completely identical to the first implementation on switch / case (I checked the listings - all that). But I could not get a recursive template (as you can see, ChooseFrom1 and ChooseFrom2 have different names). You can call it, for example, like this (it is not at all convenient):

 void *test_some_func_2(void *arg, char run_time_type_of_arg) { auto a = ChooseFrom1<SomeFunc, double>::call(arg, run_time_type_of_arg); if (a) return a; return ChooseFrom2<SomeFunc, int, long>::call(arg, run_time_type_of_arg); } 

Ideally, I want to get one common pattern that could be called like this:

 void *test_some_func_ideal(void *arg, char run_time_type_of_arg) { return ChooseFrom<SomeFunc, int, long, double, Whatever_I_Want>::call(arg, run_time_type_of_arg); } 

But all attempts to combine ChooseFrom1 and ChooseFrom2 ... ChooseFromN into one recursive pattern (one generalized pattern for the recursion itself plus one of its specialization to stop the recursion) run into compilation errors.

I would be grateful for any advice on the implementation of this idea or the rationale for the theoretical possibility of such implementation.

    1 answer 1

    Perhaps you need this:

     template<template<typename> class F, typename... Types> struct ChooseFrom { }; template<template<typename> class F, typename T1, typename... TRest> struct ChooseFrom<F, T1, TRest...> { inline static void *call(void *arg, char run_time_type_of_arg) { if (run_time_type_of_arg == TypeTrait<T1>::value) return F<T1>::call(arg); else return ChooseFrom<F, TRest...>::call(arg, run_time_type_of_arg); } }; template<template<typename> class F> struct ChooseFrom<F> { inline static void *call(void *arg, char run_time_type_of_arg) { return nullptr; } }; 

    On the other hand, in your place I would try not to bring up the type transfer via typeid emulation in runtime, but to stretch the type as a template parameter in the program. In this case, it would be possible, in theory, to do without ChooseFrom at all, and use Directly SomeFunc<T>::call .