在C中,是否有可能转发可变函数的调用?如,

int my_printf(char *fmt, ...) {
    fprintf(stderr, "Calling printf with fmt %s", fmt);
    return SOMEHOW_INVOKE_LIBC_PRINTF;
}

在这种情况下,以上述方式转发调用显然不是严格必要的(因为您可以以其他方式记录调用,或使用vfprintf),但我正在处理的代码库要求包装器做一些实际工作,并且没有(也不可能添加)类似于vfprintf的帮助函数。

[更新:根据目前提供的答案,似乎有一些困惑。用另一种方式来表达这个问题:一般来说,你可以在不修改函数定义的情况下包装任意的变进函数吗?


当前回答

C99支持可变参数宏;取决于你的编译器,你可以声明一个宏来做你想做的事情:

#define my_printf(format, ...) \
    do { \
        fprintf(stderr, "Calling printf with fmt %s\n", format); \
        some_other_variadac_function(format, ##__VA_ARGS__); \
    } while(0)

不过,一般来说,最好的解决方案是使用您试图包装的函数的va_list形式(如果存在的话)。

其他回答

C99支持可变参数宏;取决于你的编译器,你可以声明一个宏来做你想做的事情:

#define my_printf(format, ...) \
    do { \
        fprintf(stderr, "Calling printf with fmt %s\n", format); \
        some_other_variadac_function(format, ##__VA_ARGS__); \
    } while(0)

不过,一般来说,最好的解决方案是使用您试图包装的函数的va_list形式(如果存在的话)。

不是直接的,但是变进函数与可变参数样式的替代函数成对出现是很常见的(在标准库中几乎是普遍的情况)。例如printf函数/

v……函数接受一个va_list形参,它的实现通常是通过编译器特定的“宏魔法”来完成的,但是你可以保证调用v…像这样的可变函数的样式函数可以工作:

#include <stdarg.h>

int m_printf(char *fmt, ...)
{
    int ret;

    /* Declare a va_list type variable */
    va_list myargs;

    /* Initialise the va_list variable with the ... after fmt */

    va_start(myargs, fmt);

    /* Forward the '...' to vprintf */
    ret = vprintf(fmt, myargs);

    /* Clean up the va_list */
    va_end(myargs);

    return ret;
}

这应该会给你你正在寻找的效果。

如果你正在考虑编写一个可变值库函数,你还应该考虑将一个va_list样式的同伴作为库的一部分。从你的问题中可以看出,它对你的用户是有用的。

基本上有三种选择。

一种是不传递它而是使用目标函数的变进实现而不传递椭圆。另一种方法是使用可变宏。第三个选择是我所缺少的所有东西。

我通常选择第一种,因为我觉得这真的很容易处理。选项二有一个缺点,因为调用可变宏有一些限制。

下面是一些示例代码:

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

#define Option_VariadicMacro(f, ...)\
    printf("printing using format: %s", f);\
    printf(f, __VA_ARGS__)

int Option_ResolveVariadicAndPassOn(const char * f, ... )
{
    int r;
    va_list args;

    printf("printing using format: %s", f);
    va_start(args, f);
    r = vprintf(f, args);
    va_end(args);
    return r;
}

void main()
{
    const char * f = "%s %s %s\n";
    const char * a = "One";
    const char * b = "Two";
    const char * c = "Three";
    printf("---- Normal Print ----\n");
    printf(f, a, b, c);
    printf("\n");
    printf("---- Option_VariadicMacro ----\n");
    Option_VariadicMacro(f, a, b, c);
    printf("\n");
    printf("---- Option_ResolveVariadicAndPassOn ----\n");
    Option_ResolveVariadicAndPassOn(f, a, b, c);
    printf("\n");
}

没有办法转发这样的函数调用,因为可以检索原始堆栈元素的唯一位置是my_print()。像这样包装调用的通常方法是有两个函数,一个只是将参数转换为各种varargs结构体,另一个实际操作这些结构体。使用这样的双函数模型,您可以(例如)通过使用va_start()初始化my_printf()中的结构来包装printf(),然后将它们传递给vfprintf()。

如果可以使用c++ 11或更高版本的编译器编译代码,则可以使用可变参数函数模板:

#include <stdio.h>

template<typename... Targs>
int my_printf(const char *fmt, Targs... Fargs) {
    fprintf(stderr, "Calling printf with fmt %s", fmt);
    return printf(fmt, Fargs...);;
}

int main() {
    my_printf("test %d\n", 1);
    return 0;
}

Demo