Today I passed by a question posted to accu-general mailing list of Accu organization, which raised my interest. The guy was asking if it is possible to call a function, previously casted to void* pointer, passing to it a number of homogeneous arguments, with their unknown exact quantity at compile–time, that are contained in a container, e.g. vector. Simply saying, expressed in pseudo-code, he wanted something like:
template <class TParameterType>
void call_function(void *pFunction, std::vector<TParameterType> pars)
{
pFunction(pars.begin(), pars.end());
}
Easy to imagine — tricky to implement!
I felt like I remembered I couldn’t have achieved that some few years ago, in c++03, when I had similar problem for myself. Some discussion in the list made me feel that it’s really kinda tricky task. But what came to my mind were variadic templates from c++0x. Well, we already know, partially from my previous posts, how to manipulate parameters typed with variadic templates. So I decided to apply similar technique to accomplish this task as well, which I had success with!
Some few words on the above excerpt: it’s important that we don’t have the exact function pointer type inside call_function, we have clear void*. Otherwise the task would’ve been somehow simpler. But of course arity of the function actually pointed by pFunction should match the number of parameters contained in pars, otherwise we’ll end up calling function with wrong number of arguments, with all the consequences.
Typical usage of that would be to create a table of functions each accepting some specific number of args, keep it in some homogeneous way (like vector of void* pointers), and then, before calling call_function, to chose the right function pointer from the table based on the number of elements in pars vector. So that at runtime arbitrary number can be accepted and correctly passed.
So, there we go. Below is just the same example that I posted to the list, with some more comments. This is a completely working example, buildable at least with gcc-4.4.1 on Ubuntu 9.10, with -std=c++0x option.
#include <cstdlib>
#include <vector>
#include <stdio.h>
// We need to wrap function in class to bypass impossibility to have partially specialized template functions
// nInst is the "reverse" number of instantiations. If first called with value X, then each instantiation of F<Y>
// would serve a target function call with arity (X-Y). In total we'll have X instantiations, meaning that we'll have
// functions with up to X arguments
template <int nInst>
struct F
{
// Iterator type and "collecting" list of function parameters, with corresponding values:
// function pointer, first argument to add, end of arguments list, actual parameters values
template <class TIt, class ...TParams>
static void f(void* pFunc, TIt curIt, TIt lastIt, TParams ...params)
{
if (curIt == lastIt)
{
// We reach the end of arguments list
// Converting void* to function pointer with required arity.
void (*fun)(TParams...) = reinterpret_cast<void(*)(TParams...)>(pFunc);
// Finally calling the function
fun(params...);
}
else
{
// Extracting next argument
TIt nextIt = curIt + 1;
// recursively calling ourselves, previously adding extracted parameter to variadic parameters list
F<nInst - 1>::f(pFunc, nextIt, lastIt, params..., *curIt);
}
}
};
// This specialization is required for compiler to finish producing compile--time instantiations of F with, which
// would happen otherwise because of the line above: F<nInst - 1>::f(pFunc, nextIt, lastIt, params..., *curIt);
template<>
struct F<0>
{
template <class TIt, class ...TParams>
static void f(void* pFunc, TIt curIt, TIt lastIt, TParams ...params)
{
// In case designed number of parameters is not enough
//runtime_assert(!"Please, increase number of instantiations of F");
}
};
// This is the one that well use, the interface function,
// which actully introduces the syntactic sugar we want:
// func(v.begin(), v.end());
template <class TIt>
void call_fun(void* pFunc, TIt curIt, TIt lastIt)
{
TIt nextIt = curIt + 1;
// We default to max 50 arguments by now
F<50>::f(pFunc, nextIt, lastIt, *curIt);
}
int main(int argc, char** argv)
{
// Container with parameters
std::vector<const char*> pCharVec(2);
pCharVec[0] = "There we go, par = %s!";
pCharVec[1] = "'I'm a parameter!'";
// Function pointer, should handle ok the number of paramters in container
// The actual signature isn't required here! It'll be "composed" based on the
// number of paratmers in container above.
// We explicitly show this by casting "something" (printf) to void*.
void *pFunc = reinterpret_cast<void*>(&printf);
call_fun(pFunc, pCharVec.begin(), pCharVec.end());
return (EXIT_SUCCESS);
}
So what do we do?
Using compile-time recursion we create 50 possible instantiations of F (and consequently F::f), that implement function calling with 1, 2 … 50 arguments. Then, using runtime recursion we extract arguments from vector, one by one, and using variadic templates transforming arguments vector to real arguments list. At the same time we chose the right arity to use with the function pointer. As soon as we reach last argument we simply cast function pointer to function accepting the number of arguments deduced from arguments vector size, with arguments types deduced from vector elements type and, finally, we call that function using the list of arguments that we prepared using variadic templates. Pretty simple and works.
C++0x is sweeeeeet!
PS We can somehow more generalize it by passing function return value type as a template argument as well.
PPS This technique can be used with heterogeneous arguments containers as well. Of course, function pointers have to match types of arguments from container, and their quantity.
PPPS This technique can be used to create dynamic–languages like behavior: by using single function with single argument …, and by for example preceeding each parameter in the list with object representing its type, we can create a function that can be called with apriori unknown number of arguments and their types. And arguments for the function can created / decided in run–time!. This almost implements function call reflection in C++, without any additional runtime support required. Isn’t that even more sweeeeeeeet?
))




All is much easier
#include
#include
#include
#include
void f1() {
std::cout << "f1()" << std::endl;
}
int f2() {
std::cout << "f2()" << std::endl;
return 3;
}
int f3(int v) {
std::cout << "f3()" << std::endl;
return f2()+v;
}
int main() {
/** without args */
boost::fusion::invoke(&f1, boost::fusion::make_vector());
/** without args */
boost::fusion::invoke(&f2, boost::fusion::make_vector());
/** with args */
int r = boost::fusion::invoke(&f3, boost::fusion::make_vector(3));
std::cout << r << std::endl;
}
http://liveworkspace.org/code/da4fb745ee9f2509eb5af862408e7648
Heh, that requires boost::fusion, while the original approach is in naked C++0x, showing the mechanics of the process!
Also, this approach doesn’t work for functions casted to void* pointers, so the following snippet doesn’t even compile:
void *pFun = reinterpret_cast
boost::fusion::invoke(pFun, boost::fusion::make_vector());
PS Anyway, surely the task should have many possible solutions, including elegant and simple ones, like the one you provided, even if they don’t match the task precisely
So thanks for that!
Oh? sorry. Delete this post. I wrote it in Russian