动态创建 va_list

Posted

技术标签:

【中文标题】动态创建 va_list【英文标题】:Create va_list dynamically 【发布时间】:2010-07-28 08:20:44 【问题描述】:

我有一个功能

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


有人知道我该怎么做吗?

【问题讨论】:

你能改变'foo'函数吗? ***.com/questions/988290/populating-a-va-list的可能重复 就正确答案而言肯定是重复的。 【参考方案1】:

您想要做的是模拟调用堆栈,以便您可以将构造的 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 );

我现在会因为想到这样的想法而羞愧地低下头。

【讨论】:

不仅是个坏主意,甚至可能与 C 标准冲突(C99:7.15.1.1.2、7.15.1.3.2 和可能的 7.15.1.4.2),可能是未定义的行为。不过不太确定。【参考方案2】:

...嗯...可能不便携...肯定不好...但可以解决你的问题...

va_list (至少对于 Visual c++)只是 char* 的 #define → 参数不需要在堆栈中 → 参数只需要在内存中是连续的 → 无需使用汇编程序和/或复制(请参阅我的“只是为了好玩的答案”:-) → 无需担心清理 高效! 在 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;

【讨论】:

可能不便携。在 GCC 中,va_list__builtin_va_list 的 typedef。【参考方案3】:

您的问题被标记为 C++,并且有一些很好的方法(如流)可以在 C++ 中完全避免可变参数。

这是为什么 va_args 会引起疼痛的一个很好的例子。如果您有任何机会更改foo 的签名,那是您最好的选择。使用std::vector&lt;std::string&gt; 而不是 va_list 就可以解决您的问题。

如果foo 位于您无法更改的外部库中,我的下一个建议是寻找其他库。

如果这些都不是一个选项,那么似乎应该有一种方法可以使用 va_list 递归地建立调用列表,但我不知道如何使它工作。

【讨论】:

这是一种非常、非常冗长的“我不知道”的说法。【参考方案4】:

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

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

【讨论】:

这样做的原因不是最有效的方法是:我可以有 1-999 个元素联系人,这看起来很丑陋。我希望找到更有效的解决方案。 Rgds 卢卡斯 我不确定调用具有 999 个参数的函数是否效率更高 你确定库中的 foo 函数能够处理 999 个参数的 va_list 吗? 其实我认为C&C++标准只保证了64个参数。如果这太难看,您可以随时尝试使用 boost 预处理器库来重复代码。【参考方案5】:

您尝试使用的是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 不需要是常数。同样,您只能在封闭范围内使用它,所以 not 从函数中返回它。

如果您想要从 va_list 中获得一个列表中的多种类型,请考虑编写这样的联合:

union variant

    int          i;
    unsigned int u;
    float        f;
    double       d;
    const char * s;
    void *       v;
;

【讨论】:

【参考方案6】:

这取决于编译器什么是 va_list 类型,什么是 va_start 和 va_end 宏。您不能以标准方式执行此操作。您必须使用特定于编译器的构造。

也许你可以改变'foo'函数?如果是这样,则反过来 - 将 va_list 转换为 QList 并使 'foo' 接受 QList。

// 编辑

然后看看 va_list 类型是什么, va_start 和 va_end 宏在您的特定编译器中是什么。然后构建您的 va_list 以使这些宏对其起作用。

【讨论】:

我实际上可以预料到这一点。我为 Windows IDE 开发 QT 应用程序:Visual Studio 2005。 我不能这样做,这个 if 函数由 LIB 公开:)。我没有它的源代码。 我不能这样做,这是 LIB 公开的功能 :)。我没有它的源代码。【参考方案7】:

允许任意参数计数 幸运的是 sizeof(std::wstring) 是 sizeof(int) 的倍数 在 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);

只是为了好玩>

【讨论】:

以上是关于动态创建 va_list的主要内容,如果未能解决你的问题,请参考以下文章

C++中为啥要动态创建对象,有啥好处

DOM中动态创建元素与jQuery中动态创建元素

怎么在动态下创建一个空格 象这样 window.document.createElement()

Delphi 怎么创建动态库?

访问动态创建的面板上的动态创建的控件

设计模式代理模式 ( 动态代理使用流程 | 创建目标对象 | 创建被代理对象 | 创建调用处理程序 | 动态创建代理对象 | 动态代理调用 )