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:
在c中,请考虑这种情况。我有一个函数指针数组,我想调用它们中的每一个。我还有一个整数数组,告诉我每个参数有多少参数。我第三个有一个我想用它们调用的参数数组。以下程序是使用此程序的程序示例:
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?
上述程序有效,输出:131 27 9 0这是意图输出。问题是我需要在switch语句中为每个我想支持的参数提供一个案例。所以我的问题是:是否有一种更简单的方法可以做到这一点,并不是那么难看,并且可以使用任意数量的参数?
2 个解决方案
#1
3
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.
如果可能的话,为每个参数数量而不是单独的函数,编写一个带有一个int数组而不是一个count的函数,或者甚至考虑使用
Otherwise, you're into non-portable implementation-specified behaviours not guaranteed by the language standard itself....
否则,您将进入非便携式实现指定的行为,这些行为不受语言标准本身的保证....
With some calling conventions (e.g. 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.
使用一些调用约定(例如,在这里读取x86示例,可以使用额外的参数调用该函数,并且它们将在寄存器或堆栈中被忽略,而感兴趣的是正确使用的,然后被丢弃,因为原始堆栈指针是在其他体系结构中,函数返回时调整堆栈指针的数量与函数参数的数量有关,因此上述方法将崩溃。如果您想要阅读编译器/系统的约定并且有非 - 便携式解决方案,这是一个选择。
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 *, but it might take some digging to find one. Still, you'd want one corresponding to the calling convention you're using.
否则,再次根据您的调用约定,您可以在调用函数之前使用汇编语言在堆栈上推送一些参数。我已经看到代码在*上执行此操作的问题,但可能需要一些挖掘来找到一个。不过,你想要一个与你正在使用的调用约定相对应的一个。
#2
2
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.
当然,您可以重写函数以获取包含可变长度参数列表的结构,如TonyD建议的那样。您也可以为每个带有固定参数列表的函数编写thunk,但这与切换表一样多。
#1
3
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.
如果可能的话,为每个参数数量而不是单独的函数,编写一个带有一个int数组而不是一个count的函数,或者甚至考虑使用
Otherwise, you're into non-portable implementation-specified behaviours not guaranteed by the language standard itself....
否则,您将进入非便携式实现指定的行为,这些行为不受语言标准本身的保证....
With some calling conventions (e.g. 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.
使用一些调用约定(例如,在这里读取x86示例,可以使用额外的参数调用该函数,并且它们将在寄存器或堆栈中被忽略,而感兴趣的是正确使用的,然后被丢弃,因为原始堆栈指针是在其他体系结构中,函数返回时调整堆栈指针的数量与函数参数的数量有关,因此上述方法将崩溃。如果您想要阅读编译器/系统的约定并且有非 - 便携式解决方案,这是一个选择。
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 *, but it might take some digging to find one. Still, you'd want one corresponding to the calling convention you're using.
否则,再次根据您的调用约定,您可以在调用函数之前使用汇编语言在堆栈上推送一些参数。我已经看到代码在*上执行此操作的问题,但可能需要一些挖掘来找到一个。不过,你想要一个与你正在使用的调用约定相对应的一个。
#2
2
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.
当然,您可以重写函数以获取包含可变长度参数列表的结构,如TonyD建议的那样。您也可以为每个带有固定参数列表的函数编写thunk,但这与切换表一样多。