简体   繁体   中英

How do I call a function pointer where number of arguments if only known at runtime

In c, consider this scenario. I have an array of function pointers and I want to call each of them. I also have an array of integers telling me how many arguments each one takes. I thirdly have an array of the arguments that I want to call them with. The following program is an example of a program that uses this:

int foo(int a, int b, int c){
    return a+b+c;
}

int bar(int a, int b){
    return a+b;
}

int baz(int a){
    return a;
}

int qux(){
    return 0;
}


int main(){
    void *funcArray[4] = {foo, bar, baz, qux}; //an array of function pointers, all of which return ints but have different numbers of arguments
    int argArray[3+2+1+0] = {100,30,1,  20,7,  9}; //these are the arguments to the functions to be executed
    int numArgsArray[4] = {3,2,1,0}; //these are the numbers of arguments that each function takes in the funcArray array
    int nextArg = 0; //used to keep track of which argument goes to which function

    for (int i = 0; i<4; i++){
        int result;
        switch(numArgsArray[i]){
        case 0://if the function takes no args, just call it
            result = ((int(*)())funcArray[i])();
            break;
        case 1://if the function takes one arg, pass it the argument when calling it
            result = ((int(*)(int))funcArray[i])(argArray[nextArg]);
            nextArg += 1;
            break;
        case 2://if the function takes two arguments, pass it both when calling
            result = ((int(*)(int, int))funcArray[i])(argArray[nextArg], argArray[nextArg+1]);
            nextArg += 2;
            break;
        case 3://if the function takes three args, pass it all three when calling
            result = ((int(*)(int, int, int))funcArray[i])(argArray[nextArg], argArray[nextArg+1], argArray[nextArg+2]);
            nextArg += 3;
            break;
        }
        printf("%d\n", result);
    }

    return 0;
}

The above program works, and outputs: 131 27 9 0 This is the intented output. The problem is that I need to have a case in the switch statement for each number of arguments that I want to support. So my question is: is there an easier way to do this that isn't so ugly and will work with any number of arguments?

If possible, instead of separate functions for each number of parameters, write a function that takes an array of int s instead with a count, or even consider using <stdarg.h> but you'll still need some kind of sentinel or count.

Otherwise, you're into non-portable implementation-specified behaviours not guaranteed by the language standard itself....

With some calling conventions (eg read here for x86 examples , you can call the function with extra arguments and they'll sit ignored in registers or on the stack while the ones of interest are used correctly, then be discarded as the original stack pointer is restored. On other architectures, the amount by which the stack pointer is adjusted when the function returns relates to the number of function parameters, so the above approach will crash. If you want to read up on your compiler/system's conventions and have a non-portable solution, that's an option.

Otherwise, again depending on your calling conventions, you may be able to use assembly language to push some number of arguments on the stack before calling the function. I've seen questions with code doing that on stackoverflow, but it might take some digging to find one. Still, you'd want one corresponding to the calling convention you're using.

This can be improved slightly:

typedef int (*FuncP)();    // function returning int and taking unspecified arguments
FuncP func_array[4] = { foo, bar, baz, qux };

// ...

// cast unnecessary
case 1:
    result = funcArray[i](arg1);

If you call a function with the wrong number or type of arguments then it causes undefined behaviour, but so long as you keep track of your argument counts specifically like you actually do in your code, then it's well-defined.

There's no simpler way that retains portability and also retains the functions as they are. On some systems you'd get away with passing extra dummy arguments.

Of course you could rewrite the functions to take a structure which contains a variable-length argument list as TonyD suggests. You could also write thunks for each function that take a fixed argument list, but that would be just as much work as your switch table.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM