Unfortunately it is not possible to call an arbitrary function passing arbitrary values in a complete dynamic way at runtime. At least some part of the C++ code must compile a static call to a function with the same exact signature.
The problem is that in C++ the parameter passing can only be handled by the compiler and even variadic functions (e.g. printf
) where only inspection is performed at runtime normally require a different and simpler calling convention.
One technical problem is that modern ABIs are extremely complex and asymmetrical (e.g. some parameters in registers, some in special FPU registers, some in the stack) and are only handled at compile time.
What you can do is "wrap" the functions you want to be able to call in a function accepting for example an std::vector
of generic values and then calling the function passing those parameters converted to the proper type, e.g. something like
Value myabs(const std::vector<Value>& args) {
if (args.size() != 1) throw std::runtime_error("Bad arity");
return Value(fabs(args[0].to<double>()));
}
then it is easy to dynamically call these wrappers because the signature is the same for all of them.
Those wrappers unfortunately must be written (or generated) for each of the functions you need to be able to call so the compiler can actually generate the code needed to call the regular C++ functions.
I am not a template expert, but C++ metaprogramming can be used to generate the wrappers instead of coding each of them manually or writing an external code generator.
This is a very basic example:
typedef Value (*FWrapper)(const std::vector<Value>&);
template<typename RV, typename T1, typename T2, RV (*f)(T1, T2)>
Value wrapper2(const std::vector<Value>& args) {
if (args.size() != 2) throw std::runtime_error("Bad arity");
return f(args[0].to<T1>(), args[1].to<T2>());
}
For example to wrap double atan2(double, double)
you can simply write:
FWrapper myatan2 = &wrapper2<double, double, double, &atan2>;
I tried a bit without success to avoid passing explicitly the return value type and number and types of parameters and to extract them instead from the passed function pointer, but may be that is also possible or even practical with recent C++.
The idea is basically to invoke the compiler to build a specific function call on demand when doing a template instantiation.