c++ 动态创建va_list

taor4pac  于 2023-01-18  发布在  其他
关注(0)|答案(7)|浏览(255)

我有一个功能

void foo(int cnt, va_list ap);

我需要使用它,但要求相当严格,va_list的数量会有所变化,并且在运行时会发生变化。我想做的是:
创建一个va_list(需要char*)表单

QList<Contact*>

其中Contact是已定义的类

class Contact
{
   public:
      QString getName();
   private: 
      QString m_name;

};

我想在循环va_list中填充,例如:

for (int idx = 0; idx<contacts.count(); idx++)
{
    contacts.at(idx)->getName(); // this i would like to pass to va_list

}

有人知道我是怎么做到的吗?

hc2pp10m

hc2pp10m1#

您需要做的是模拟调用堆栈,以便将构造的va_list传递给foo()。这是编译器所特有的(并且警告,即使是32位编译器和64位编译器之间也存在差异)。下面的代码***仅用于娱乐目的!!!***(如果它在你的系统上工作的话)它很容易被破坏。有了它,我使用一个平面内存缓冲区,然后用一个计数和一堆字符串填充它。你可以在适当的时候用指向你的字符串的指针填充它,然后把它们传下来。
它似乎确实可以在我的系统Windows 7 W/ Visual Studio 2008上工作,仅适用于32位应用程序。

错误的想法代码如下!!!

#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 );

我现在要为想到这样的主意而羞愧地低下头去了。

f1tvaqid

f1tvaqid2#

...嗯...也许不方便携带...肯定不好...但可能会解决你的问题...

  • va_list(至少对于visual c++)只是char* 的#define
  • →参数不需要在堆栈上
  • →参数只要求在内存中连续
  • →不需要使用汇编程序和/或复制(见我的“只是为了好玩的答案”:-)
  • →无需担心清理
  • 效率!
  • 在w2 k3 sp2 32位+ 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;
}
ef1yzkbh

ef1yzkbh3#

你的问题被标记为C++,在C++中有很好的方法(比如流)来完全避免varargs。
这是一个很好的例子,说明了为什么va_args会导致痛苦。如果你有机会改变foo的签名,那是你最好的选择。用std::vector<std::string>代替va_list正好解决了你的问题。
如果foo在一个不能更改的外部库中,我的下一个建议是找到一个不同的库。
如果这些都不是一个选项,那么似乎应该有一种方法使用va_list递归地构建调用列表,但是我不知道如何使其工作。

2ul0zpep

2ul0zpep4#

如果列表中的元素数量有限,我会根据元素数量选择手动调度。

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);
}
1bqhqjot

1bqhqjot5#

您尝试使用的是allocava_list对象不能存储变量,函数调用存储它们,并且您只能通过va_list访问它。这些变量仅在调用期间有效,之后它们将被重写。
这将不起作用:

va_list func(int dummy, ...)
{
   va_list result;
   va_start(result, dummy);
   return result;
}

要在堆栈上分配内存,而不需要写变量函数,可以使用alloca,它的工作原理或多或少类似于malloc,但你不需要调用free,当你离开作用域时,它会自动释放自己。

int * local = (int *) alloca(3 * sizeof(int));
local[0] = 10;
local[1] = 20;
local[2] = 30;

这和写作基本上是一样的

int local[3];
local[0] = 10;
local[1] = 20;
local[2] = 30;

但是在alloca中,3不需要是常量,同样,你只能在封闭作用域内使用它,所以不要从函数中返回它。
如果你想从va_list得到的是一个列表中的多个类型,考虑写一个如下的union:

union variant
{
    int          i;
    unsigned int u;
    float        f;
    double       d;
    const char * s;
    void *       v;
};
3vpjnl9f

3vpjnl9f6#

这取决于编译器,什么是va_list类型,什么是va_start和va_end宏。你不能用标准的方法来做这件事。你必须使用编译器特定的构造。
也许你可以改变'foo'函数?如果是这样的话,那么让它反向转换va_list到QList,并让'foo'接受QList。
//编辑
然后看看va_list类型是什么,在你的编译器中va_start和va_end宏是什么,然后用这些宏可以工作的方式构建你的va_list。

sy5wg1nm

sy5wg1nm7#

〈只是为了好玩〉

  • 允许任意参数计数
  • 幸运的是,sizeof(std::wstring)是sizeof(int)的倍数
  • 在w2 k3 sp2 32位+ 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);
}

〈/只是为了好玩〉

相关问题