How to wrap a function with variable length arguments?

后端 未结 7 2051
借酒劲吻你
借酒劲吻你 2020-12-05 12:54

I am looking to do this in C/C++.

I came across Variable Length Arguments but this suggests a solution with Python & C using libffi.

Now, if I want to wr

相关标签:
7条回答
  • 2020-12-05 13:13

    How do you mean a pure C/C++ solution?

    The rest parameter (...) is supported cross platform in the C runtime.

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

    0 讨论(0)
  • 2020-12-05 13:14

    In C++11 this is one possible solution using Variadic templates:

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

    EDIT

    As @rubenvb points out there are trade-offs to consider, for example you will be generating code for each instance which will lead to code bloat.

    0 讨论(0)
  • 2020-12-05 13:17

    I am also unsure what you mean by pure

    In C++ we use

    #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);
        ...
    }
    
    0 讨论(0)
  • 2020-12-05 13:25

    Actually, there's a way to call a function that has no va_list version from a wrapper. The idea is to use assembler, do not touch arguments in stack, and temporary replace the function return address.

    Example for Visual C x86. call addr_printf calls 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
        }
    }
    
    0 讨论(0)
  • 2020-12-05 13:26
    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);
    
    0 讨论(0)
  • 2020-12-05 13:34

    Are you using C or C++? The next C++ version, C++0x, will support variadic templates which provide a solution to that problem.

    Another workaround can be achieved by clever operator overloading to achieve a syntax like this:

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

    In order to get this to work, the class varargs has to be implemented to override operator = that returns a proxy object which, in turn, overrides operator ,. However, making this variant type safe in current C++ isn't possible as far as I know since it would have to work by type erasure.

    0 讨论(0)
提交回复
热议问题