C++ 将可变参数传递给另一个接受可变参数列表的函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3530771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:02:33  来源:igfitidea点击:

Passing variable arguments to another function that accepts a variable argument list

c++variadic-functions

提问by Not Available

So I have 2 functions that both have similar arguments

所以我有两个函数,它们都有相似的参数

void example(int a, int b, ...);
void exampleB(int b, ...);

Now examplecalls exampleB, but how can I pass along the variables in the variable argument list without modifying exampleB(as this is already used elsewhere too).

现在example调用exampleB,但是如何在不修改的情况下传递变量参数列表中的变量exampleB(因为这也已在其他地方使用)。

回答by Jonathan Leffler

You can't do it directly; you have to create a function that takes a va_list:

你不能直接做;你必须创建一个函数,它需要一个va_list

#include <stdarg.h>

static void exampleV(int b, va_list args);

void exampleA(int a, int b, ...)    // Renamed for consistency
{
    va_list args;
    do_something(a);                // Use argument a somehow
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

void exampleB(int b, ...)
{
    va_list args;
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

static void exampleV(int b, va_list args)
{
    ...whatever you planned to have exampleB do...
    ...except it calls neither va_start nor va_end...
}

回答by Vincent Fourmond

Maybe throwin a rock in a pond here, but it seems to work pretty OK with C++11 variadic templates:

也许在这里的池塘里扔一块石头,但它似乎与 C++11 可变参数模板一起工作:

#include <stdio.h>

template<typename... Args> void test(const char * f, Args... args) {
  printf(f, args...);
}

int main()
{
  int a = 2;
  test("%s\n", "test");
  test("%s %d %d %p\n", "second test", 2, a, &a);
}

At the very least, it works with g++.

至少,它适用于g++.

回答by tenfour

you should create versions of these functions which take a va_list, and pass those. Look at vprintfas an example:

您应该创建这些函数的版本,这些版本采用 va_list,并传递它们。看vprintf一个例子:

int vprintf ( const char * format, va_list arg );

回答by Axel

I also wanted to wrap printf and found a helpful answer here:

我也想包装 printf 并在这里找到了一个有用的答案:

How to pass variable number of arguments to printf/sprintf

如何将可变数量的参数传递给 printf/sprintf

I was not at all interested in performance (I'm sure this piece of code can be improved in a number of ways, feel free to do so :) ), this is for general debugprinting only so I did this:

我对性能完全不感兴趣(我确信这段代码可以通过多种方式改进,请随意这样做:)),这仅用于一般调试打印,所以我这样做了:

//Helper function
std::string osprintf(const char *fmt, ...)
{
    va_list args;
    char buf[1000];
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args );
    va_end(args);
    return buf;
}

which I then can use like this

然后我可以像这样使用

Point2d p;

cout << osprintf("Point2d: (%3i, %3i)", p.x, p.y);
instead of for example:
cout << "Point2d: ( " << setw(3) << p.x << ", " << p.y << " )";

The c++ ostreams are beautiful in some aspects, but practically the become horrific if you want to print something like this with some small strings such as parenthesis, colons and commas inserted between the numbers.

c++ ostreams 在某些方面很漂亮,但如果你想打印这样的东西,在数字之间插入一些小字符串,如括号、冒号和逗号,实际上变得很糟糕。

回答by supercat

Incidentally, many C implementations have an internal v?printf variation which IMHO should have been part of the C standard. The exact details vary, but a typical implementation will accept a struct containing a character-output function pointer and information saying what's supposed to happen. This allows printf, sprintf, and fprintf to all use the same 'core' mechanism. For example, vsprintf might be something like:

顺便说一句,许多 C 实现都有一个内部 v?printf 变体,恕我直言,它应该是 C 标准的一部分。确切的细节有所不同,但典型的实现将接受一个包含字符输出函数指针和说明应该发生什么的信息的结构。这允许 printf、sprintf 和 fprintf 都使用相同的“核心”机制。例如,vsprintf 可能类似于:

void s_out(PRINTF_INFO *p_inf, char ch)
{
  (*(p_inf->destptr)++) = ch;
  p_inf->result++;
}

int vsprintf(char *dest, const char *fmt, va_list args)
{
  PRINTF_INFO p_inf;
  p_inf.destptr = dest;
  p_inf.result = 0;
  p_inf.func = s_out;
  core_printf(&p_inf,fmt,args);
}

The core_printf function then calls p_inf->func for each character to be output; the output function can then send the characters to the console, a file, a string, or something else. If one's implementation exposes the core_printf function (and whatever setup mechanism it uses) one can extend it with all sorts of variations.

core_printf 函数然后为每个要输出的字符调用 p_inf->func;然后输出函数可以将字符发送到控制台、文件、字符串或其他东西。如果一个人的实现公开了 core_printf 函数(以及它使用的任何设置机制),则可以使用各种变体对其进行扩展。

回答by Shai

A possible way is to use #define:

一种可能的方法是使用#define:

#define exampleB(int b, ...)  example(0, b, __VA_ARGS__)

回答by SSpoke

This is the only way to do it.. and the best way to do it too..

这是唯一的方法..也是最好的方法..

static BOOL(__cdecl *OriginalVarArgsFunction)(BYTE variable1, char* format, ...)(0x12345678); //TODO: change address lolz

BOOL __cdecl HookedVarArgsFunction(BYTE variable1, char* format, ...)
{
    BOOL res;

    va_list vl;
    va_start(vl, format);

    // Get variable arguments count from disasm. -2 because of existing 'format', 'variable1'
    uint32_t argCount = *((uint8_t*)_ReturnAddress() + 2) / sizeof(void*) - 2;
    printf("arg count = %d\n", argCount);

    // ((int( __cdecl* )(const char*, ...))&oldCode)(fmt, ...);
    __asm
    {
        mov eax, argCount
        test eax, eax
        je noLoop
        mov edx, vl
        loop1 :
        push dword ptr[edx + eax * 4 - 4]
        sub eax, 1
        jnz loop1
        noLoop :
        push format
        push variable1
        //lea eax, [oldCode] // oldCode - original function pointer
        mov eax, OriginalVarArgsFunction
        call eax
        mov res, eax
        mov eax, argCount
        lea eax, [eax * 4 + 8] //+8 because 2 parameters (format and variable1)
        add esp, eax
    }
    return res;
}

回答by Mark B

Based on the comment that you're wrapping vsprintf, and that this is tagged as C++ I'd suggest not trying to do this, but change up your interface to use C++ iostreams instead. They have advantages over the printline of functions, such as type safety and being able to print items that printfwouldn't be able to handle. Some rework now could save a significant amount of pain in the future.

根据您正在包装的评论vsprintf,并且这被标记为 C++,我建议不要尝试这样做,而是更改您的界面以使用 C++ iostreams。它们print在功能方面具有优势,例如类型安全和能够打印printf无法处理的项目。现在进行一些返工可以在未来节省大量的痛苦。

回答by David

Using the new C++0x standard, you may be able to get this done using variadic templates or even convert that old code to the new template syntax without breaking anything.

使用新的 C++0x 标准,您可以使用可变参数模板完成此操作,甚至可以将旧代码转换为新模板语法而不会破坏任何内容。