tags:

views:

106

answers:

1

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!

+2  A: 

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.

unwind