Create va_list dynamically

后端 未结 7 726
一个人的身影
一个人的身影 2020-12-06 01:08

I have a function

void foo(int cnt, va_list ap);

I need to use it, but requirement is quite strict, number of va_list vary an

相关标签:
7条回答
  • 2020-12-06 01:21

    What you're wanting to do is to simulate the call stack so you can pass a constructed va_list to foo(). This is rather specific to the compiler ( and warning, there are differences between even 32- and 64-bit compilers ). The following code is for ENTERTAINMENT PURPOSES ONLY!!! as (if it even works on your system) it is prone to breakage. With it, I use a flat memory buffer and the populate it with a count and a bunch of character strings. You could fill it as appropriate with pointers to your strings and hand them down.

    It does seem to work on my system, Windows 7 w/ Visual Studio 2008, for 32-bit applications only.

    * BAD IDEA CODE FOLLOWS!!! *

    #define PSEUDOSTACKSIZE ( sizeof(int) + 999 * sizeof(const char*) )
    #pragma pack( push,1 )
    union PSEUDOSTACK
    {
        int count;
        char data[PSEUDOSTACKSIZE];
    };
    #pragma pack( pop )
    
    void foo( int count, va_list args )
    {
        for ( int i = 0; i < count; i++ )
        {
            char *s = va_arg( args, char* );
            printf( "%s\n", s);
        }
    }
    
    void bar( PSEUDOSTACK data, ... ) 
    { 
        va_list args; 
        va_start(args, data.count); 
        foo( data.count, args);
        va_end(args); 
    } 
    // And later on, the actual test case code.
    PSEUDOSTACK barData;
    barData.count = 999;
    char *p = barData.data + sizeof(int);
    for ( int i = 0; i < 999; i++, p += sizeof(char*) )
    {
        *reinterpret_cast<char**>(p) = "ThisIsABadIdea";
    }
    bar( barData );
    

    I'll now go hang my head in shame for thinking of such an idea.

    0 讨论(0)
  • 2020-12-06 01:22

    If the number of elements in the list is limited, I would go for manual dispatch depending on the number of elements.

    void call_foo(int count, ...) {
        va_list args;
        va_start(args, count);
        foo(count, args);
        va_end(args);
    }
    
    switch (contacts.count()) {
        case 0: return call_foo(contacts.count());
        case 1: return call_foo(contacts.count(),
                                contacts.at(0)->getName());
        case 2: return call_foo(contacts.count(),
                                contacts.at(0)->getName(),
                                contacts.at(1)->getName());
        case 3: return call_foo(contacts.count(),
                                contacts.at(0)->getName(),
                                contacts.at(1)->getName(),
                                contacts.at(2)->getName());
        default: /* ERROR HERE, ADD MORE CASES */ return call_foo(0);
    }
    
    0 讨论(0)
  • 2020-12-06 01:22

    <just for fun>

    • allowing arbitrary argument count
    • luckily sizeof(std::wstring) is a multiple of sizeof(int)
    • tested on w2k3 sp2 32bit + visual c++ 2010
    
    #include <stdarg.h>
    #include <string>
    #include <vector>
    #include <iostream>
    
    #define N 6 // test argument count
    
    void foo(int n, ...);
    
    int main(int, char*[])
    {
        std::vector strings;
        std::wstring s(L"a");
        int i(0);
    
        // create unique strings...
        for (; i != N; ++i)
        {
            strings.push_back(s);
            ++s.front();
        }
    
        int n_stack_strings(N*sizeof(std::wstring)),    // space needed for strings
            n_stack(sizeof(int)+n_stack_strings);   // overall stack space...needed for cleanup
    
        __asm sub esp, n_stack_strings  ; reserve stack space
    
        std::wstring* p_stack(0);
    
        __asm mov p_stack, esp  ; get stack pointer
    
        std::wstring* p(p_stack);
        std::vector<std::wstring>::iterator string(strings.begin());
    
        // copy to stack
        for (; string != strings.end(); ++string, ++p)
            new (p) std::wstring(*string);
        __asm push N    ; argument count...arguments right to left (__cdecl)
        __asm call foo
        // cleanup
        for (p = p_stack; p != p_stack+N; ++p)
            p->~basic_string();
        __asm add esp, n_stack  ; caller has to cleanup the stack (__cdecl)
        return 0;
    }
    
    void foo(int n, ...)
    {
        int i(0);
        va_list marker;
    
        va_start(marker, n);
        for (; i != n; ++i)
            std::wcout << va_arg(marker, std::wstring) << std::endl;
        va_end(marker);
    }
    
    

    </just for fun>

    0 讨论(0)
  • 2020-12-06 01:39

    It depends on compiler what is the va_list type, what are the va_start and va_end macros. You cannot do this in a standard way. You would have to use compiler-specific construction.

    Maybe you can alter the 'foo' function? If so, then make it inversely - convert va_list to QList and make 'foo' accept QList.

    // EDIT

    Then see what the va_list type is, what the va_start and va_end macros are in your specific compiler. Then build your va_list in such a way that these macros will work on it.

    0 讨论(0)
  • 2020-12-06 01:43

    ...hmmm...maybe not portable...for sure not nice...but may solve yor problem...

    • va_list is (at least for visual c++) just a #define for char*
    • → arguments don't need to be on the stack
    • → arguments are just required to be continuous in memory
    • → no need to use assembler and/or copying (see my 'just for fun answer' :-)
    • → no need to worry about cleanup
    • efficient!
    • tested on w2k3 sp2 32bit + vc++ 2010
    
    #include <stdarg.h>
    #include <string>
    #include <vector>
    #include <iostream>
    
    #define N 6 // test argument count
    
    void foo(int n, va_list args);
    
    int main(int, char*[])
    {
        std::vector<std::wstring> strings;
        std::wstring s(L"a");
        int i(0);
    
        // create unique strings...
        for (; i != N; ++i)
        {
            strings.push_back(s);
            ++s.front();
        }
        foo(N, reinterpret_cast<va_list>(strings.data()));
        return 0;
    }
    
    void foo(int n, va_list args)
    {
        int i(0);
    
        for (; i != n; ++i)
            std::wcout << va_arg(args, std::wstring) << std::endl;
    }
    
    
    0 讨论(0)
  • 2020-12-06 01:44

    Your question is tagged C++ and there are nice ways (like streams) to avoid varargs completely in C++.

    This is a great example of why va_args can cause pain. If you have any chance at all to change the signature of foo, that's your best option. Taking a std::vector<std::string> instead of va_list would just solve your problem right there.

    If foo is in an external library you can't change, my next suggestion would be to find a different library.

    If none of those is an option it seems like there ought to be a way to recursively build up the call list using va_list, but I couldn't figure out how to make that work.

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