动态地将参数传递给可变参数函数
我想知道是否有任何方法将参数动态地传递给可变参数函数。即如果我有一个函数动态地将参数传递给可变参数函数
int some_function (int a, int b, ...){/*blah*/}
,我接受了一堆数值从用户,我想这些值传递到函数的一些方法:
some_function (a,b, val1,val2,...,valn)
我不想写所有这些功能的不同版本,但我怀疑没有其他选择?
尝试传递一个数组可能会很有趣,然后使用可变参数宏。根据堆栈对齐情况,它可能正常工作(tm)。
这可能不是最佳解决方案,我主要发布它,因为我发现这个想法很有趣。 试用后,这种方法在我的Linux x86上工作,但不在x86-64上 - 它可能可以改进。此方法将取决于堆栈对齐,结构对齐和可能更多。
void varprint(int count, ...)
{
va_list ap;
int32_t i;
va_start(ap, count);
while(count--) {
i = va_arg(ap, int32_t);
printf("Argument: %d\n", i);
}
va_end(ap);
}
struct intstack
{
int32_t pos[99];
};
int main(int argc, char** argv)
{
struct intstack *args = malloc(sizeof(struct intstack));
args->pos[0] = 1;
args->pos[1] = 2;
args->pos[2] = 3;
args->pos[3] = 4;
args->pos[4] = 5;
varprint(5, *args);
return 0;
}
变量函数使用调用约定,其中调用者负责从栈中弹出函数参数,所以是的,可以动态执行此操作。它在C中没有标准化,通常需要一些程序集手动推送所需的参数,并正确调用可变参数函数。
cdecl
调用约定要求参数按正确的顺序推送,并且在调用之后,在调用之前作为参数推送的字节被弹出。通过这种方式,被调用的函数可以接收任意数量的参数,因为调用者将处理将堆栈指针恢复到它的预调用状态。 ...
之前的参数占用的空间是推送的字节数的安全下限。额外的可变参数在运行时被解释。
FFCALL是一个库,它提供了将参数动态传递给可变参数函数的包装器。您感兴趣的功能组是avcall。这里有一个例子给你打电话了以上的功能:
#include <avcall.h>
av_alist argList;
int retVal;
av_start_int(argList, some_function, retval);
av_int(argList, a);
av_int(argList, b);
av_type(argList, val1);
...
av_type(argList, valn);
av_call(argList);
您也可能会发现this link讨论用C产生绕可变参数函数的包装,对感兴趣的理由,为什么这不是标准C.
一个标准的方法是让每个可变参数函数伴随着一个va_list
评论对象(如在printf和vprintf中)。该可变参数版本只是将...
转换为va_list
(使用来自stdarg.h
的宏),并调用其实际工作的va_list-sister。
谢谢,我会研究这种方法。 – tommobh 2009-11-12 12:06:26
@atzz虽然同样的问题。我仍然需要动态地将参数添加到该函数,无论它是否调用其va_list-姐妹。你的意思是我将这些值转换为va_list,然后使用va_list-taking姐妹函数来代替? – tommobh 2009-11-12 12:54:45
@tommobh - 我想我错误地解释了你的问题。通过阅读评论,我认为你有一种获得不同类型值的方法(例如,来自gui),并且需要将它们传递给一个函数;这是对的吗?在这种情况下,va_list不会有太大的帮助。如果你不想依赖黑客,你将不得不改变功能... – atzz 2009-11-12 16:10:37
取决于你传递的是什么,它可能是你在此之后的一个有区别的联盟(正如评论中暗示的那样)。这将避免需要可变参数函数或void*
的数组,并回答“问题如何some_function知道你实际上通过它”。你可能有代码是这样的:
enum thing_code { INTEGER, DOUBLE, LONG };
struct thing
{
enum thing_code code;
union
{
int a;
double b;
long c;
};
};
void some_function(size_t n_things, struct thing *things)
{
/* ... for each thing ... */
switch(things[i].code)
{
case INTEGER:
/* ... */
}
}
你可以借此更进一步,通过与一个或多个指针替换code
该做些什么,每个thing
有用的功能,避免了开关。例如,如果你想要做的是简单地打印出每一件事情,你可以有这样的:
struct thing
{
void (*print)(struct thing*);
union
{
...
};
}
void some_function(size_t n_things, struct thing *things)
{
/* .. for each thing .. */
things[i]->print(things[i]);
/* ... */
}
@Ned:我正在使用的可变参数函数是在一个库中,我最好不喜欢改变(即我不想要改变'some_function()')。用户可以输入他们喜欢的任何类型的数量(在合理范围内),因此定义严格的联合似乎不会起作用。一系列void *或Anacrolix的建议似乎是我最好的选择。无论是我还是我写一些内联汇编:S。 – tommobh 2009-11-12 13:52:15
但是,如果你不能改变some_function,你怎么能通过它一个void *数组?用户在这里,我们是在讨论一个使用你的代码的开发者,或者是该程序的最终用户? – Ned 2009-11-12 13:56:34
@Ned:User =最终用户。是的,我明白你的意思了。我不想改变函数,因为函数本身调用另一个可变参数函数,我也必须改变它。它只会雪球。我感觉我回到了我开始的地方。 – tommobh 2009-11-12 14:14:16
如果所有值均为同一类型(如你的问题似乎暗示 - 纠正我,如果我错了)我建议不要使用可变参数函数,而是传递一个数组。看到这里的一些宏魔法漂亮起来,当你想传递固定数量的参数:http://stackoverflow.com/questions/1375474/variable-arity-in-c/1375636#1375636 – Christoph 2009-11-12 11:59:38
不,他们是不同的类型。 – tommobh 2009-11-12 12:01:32
@tommobh:如果你将值包装在union中,或者将'void *'数组传递给值而不是值本身,但数组方法仍然可以工作 – Christoph 2009-11-12 12:04:10