Reputation: 12618
I have this program that I want other processes to be able to call functions on (through unix sockets). The message protocol is very simple, the function name, a function signature, and a buffer (char *) that holds the parameters.
When a module in my program wants to allow a function to be accessible, it registers the name and signature with the library. The problem I'm facing is with physically calling the function once the request comes in. I have looked at RPC and java RMI-like libraries, but those require that I generate stubs to wrap calls. The system I am working on is very dynamic and I also have to interface with other peoples code that I can't modify.
So basically, a function might look like:
int somefunc(int someparam, double another)
{
return 1234;
}
now I register with the library:
// func ptr name signature
REG_FUNCTION(somefunc, "somefunc", "i:id");
When the request comes in, I do some error checking, once valid I want to call the function. So I have the variables:
void * funcptr = (the requested function);
char * sig = (the function signature);
char * params = (a buffer of function parameters);
//note that the params buffer can hold data types of arbitrary lengths
How can I call the function with the parameters in C?
Thanks!
Upvotes: 2
Views: 525
Reputation:
Check out libffi. This library allows to call function with a set of parameters specified at runtime.
Upvotes: 0
Reputation: 400009
I don't think this is completely solvable in general, using only C. You don't know the calling convention used by the target function, for instance. There is a risk that you end up "cheating" the compiler, or at least having to second-guess it. What if the compiler decided to build the registered function using arguments passed in registers, perhaps due to some optimization setting (or what if it was built with a different compiler?).
There's also no way in general to express in C that you want to call a function with a given set of arguments, and that the values for the arguments need to be unpacked from a buffer of random bytes.
You could do something horrible like this:
enum { VOID_INT, VOID_INT_DOUBLE, VOID_DOUBLE_INT, ... } Signature;
void do_call(const void *userfunction, const void *args, Signature sig)
{
switch(signature)
{
case VOID_INT:
{
int x = *(int *) args;
void (*f)(int) = userfunction;
f(x);
break;
}
case VOID_INT_DOUBLE:
...
}
}
But it's quite clear that this doesn't live on the same continent as scalability. You could of course auto-generate this code, for some reasonable set of return types and argument types. You'd still be a bit out in the cold for casting function pointers to/from void *
, but that might be acceptable. You'd still always run the risk of someone handing you a signature that you don't have pre-generated code for, though.
Upvotes: 2