代码之家  ›  专栏  ›  技术社区  ›  prakash

如何用可变长度参数包装函数?

  •  46
  • prakash  · 技术社区  · 18 年前

    我想在C/C++中做这件事。

    我遇见 Variable Length Arguments 但这建议使用python&c的解决方案使用 libffi .

    现在,如果我想包装 printf 函数与 myprintf

    我的做法如下:

    void myprintf(char* fmt, ...)
    {
        va_list args;
        va_start(args,fmt);
        printf(fmt,args);
        va_end(args);
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        int a = 9;
        int b = 10;
        char v = 'C';
        myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
        return 0;
    }
    

    但结果并不像预期的那样!

    This is a number: 1244780 and
    this is a character: h and
    another number: 29953463
    

    我错过了什么??

    7 回复  |  直到 11 年前
        1
  •  65
  •   Arun    13 年前

    问题是不能将“printf”与va_参数一起使用。你必须使用 VPrTNF 如果使用变量参数列表。vprint、vsprintf、vfprintf等(Microsoft的C运行时中还有“安全”版本,可防止缓冲区溢出等)

    您的示例工作如下:

    void myprintf(char* fmt, ...)
    {
        va_list args;
        va_start(args,fmt);
        vprintf(fmt,args);
        va_end(args);
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        int a = 9;
        int b = 10;
        char v = 'C'; 
        myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
        return 0;
    }
    
        2
  •  11
  •   Shafik Yaghmour    13 年前

    在C++ 11中,这是一种可能的解决方案。 Variadic templates :

    template<typename... Args>
    void myprintf(const char* fmt, Args... args )
    {
        std::printf( fmt, args... ) ;
    }
    

    编辑

    正如@rubenvb指出的,需要考虑一些权衡因素,例如,您将为每个实例生成代码,这将导致代码膨胀。

        3
  •  8
  •   David Sykes    13 年前

    我也不确定你所说的纯

    在C++中,我们使用

    #include <cstdarg>
    #include <cstdio>
    
    class Foo
    {   void Write(const char* pMsg, ...);
    };
    
    void Foo::Write( const char* pMsg, ...)
    {
        char buffer[4096];
        std::va_list arg;
        va_start(arg, pMsg);
        std::vsnprintf(buffer, 4096, pMsg, arg);
        va_end(arg);
        ...
    }
    
        4
  •  4
  •   basin    12 年前

    实际上,有一种方法可以调用一个没有 va_list 包装的版本。其思想是使用汇编程序,不要触摸堆栈中的参数,并临时替换函数的返回地址。

    Visual C x86的示例。 call addr_printf 电话 printf() :

    __declspec( thread ) static void* _tls_ret;
    
    static void __stdcall saveret(void *retaddr) {
        _tls_ret = retaddr;
    }
    
    static void* __stdcall _getret() {
        return _tls_ret;
    }
    
    __declspec(naked)
    static void __stdcall restret_and_return_int(int retval) {
        __asm {
            call _getret
            mov [esp], eax   ; /* replace current retaddr with saved */
            mov eax, [esp+4] ; /* retval */
            ret 4
        }
    }
    
    static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) {
        printf("calling printf(\"%s\")\n", fmt);
    }
    
    static void __stdcall _dbg_printf_end(int ret) {
        printf("printf() returned %d\n", ret);
    }
    
    __declspec(naked)
    int dbg_printf(const char *fmt, ...)
    {
        static const void *addr_printf = printf;
        /* prolog */
        __asm {
            push ebp
            mov  ebp, esp
            sub  esp, __LOCAL_SIZE
            nop
        }
        {
            va_list args;
            va_start(args, fmt);
            _dbg_printf_beg(fmt, args);
            va_end(args);
        }
        /* epilog */
        __asm {
            mov  esp, ebp
            pop  ebp
        }
        __asm  {
            call saveret
            call addr_printf
            push eax
            push eax
            call _dbg_printf_end
            call restret_and_return_int
        }
    }
    
        5
  •  1
  •   Konrad Rudolph    18 年前

    你使用C还是C++?下一个C++版本c++ 0x,将支持 variadic templates 为解决这个问题提供了解决方案。

    另一种解决方法是通过巧妙的运算符重载来实现如下语法:

    void f(varargs va) {
        BOOST_FOREACH(varargs::iterator i, va)
            cout << *i << " ";
    }
    
    f(args = 1, 2, 3, "Hello");
    

    为了让这个工作,班上 varargs 必须执行才能重写 operator = 它返回一个代理对象,该对象依次重写 operator , . 然而,在我看来,在当前C++中不可能使这种变体类型安全,因为它必须通过类型擦除来工作。

        6
  •  0
  •   Mark Ingram    18 年前

    你说的纯C/C++解决方案是什么意思?

    C运行时中支持跨平台的rest参数(…)。

    http://msdn.microsoft.com/en-us/library/kb57fad8.aspx

        7
  •  0
  •   john    14 年前
    void myprintf(char* fmt, ...)
    {
        va_ list args;
        va_ start(args,fmt);
        printf(fmt,args); ----> This is the fault. vprintf(fmt, args); should have been used.
        va_ end(args);
    }
    If you're just trying to call printf, 
    there's a printf variant called vprintf that takes 
    the va_list directly :  vprintf(fmt, args);