如何在c ++中构建va_list

时间:2018-01-14 16:20:27

标签: c++ printf variadic-functions

我有一个带有不同类型和值的参数struct(这对我的问题并不重要):

enum class ArgType {...}

struct Argument
{
    ArgType type;
    void* value;
}

我有一个自定义动态List实现,用于创建Argument的列表。然后,我有以下功能可以接受此列表,我的目的是遍历此列表并构建va_list

void foo(List<Argument>* args)
{
    for (int i = 0; i < args->length; i++)
    {
        Argument* arg = args->get(i);
        char* value = (char*) value;
        // build a `va_list` from value
    }
}

然后我有一个名为bar的函数,它将使用foo函数构建一个va_list,并将打印一个格式化的字符串。

void bar(char* format_str, List<Argument>* args)
{
    // get va_list from: foo(args);
    vfprintf(stdout, format_str, argptr);
}

我如何构建va_list,或制作类似于va_list的内容,我仍然可以传递给vfprintf

提前致谢。

2 个答案:

答案 0 :(得分:1)

va_list只是一个指向已被推入调用堆栈以与函数CALL指令一起使用的参数的指针。您的链表存在于内存的其他位置。执行所要求的唯一方法是编写枚举列表的汇编代码,并手动将值推送到调用堆栈中。不要这样做,它依赖于编译器,甚至依赖于平台。

你甚至不应该在C ++中使用printf - 样式的函数开始。改为使用I / O流,例如std::coutstd::ostringstream等。为operator<<写一个Argument,然后使用普通循环枚举列表并将其值写入您想要的std::ostream对象。

对于bar()函数,您只需将format_str拆分为一个令牌列表,然后将每个令牌写入std::ostream,替换为Argument值&#34;特殊&#34;根据需要提供令牌。

或者,您可以完全摆脱bar()并让调用者使用它想要的任何Arguments直接格式化其std::ostream列表。

答案 1 :(得分:1)

在C ++ 11或更高版本中,您应该尽可能使用variadic template。阅读parameter pack s。考虑使用std::stringstream - s,std::ostringstream - s等。

如果你真的想在运行时建立一个可变参数调用(但这通常是一个坏主意),你可以考虑使用libffi(它知道你的特定ABI&amp; {{3} },并且有一些汇编程序。)

今天,va_list需要编译器支持(通过__builtin_va_start等...)并且您的ABI知道它(因为许多调用约定涉及calling conventions)。请参阅processor registers(并注意其va_copy)和stdarg(3)