开发者

emulating partial function application in c

I'm writing a C program that would, if had been a part of the language, benefit from, partial function application. I need to supply a function pointer with a fixed signature but sometime I need to call already written function that needs arguments.

I can write a lot of small functions, calling each other as:

// example, not actual code
void (*atJoystickLeft)(void); // fixed signature

.开发者_StackOverflow. code ..
atJoystickLeft = &func1;
.. code ..

void func1(void) { func2(10, "a string"); } /

I vould like instead:

atJoystickLeft = &func2(10, "a string");

so I can dispose of the function func1 that is only to set parameter for the call to func2.

That would make the code at least, 20% - 30% smaller. My simple question is: Do someone have a trick for emulating partial function application in C? (C99)


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.


  1. "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.


I don't know of any straight foward way do do what you want, since C doesn't support closures or any other way to pass "hidden parameters" to your functions (without using globals or static variables, strtok style)

You seem to come from a FP background. In C everything has to be done by hand and I'd risk saying that in this case it might be better to try to model things in OO style instead: turn your functions into "methods" by making them receive an extra argument, a pointer to a struct of "inner attributes", the this or self. (If you happen to know Python this should be very clear :) )

In this case, for example, func1 might turn into something like this:

typedef struct {
     int code;
     char * name;
} Joystick;

Joystick* makeJoystick(int code, char* name){
    //The constructor
    Joystick* j = malloc(sizeof Joystick);
    j->code = code;
    j->name = name;
    return j;
}

void freeJoystick(Joystick* self){
    //Ever noticed how most languages with closures/objects
    // have garbage collection? :)
}

void func1(Joystick* self){
    func2(self->code, self->name);
}

int main(){
     Joystick* joyleft  = make_Joystick(10, "a string");
     Joystick* joyright = make_Joystick(17, "b string");

     func1(joyleft);
     func1(joyright);

     freeJoystick(joyleft);
     freeJoystick(joyright);

     return 0;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜