Performance wise (and ease of use) you will probably be best off with cdecl
- everything goes onto the stack. The C standard allows you to specify function prototypes with arbitrary arguments
typedef void (__cdecl * function_with_any_parameters)();
You will have to make sure to define all functions that you wish to invoke as:
void __cdecl f(type1 arg1, type2 arg2, type3 arg3); // any amount of arguments
And just invoke them with the right amount of arguments:
f(arg1, arg2, arg3, arg4);
If you wish to go through a single pointer then you do have extra overhead: the one pointer. The easiest way would be to define all functions as accepting a pointer to an anonymous struct:
void f(struct {type1 a; type2 b;} * args);
Then you can invoke the function with a pointer to the appropriate struct to avoid any misalignments.
struct {type1 a; type2 b;} args = {arg1, arg2};
f(&args);
You are effectively implementing cdecl
on your own.