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

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

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

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

134646 次浏览

你应该创建这些函数的版本,它们接受一个 va _ list 并传递它们:

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

您不能直接执行; 您必须创建一个接受 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...
}

使用新的 C + + 0x 标准,您可以使用可变模板完成这项工作,甚至可以在不破坏任何内容的情况下将旧代码转换为新的模板语法。

基于你正在包装 vsprintf的评论,以及它被标记为 C + + ,我建议不要尝试这样做,而是改变你的界面,使用 C + + iostream 代替。相对于 print系列的功能,它们有许多优势,例如类型安全性和能够打印 printf无法处理的项目。现在的一些返工可以在将来节省大量的痛苦。

顺便说一下,许多 C 实现都有一个内部的 v? printf 变体,恕我直言,它应该是 C 标准的一部分。确切的细节有所不同,但典型的实现会接受一个包含字符输出函数指针和说明应该发生什么的信息的 struct。这允许 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);
}

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

我还想把 printf 包装起来,在这里找到了一个有用的答案:

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

我对性能一点也不感兴趣(我确信这段代码可以通过多种方式得到改进,请尽管这样做:) ,这只是为了普通的调试打印,所以我这样做了:

//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;
}

然后我可以像这样使用

Point2d p;


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

C + + ostream 在某些方面很漂亮,但实际上,如果你想用一些小字符串(如括号、冒号和逗号)来打印这样的内容,那么它就会变得很恐怖。

也许在这里扔一块石头在池塘里,但它似乎工作得很好与 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);
}

至少,它与 g++一起工作。

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

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

这是唯一的方法,也是最好的方法。

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;
}

它可能与这里描述的情况不完全一样,但是如果您要为一个字符串格式函数(例如 logger)定义一个包装器:

void logger(const char *name, const char *format, ...);
void wrapper(const char *format, ...);

当实现调用 loggerwrapper时,我们可以先用 Vasprintf创建一个字符串,然后将其传递给 logger

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>


static void wrapper(const char *format, ...)
{
char *string;
va_list args;
va_start(args, format);


// variadic printf with allocated string. must free()
vasprintf(&string, format, args);
logger("wrapper", "%s", string);


free(string);
va_end(args);
}

不是最干净的,但可以工作。当你必须避免使用 宏观函数时,试试这个。

使用 GNU C 扩展:

int FIRST_FUNC(...){
__builtin_return(
__builtin_apply(
(void(*)())SECOND_FUNC, __builtin_apply_args(), 100));
}

克隆也使用 _builtin_return返回值。