views:

143

answers:

3

Given that I have a pointer to a function (provided by dlsym() for example) and a linked list of typed arguments, how can I construct a C function call with those arguments?

Example:

struct param {
   enum type { INT32, INT64, STRING, BOOL } type;
   union { int i32; long long i64; char *str; bool b; } value;
   struct param *next;
 };
 int call_this(int (*function)(), struct param *args)
 { 
     int result;
     /* magic here  that calls function(), which has a prototype of
     f(int, long long, char *, bool); , when args consist of a linked list of
     INT32, INT64, STRING, BOOL types. */
     return result;
 }

The OS is Linux. I would like the solution to be portable across MIPS, PPC and x86 (all 32 bits) architecture, using GCC as the compiler.

Thanks!

A: 

Unpack your arguments into variables, and then call the function.

int a[10];
int n = 0;
while (args != NULL) {
    if (args->type == INT64) {
#if __BIG_ENDIAN__
        a[n++] = args->value.i64 >> 32;
        a[n++] = args->value.i64 & 0xffffffff;
#else
        a[n++] = args->value.i64 & 0xffffffff;
        a[n++] = args->value.i64 >> 32;
#endif
    } else { /* all other types are pushed as 32 bits parameters */
        a[n++] = args->value.i32;
    }
}
result = (*function)(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);

You will also have to check not to overflow the a array.

Didier Trosset
That's a (tagged) `union`, not a `struct`.
KennyTM
This won't work since `function` doesn't have the correct type.
interjay
`function` is of type `int(*)()`, which in C is equivalent to `int(*)(...)`.
Didier Trosset
I also deliberately not answered on the argument unpacking, because the question was more oriented on the *magic* to call the function, rather than the arguments.
Didier Trosset
You're sort of right about that. But the types of `arg0`, `arg1`, etc. can't be known at compile-time so this still won't work.
interjay
You're right. I didn't thought about that. But then, what's the point of relying on run-time operations on arguments that are known at compile time (I suppose, as the function is loaded from a shared lib).
Didier Trosset
For example, it's useful if you're creating an interpreted scripting language and want it to support calling arbitrary C functions in a shared lib.
interjay
If the compiler issue warnings, I do not care much as this is controllable with function __attribute__() and I have a hacker solution.. ;-)
0x6adb015
+6  A: 

You'll probably need to use libffi.

Tom Dalling
+2  A: 

Supporting arbitrary function signatures is impossible with standard C (at least I don't know of any way to do so). If you need this, I'd go with libffi as Tom suggested.

If there's only a limited number of signatures you want to support, you can examine the type fields and dispatch the call appropriately by casting the prototype-less function pointer to the correct prototype and supply the correct arguments yourself. Casting the function pointer is necessary to avoid default argument promotion, but even without the cast, you still would have to dispatch manually on the signatures to access the correct union members. The process can be automated by using the scripting language of your choice to generate the C code from a list of signatures.

Christoph