The short answer is that no, C doesn't support it.
You could hack together a variadic front-end that took the address of a function and created a stack frame to invoke that function with those arguments, but it wouldn't be portable1. If you wanted to make it portable, and were free to modify the other functions you were going to invoke via this front end (into a form that's not really suitable for direct invocation) you could rewrite all of them to receive a va_alist
as their sole parameter, and retrieve the correct number/type of parameters using va_arg
:
// pointer to function taking a va_alist and return an int:
typedef int (*func)(va_alist);
void invoke(func, ...) {
va_alist args;
va_start(args, func);
func(args);
va_end(args);
}
Edit: Sorry, as @missingno pointed out, I didn't make this work quite the way it was intended. This should really be two functions: one that takes the inputs and wraps them into a structure, and the other that takes the structure and invokes the intended function.
struct saved_invocation {
func f;
argument_list args;
};
saved_invocation save(func f, ...) {
saved_invocation s;
va_alist args;
s.f = f;
va_start(args, f);
va_make_copy(s.args, args);
va_end(args);
}
int invoke(saved_invocation const *s) {
s->f(s->args);
}
For va_make_copy
, you'd get into more non-standard stuff. It would not be the same as va_copy
-- va_copy
normally just stores a pointer to the beginning of the arguments, which only remains valid until the current function returns. For va_make_copy
, you'd have to store all the actual arguments so you could retrieve them later. Assuming you used the argument
structure I outline below, you'd walk through the arguments using va_arg
, and use malloc
(or whatever) to allocate a node for each argument, and create some sort of dynamic data structure (e.g., linked list, dynamic array) to hold the arguments until you were ready to use them.
You'd also have to add some code to deal with freeing that memory once you were finished with a particular bound function. It would also actually change your function signature from taking a va_list directly, to taking whatever kind of data structure you devised to hold your list of arguments.
[end edit]
This would mean the signature for every other function you were going to invoke would need to be:
int function(va_alist args);
...and then each of these functions would have to retrieve its arguments via va_arg
, so (for example) a function that was going to take two ints as its arguments, and return their sum would look something like this:
int adder(va_alist args) {
int arg1 = va_arg(args, int);
int arg2 = va_arg(args, int);
return arg1 + arg2;
}
This has two obvious problems: first, even though we no longer need a separate wrapper for each function, we still end up adding extra code to every function to let it be invoked via the single wrapper. In terms of code size, it's unlikely to do much better than break even, and might easily be a net loss.
Much worse, however, since we're now retrieving all the arguments to all the functions as a variable argument list, we no longer get any type checking on the arguments. If we wanted to badly enough, it would (of course) be possible to add a small wrapper type and code to handle that as well:
struct argument {
enum type {CHAR, SHORT, INT, LONG, UCHAR, USHORT, UINT, ULONG, /* ... */ };
union data {
char char_data;
short short_data;
int int_data;
long long_data;
/* ... */
}
}
Then, of course, you'd write still more code to check that the enumeration for each argument indicated that it was the expected type, and retrieve the correct data from the union if it was. This, however, would add some serious ugliness to invoking the functions -- instead of:
invoke(func, arg1, arg2);
...you'd get something like:
invoke(func, make_int_arg(arg1), make_long_arg(arg2));
Obviously, this can be done. Unfortunately, it still does no good -- the original goal of reducing code has almost certainly been completely lost. This does eliminated the wrapper function -- but instead of a simple function with a simple wrapper and simple invocation, we end up with a complex function and complex invocation, and a small mountain of extra code to convert a value to our special argument type, and another to retrieve a value from an argument.
While there are cases that can justify code like this (e.g., writing an interpreter for something like Lisp), I think in this case it works out to a net loss. Even if we ignore the extra code to add/use type checking, the code in each function to retrieve arguments instead of receiving them directly works out to more than the wrapper we're trying to replace.
- "Wouldn't be portable" is almost an understatement -- you really can't do this in C at all -- you just about have to use assembly language to even get started.
func2
? – Baelbind
ing, though. www2.research.att.com/~bs/C++0xFAQ.html#std-function – Cassock