【问题标题】:Invoking a C callback function with supplied variadic arguments gives strange result使用提供的可变参数调用 C 回调函数会产生奇怪的结果
【发布时间】:2020-08-23 21:57:57
【问题描述】:

在下面的代码中 float 或 double 参数不起作用,值总是错误的。整数和字符串按预期工作。

除非有人能发现问题,否则我将放弃这个结构并尝试其他方法。

typedef void(*varfunc)(...);
typedef void(*invokefunc)(int, ...);

varfunc func_table[2];

void register_func(int slot, varfunc cb)
{
    func_table[slot] = cb;
}

void invoke_func(int slot, void* a1, void* a2, void* a3, void* a4)
{
    func_table[slot](a1, a2, a3, a4);
}

void func_1(float a, int b)
{
    printf("%f %d", a, b);
}

void func_2(const char* c)
{
    printf("%s", c);
}


int main()
{
    register_func(0, (varfunc)func_1);
    register_func(1, (varfunc)func_2);

    ((invokefunc)invoke_func)(0, 1.23f, 456);
    ((invokefunc)invoke_func)(1, "hello world");

    return 0;
}

【问题讨论】:

  • 对我来说,这看起来好像根本不应该工作。您必须使用来自<stdarg.h>va_... 宏访问可变参数。 (这可能就是你实现你的想法的方式:让varfuncva_list 作为参数。但我真的看不到这种设计的好处。可变参数函数意味着你失去了类型安全,如果你把插槽弄混了,func_2 很可能会崩溃。)
  • 这很有效,我在公共图书馆看到过类似的结构。不幸的是 va_list 需要传递许多参数,并用这个想法打破了整个观点。我对类型安全的看法是,类型安全在 C 中没有任何作用,这取决于程序员的类型安全和了解数据。
  • 好吧,如果我从varfunc 定义中删除...,它会按照您的描述工作。顺便说一句,您不必使用va_list 传递参数的数量。但是,... 之前必须至少有一个 names 参数,但这不适用于 va_list 参数。而且您在需要它的函数中确实有一个参数,即插槽 id。 Here's 怎么会这样。浮点数被正确打印。
  • 是的,你说得对,这可以工作。但在那种情况下,我需要某种包装器来解包正确的参数并调度回调。这与我现在所拥有的类似,除了我为每种类型的函数调用使用参数结构而不是 va_list。但我一定会尝试像你建议的那样。谢谢。
  • 您不需要包装器,但您必须根据va_list 实现回调,您必须从中“解包”值。 (这有点像在 Perl 5 中 shifting 来自 @_ 的子参数。)无论如何,祝你好运。

标签: c callback arguments variadic


【解决方案1】:

在您提供的代码中,您将 invoke_func 的参数指定为 void 指针,其大小可能与浮点数或整数不同,因此,当您将它们传递给 func_1 时会导致未定义的行为。我已将func_1 修改如下:

void func_1(float* a, int* b)
{
    printf("%f %d", *a, *b);
}

现在你可以调用它了:

float a = 1.23f;
int b = 456;

((invokefunc)invoke_func)(0, &a, &b);

这在我的机器上成功运行。

【讨论】:

  • 是的,谢谢。但这使得进行函数调用变得非常麻烦,一点也不好玩。我在我目前正在研究的信号/插槽系统中做了一些不同的方法,我基本上使用调度函数并通过知道被调用的信号类型将一个 void 函数指针转换为正确的函数。但它会分配修补和指针转换。
猜你喜欢
  • 2011-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2012-09-19
  • 1970-01-01
相关资源
最近更新 更多