我有一个关于c函数指针并将参数传递给它们的相当复杂的问题。
我在查找表中有一个函数指针和几个函数地址。我通过串行接口获取所有数据。首先是必须调用的函数的数量。我在表中查找并将引用传递给我的函数指针。
之后,我收到几对4字节值作为参数的数据。问题是,我必须使用相同的返回类型但不同的参数量调用不同的函数。
有没有办法将动态数据传递给函数调用。也许通过手动将它们推到堆栈上?无法找到解决方案。
有人有任何想法或暗示解决这个问题吗?
答案 0 :(得分:1)
有时,以下使用联合的方法很有用:
union foo {
struct {
int arg1;
} f1_args;
struct {
int arg1, arg2;
} f2_args;
};
int f1(union foo*);
int f2(union foo*);
int (*table[])(union foo*) = {f1, f2};
//...
union foo data;
//...
int answer = table[1](&data); // calls f2, which uses arg1 and arg2
而且,如果您愿意,f1
和f2
可以是“真实”函数的简单包装,如:
int f1(union foo *u) { return f1_real(u->f1_args.arg1); }
int f2(union foo *u) { return f2_real(u->f2_args.arg1, u->f2_args.arg2); }
这非常灵活。但是如果你的参数总是只有4字节的整数,那么你就可以摆脱union
而只使用数组。改写后,上面变为:
int f1(uint32_t *a) { return f1_real(a[0]); } // wrapper
int f2(uint32_t *a) { return f2_real(a[0], a[1]); } // wrapper
int (*table[])(uint32_t *) = {f1, f2}; // lookup table
//...
uint32_t data[99]; // data from e.g. serial port
//...
int answer = table[1](data); // calls f2, which uses two args
答案 1 :(得分:1)
我不相信这是一个简单的方法来解决这个问题,因为参数传递是ABI(应用程序二进制接口)特定的。如果你的平台是固定的,你不介意编写不可移植的代码,那么你可以用ABI特定的方式编写你的代码,但我不会建议。
我在我写的一个小模拟器中解决了这个问题,对我来说这很简单,因为命令的数量永远不会超过4,我基本上使用了函数指针的联合,它具有我想要的参数数量和一个switch语句来选择正确的。
typedef struct
{
union
__attribute__((__packed__))
{
void *func;
void (*func_a0)(void);
uint32_t (*func_a0r)(void);
void (*func_a1)(uint32_t);
uint32_t (*func_a1r)(uint32_t);
void (*func_a2)(uint32_t, uint32_t);
uint32_t (*func_a2r)(uint32_t, uint32_t);
void (*func_a3)(uint32_t, uint32_t, uint32_t);
uint32_t (*func_a3r)(uint32_t, uint32_t, uint32_t);
void (*func_a4)(uint32_t, uint32_t, uint32_t, uint32_t);
uint32_t (*func_a4r)(uint32_t, uint32_t, uint32_t, uint32_t);
};
unsigned args;
bool ret;
const char* name;
} jump_entry_t;
bool jump_table_exec(
jump_table_t* table, void* addr,
uint32_t* args, uint32_t* ret)
{
#ifdef JUMP_TABLE_DEBUG
if (!table)
return false;
#endif
if ((uintptr_t)addr < (uintptr_t)table->base)
return false;
unsigned i = ((uintptr_t)addr - (uintptr_t)table->base);
if ((i & 4) || (i >= table->size))
return false;
jump_entry_t j = table->entry[i >> 3];
if (!j.func)
return false;
if (j.args && !args)
return false;
if (j.ret)
{
if (!ret) return false;
switch (j.args)
{
case 0:
*ret = j.func_a0r();
break;
case 1:
*ret = j.func_a1r(args[0]);
break;
case 2:
*ret = j.func_a2r(args[0], args[1]);
break;
case 3:
*ret = j.func_a3r(args[0], args[1], args[2]);
break;
case 4:
*ret = j.func_a4r(args[0], args[1], args[2], args[3]);
break;
default:
return false;
}
}
else
{
switch (j.args)
{
case 0:
j.func_a0();
break;
case 1:
j.func_a1(args[0]);
break;
case 2:
j.func_a2(args[0], args[1]);
break;
case 3:
j.func_a3(args[0], args[1], args[2]);
break;
case 4:
j.func_a4(args[0], args[1], args[2], args[3]);
break;
default:
return false;
}
}
#ifdef JUMP_TABLE_DEBUG
if (j.name)
{
fprintf(stderr, "Info: Jump table %s(", j.name);
if (j.args >= 1) fprintf(stderr, "%" PRIu32, args[0]);
if (j.args >= 2) fprintf(stderr, ", %" PRIu32, args[1]);
if (j.args >= 3) fprintf(stderr, ", %" PRIu32, args[2]);
if (j.args >= 4) fprintf(stderr, ", %" PRIu32, args[3]);
fprintf(stderr, ")");
if (j.ret) fprintf(stderr, " returned %" PRIu32, *ret);
fprintf(stderr, ".\n");
}
#endif
return true;
}
答案 2 :(得分:0)
由于函数可以区分它们的参数,因此您始终可以为它们指定...
类型。例如:
int f(...)
{
/* extract one int */
}
int g(...)
{
/* extract two floats */
}
...
int (*fp)(...);
if (type_one)
fp(10);
else if (type_two)
fp(1.3, 4.3);
或者更好的是使用union
。但是,在您的特定情况下,由于参数本身是&#34;对4个字节&#34;,您始终可以使用数组:
struct arg
{
uint32_t pair_of_4_bytes[2];
};
int f(struct arg *args, size_t count)
{
}
int g(struct arg *args, size_t count)
{
}
...
int (*fp)(struct arg *args, size_t count);
struct arg args[MAX];
size_t count = 0;
/* get args from serial and put in args/count */
fp(args, count);
答案 3 :(得分:0)
我认为&#34; Variadic功能&#34;可以解决您的问题需求。在这里查看一个简单的例子:
http://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html#Variadic-Example