如何在 GCC 上创建 va_list?
我正在尝试转换一些代码,以便它也可以在 gcc 上编译(现在,它只能在 MSVC 上编译)。
我遇到的代码位于伪格式化函数中,该函数接受格式字符串和零个或多个参数(const char *format, ...
)作为输入。然后,它将处理使用一些参数的一些占位符,并将其余部分与动态生成的新va_list一起传递给vsprintf
。
这是生成新 va_list
的实际代码:
char *new_args = (char *) malloc(sum);
char *n = new_args;
for(int i = 0; i < nArgs; i++)
{
int j = order[i];
int len = _getlen(types[j]);
memcpy(n, args + cumulOffsets[j], len);
n += len;
}
vsprintf(buffer, sFormat.c_str(), new_args);
在我看来,我没有也永远不会编写这段代码。事实上,我认为这是我一生中见过的最黑客的事情之一。
然而,这个函数非常复杂,非常古老,而且非常重要。它也已经多年没有被修改过(好吧,除了现在),所以虽然我想从头开始重写它,但我无法证明它所花费的时间以及它会引入的错误是合理的。
所以,我需要一种方法在 GCC 上做同样的事情。但是 va_list
不是 char *
所以我得到:
error: ISO C++ forbids casting to an array type '__va_list_tag [1]'
I'm trying to convert some code so that it compiles on gcc too (right now, it compiles only on MSVC).
The code I'm stuck at is in a pseudo-formatting function that accepts as input a format string and zero or more arguments (const char *format, ...
). It will then process some of the placeholders consuming some of the arguments, and pass the rest to vsprintf
along with a new va_list dynamically generated.
This is the actual code for generating the new va_list
:
char *new_args = (char *) malloc(sum);
char *n = new_args;
for(int i = 0; i < nArgs; i++)
{
int j = order[i];
int len = _getlen(types[j]);
memcpy(n, args + cumulOffsets[j], len);
n += len;
}
vsprintf(buffer, sFormat.c_str(), new_args);
In my defense, I didn't and would never write this code. In fact, I think it's one of the most hackiest things I've seen in my whole life.
However, this function is very complex, very old, and very important. It's also hasn't been modified in years (well, except now) so while I'd like to rewrite it from scratch I can't justify the time it would take plus the bugs it would introduce.
So, I need a way to do this same thing on GCC.. But there a va_list
is not a char *
so I'm getting:
error: ISO C++ forbids casting to an array type '__va_list_tag [1]'
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我有点失落。为什么您需要一个新动态生成的va_list?为什么不直接重复使用旧的呢?
我相信 vsnprintf() 使用当前的 va_list 对象(如果你可以这样称呼它)。因此,您可以自由地va_start(),通过va_arg()使用您想要的参数,然后通过va_list将剩余的参数传递给< em>vsnprintf(),然后调用va_end()。
我错过了什么吗?为什么要深拷贝?
如果您确实需要深拷贝,为什么不va_start()新鲜,通过va_arg()删除您想要的参数,然后传递生成的va_list< /em> 对象到 vsnprintf()。
(每次调用 va_arg 都会修改 va_list 对象,以便下一次调用返回下一个参数。)
或者,您可以只使用 va_copy() 。 (尽管一定要在后面加上相应的 va_end()。)
附录:另请注意,这些 va_ 宏基于 C89 和 C89。 C99标准。 GNU g++ 将支持它们。微软的局限性更大。
继 TonyK 评论之后:
如果您从 va_list 中取出前 N 个项目,我上面所说的就有效。如果你把东西从中间拉出来,那就更难了。
没有可移植的方法来构造va_list。
但是,您可以分解格式字符串,使用它来确定对象类型(双精度型、浮点型、整数型等),并使用其自己的格式字符串(原始格式字符串的一小部分)单独打印每个对象类型。多个 snprintf() 调用会导致一些开销。但如果这个例程调用得不太频繁,它应该是可行的。
您还可以使用适当制作的 va_list 打印原始格式字符串的各个部分。换句话说,第一个 vsnprintf() 调用打印元素 1..3,第二个元素 5..7,第三个 10..13 等。(如 vsnprintf()< /em> 将忽略 va_list 上超出其需要的额外元素,您只需要一系列相应的格式字符串片段,并使用 va_list 弹出项目。 em>va_arg() 根据每个 vsnprintf() 调用的需要。)
I'm a bit lost. Why do you need a new dynamically-generated va_list? Why not just reuse the old one?
I believe vsnprintf() uses a current va_list object (if you can call it that). So you are free to va_start(), use the arguments you want via va_arg(), then pass the remaining arguments via the va_list to vsnprintf(), and then call va_end().
Am I missing something? Why the deep copy?
And if you do need a deep copy, why not va_start() fresh, remove the arguments you want via va_arg(), and then pass the resulting va_list object to vsnprintf().
(Each call to va_arg modifies the va_list object so that the next call returns the next argument.)
Alternatively, you could just use va_copy(). (Though be sure to follow it with a corresponding va_end().)
Addendum: Also note that these va_ macros are based on C89 & C99 standards. GNU g++ will support them. Microsoft is somewhat more limited.
Following up on TonyK's comment:
What I said above works if you are pulling the first N items off the va_list. If you are pulling items out of the middle, that's harder.
There is no portable way to construct a va_list.
However, you could pull apart the format string, use it to determine the object types (double,float,int,etc), and print each one out individually with it's own format string (a subsection of the original format string). The multiple snprintf() calls will cause some overhead. But if this routine isn't called too often, it should be viable.
You could also print out subsections of the original format string with a suitably crafted va_list. In other words, the first vsnprintf() call prints elements 1..3, the second elements 5..7, the third 10..13, etc. (As vsnprintf() will ignore extra elements on the va_list beyond what it needs. You just need a series of corresponding format-string-fragments, and popping items off the va_list with va_arg() as needed for each vsnprintf() call.)
没有足够的上下文来弄清楚您在这里要做什么,但是如果您需要复制 va_list,您也许可以使用 gcc 支持的 C99 标准函数
va_copy
(但我不知道 MS 是否支持)。There's not enough context to figure out what you're trying to do here, but if you need to COPY a va_list, you may be able to use the C99 standard function
va_copy
, which gcc supports (but I have no idea if MS supports it).有一种方法可以做到这一点,但它并不漂亮:
使用联合而不是强制转换很丑陋,但如果 va_list 是数组类型,则无法强制转换。
There is a way to do this, it isn't pretty:
Using a union instead of a cast is ugly, but you can't cast if va_list is an array type.