使用 va_list 作为参数从一个地方调用任何函数

Call any functions from one place using va_list as arguments

我有主要更新,我在通话前进行访问控制。我想在我的代码中使用一个地方来调用任何紧急函数。

我有一个结构动作:

{
    FUNC_PROTOTYPE pfnAction;
    unsigned int   argsnum;
    va_list        argsval;
};

当我需要调用 func 时,我会这样做:
1 将函数和参数放入队列; 2 下次更新时弹出它们

{
    ACTION action;

    while(!Queue_isEmpty())
    {   // check and pop urgent functions
        if(Queue_Pop(&action))
        {
            action.pfnAction(action.argsnum, action.args);
            va_end(action.args);
        }
    }
}

例如,我尝试调用

void func(unsigned int argsnum, va_list args)

但是我的 args 里面的 func 已经损坏了。

我想,当我从队列中弹出时遇到的问题:

Queue_Pop(P_ACTION p_res)
{
    if(!Queue_isEmpty())
    {
       p_res->pfnAction = header->pfnAction;
       p_res->argsnum = header->argsnum;

       if(0 < p_res->argsnum)
       {
           p_res->argsval = header->argsval;
           va_end(header->argsval);
       }
       ...
    }
}

但是action.args没问题

您不能将可变参数列表作为对象传递,即使使用 va_list 也是如此。那是行不通的/是未定义的行为。

如果您使用 C++ 进行编程,则 (1) 不要使用 C 标记您的问题,并且 (2) 存储带有绑定参数的 std::function

正如@mSalters 所说,您不能传递 va_list - 它仅在具有变量 args 的函数的堆栈帧中有效。

在严格传递堆栈的体系结构上,您可能可以通过存储 va_list 指向的初始地址和后面的地址来破解它遍历所有预期的参数,然后将两个地址之间的所有内存复制到分配的缓冲区中。这必须在您排队之前完成。在队列弹出端,您可以通过使 va_list 指向您的副本来伪造 va_arg。

除了实验之外,我不建议出于任何目的实际这样做。

在许多现代架构中,这行不通,因为一些变量也在寄存器中传递。有关 va_arg 的混乱程度的详细信息,请参阅 this article