[英]Create va_list dynamically
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 and it will change during run-time. 我需要使用它,但是要求非常严格, va_list
数量会有所不同,并且在运行时会有所变化。 What I would like to do is: 我想做的是:
create a va_list
(which expects char*
) form 创建一个va_list
(需要char*
)形式
QList<Contact*>
where Contact
is a defined class 其中Contact
是已定义的类
class Contact
{
public:
QString getName();
private:
QString m_name;
};
and I would like to populate in the loop va_list
for example: 我想在循环va_list
填充例如:
for (int idx = 0; idx<contacts.count(); idx++)
{
contacts.at(idx)->getName(); // this i would like to pass to va_list
}
Does anybody have a clue about how I could do this? 有人知道我该怎么做吗?
What you're wanting to do is to simulate the call stack so you can pass a constructed va_list to foo(). 您想要做的是模拟调用堆栈,以便可以将构造的va_list传递给foo()。 This is rather specific to the compiler ( and warning, there are differences between even 32- and 64-bit compilers ). 这是编译器特有的(警告,甚至32位和64位编译器之间也存在差异)。 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. 它似乎确实可以在我的系统Windows 7 w / Visual Studio 2008上运行,仅适用于32位应用程序。
* 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. 我现在想着这个主意,羞愧地垂头丧气。
...hmmm...maybe not portable...for sure not nice...but may solve yor problem... ...嗯...可能不是便携式的...肯定不是很好...但可以解决您的问题...
#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;
}
Your question is tagged C++ and there are nice ways (like streams) to avoid varargs completely in C++. 您的问题被标记为C ++,并且有很好的方法(例如流)完全避免在C ++中使用varargs。
This is a great example of why va_args can cause pain. 这是为什么va_args会引起疼痛的一个很好的例子。 If you have any chance at all to change the signature of foo
, that's your best option. 如果您完全有机会更改foo
的签名,那是您的最佳选择。 Taking a std::vector<std::string>
instead of va_list would just solve your problem right there. 使用std::vector<std::string>
而不是va_list可以解决您的问题。
If foo
is in an external library you can't change, my next suggestion would be to find a different library. 如果foo
在外部库中,则无法更改,我的下一个建议是找到其他库。
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. 如果这些都不是一个选项,则似乎应该有一种方法可以使用va_list递归建立呼叫列表,但是我不知道该如何进行工作。
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);
}
What you are trying to use is alloca
. 您要使用的是alloca
。 A va_list
object can not store variables, the function call stores them, and you can only access it via va_list. va_list
对象无法存储变量,函数调用将其存储,并且您只能通过va_list访问它。 These variables are only valid during the call, and they get ovverwriten afterwards. 这些变量仅在调用期间有效,并且之后会被ovverwriten获取。
THIS WILL NOT WORK: 这将不起作用:
va_list func(int dummy, ...)
{
va_list result;
va_start(result, dummy);
return result;
}
To allocate memory on the stack, without having to write a variadic functions use alloca
. 要在堆栈上分配内存,而不必编写可变参数,请使用alloca
。 It works more or less like malloc
, but you don't have to call free
, it automagically frees itself when you leave the scope. 它的工作原理与malloc
差不多,但是您不必调用free
,当您离开范围时,它会自动释放自己。
int * local = (int *) alloca(3 * sizeof(int));
local[0] = 10;
local[1] = 20;
local[2] = 30;
It's fundamentally the same as writing 基本上和写作是一样的
int local[3];
local[0] = 10;
local[1] = 20;
local[2] = 30;
But with alloca
3 does not need to be a constant. 但是使用alloca
3并不需要是一个常数。 Again you can only use it inside the enclosing scope, so do not return it from the function. 同样的,你只能用它的封闭的范围内,所以不从函数返回。
if what you want from a va_list is the multiple types in one list consider writing a union like this: 如果您想要从va_list中获得的是一个列表中的多种类型,请考虑编写这样的并集:
union variant
{
int i;
unsigned int u;
float f;
double d;
const char * s;
void * v;
};
It depends on compiler what is the va_list type, what are the va_start and va_end macros. 取决于编译器的是什么va_list类型,什么是va_start和va_end宏。 You cannot do this in a standard way. 您无法以标准方式执行此操作。 You would have to use compiler-specific construction. 您将必须使用特定于编译器的构造。
Maybe you can alter the 'foo' function? 也许您可以更改“ foo”功能? If so, then make it inversely - convert va_list to QList and make 'foo' accept QList. 如果是这样,则进行相反的处理-将va_list转换为QList并使'foo'接受QList。
// EDIT //编辑
Then see what the va_list type is, what the va_start and va_end macros are in your specific compiler. 然后查看va_list类型是什么,特定编译器中的va_start和va_end宏是什么。 Then build your va_list in such a way that these macros will work on it. 然后以这样的方式构建您的va_list,以使这些宏可以在其上运行。
<just for fun> <只是为了好玩>
#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> </ just for fun>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.