Suppose you had the arguments in a vector
of some kind, and a known function (fully).
You can call this. Call the function that does this invoke
.
Next, work out how to do this for template<class... Args>
. Augment invoke
.
So you have written:
typedef std::vector<run_time_stuff> run_time_args;
template<class... Args>
void invoke( void(*func)(Args...), run_time_args rta )
at this point. Note that we know the types of the argument. I do not claim the above is easy to write, but I have faith you can figure it out.
Now we wrap things up:
template<class...Args>
std::function<void(run_time_args)> make_invoker(void(*func)(Args...)){
return [func](run_time_args rta){
invoke(func, rta);
};
}
and now instead of void*
you store std::function<void(run_time_args)>
-- invokers. When you add the function pointers to the mechanism you use make_invoker
instead of casting to void*
.
Basically, at the point where we have the type info, we store how to use it. Then where we want to use it, we use the stored code!
Writing invoke
is another problem. It will probably involve the indexes trick.
Suppose we support two kinds of arguments -- double
and int
. The arguments at run time are then loaded into a std::vector< boost::variant<double, int> >
as our run_time_args
.
Next, let us extend the above invoke
function to return an error in the case of parameter type mismatch.
enum class invoke_result {
everything_ok,
error_parameter_count_mismatch,
parameter_type_mismatch,
};
typedef boost::variant<int,double> c;
typedef std::vector<run_time_stuff> run_time_args;
template<class... Args>
invoke_result invoke( void(*func)(Args...), run_time_args rta );
now some boilerplate for the indexes trick:
template<unsigned...Is>struct indexes{typedef indexes type;};
template<unsigned Max,unsigned...Is>struct make_indexes:make_indexes<Max-1, Max-1,Is...>{};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Max>using make_indexes_t=typename make_indexes<Max>::type;
With that, we can write an invoker:
namespace helpers{
template<unsigned...Is, class... Args>
invoke_result invoke( indexes<Is...>, void(*func)(Args...), run_time_args rta ) {
typedef void* pvoid;
if (rta.size() < sizeof...(Is))
return invoke_result::error_parameter_count_mismatch;
pvoid check_array[] = { ((void*)boost::get<Args>( rta[Is] ))... };
for( pvoid p : check_array )
if (!p)
return invoke_result::error_parameter_type_mismatch;
func( (*boost::get<Args>(rts[Is]))... );
}
}
template<class... Args>
invoke_result invoke( void(*func)(Args...), run_time_args rta ) {
return helpers::invoke( make_indexes_t< sizeof...(Args) >{}, func, rta );
}
And that should work when func
's args exactly match the ones passed in inside run_time_args
.
Note that I was fast and loose with failing to std::move
that std::vector
around. And that the above doesn't support implicit type conversion. And I didn't compile any of the above code, so it is probably littered with typos.