7

我正在尝试转换一些代码,以便它也可以在 gcc 上编译(现在,它只能在 MSVC 上编译)。

我遇到的代码是一个伪格式函数,它接受格式字符串和零个或多个参数 ( const char *format, ...) 作为输入。然后它将处理一些占位符,消耗一些参数,并将其余部分vsprintf与动态生成的新 va_list 一起传递。

这是生成新的实际代码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没有 achar *所以我得到:

错误:ISO C++ 禁止转换为数组类型“__va_list_tag [1]”
4

3 回答 3

4

我有点失落。为什么需要一个的动态生成的va_list?为什么不直接重用旧的?

我相信vsnprintf()使用当前的va_list对象(如果你可以这样称呼它)。所以你可以自由地使用va_start(),通过va_arg()使用你想要的参数,然后通过va_list将剩余的参数传递给vsnprintf(),然后调用va_end()

我错过了什么吗?为什么要深拷贝?

如果你确实需要一个深拷贝,为什么不更新 va_start() ,通过va_arg()删除你想要的参数,然后将生成的va_list对象传递给vsnprintf()

(对va_arg的每次调用都会修改va_list对象,以便下一次调用返回下一个参数。)

或者,您可以只使用va_copy()。(尽管一定要跟上相应的va_end()。)

附录:另请注意,这些 va_ 宏基于 C89 和 C99 标准。GNU g++ 将支持它们。微软的局限性更大。


跟进TonyK 的评论:

如果您将前 N 个项目从va_list中拉出,我上面所说的内容将有效。如果您要从中间拉出物品,那就更难了。

没有可移植的方法来构造va_list

但是,您可以拆分格式字符串,使用它来确定对象类型(double、float、int 等),并使用自己的格式字符串(原始格式字符串的一个小节)单独打印每个对象类型。多个snprintf()调用将导致一些开销。但是,如果不经常调用此例程,它应该是可行的。

您还可以使用适当制作的va_list打印出原始格式字符串的子部分。换句话说,第一个vsnprintf()调用打印元素 1..3、第二个元素 5..7、第三个 10..13 等(因为vsnprintf()将忽略va_list上超出它需要的额外元素. 您只需要一系列相应的格式字符串片段,并根据每个vsnprintf()调用的需要使用va_arg()从va_list中弹出项目。)

于 2010-12-26T19:37:01.117 回答
1

没有足够的上下文来弄清楚你在这里要做什么,但如果你需要复制一个 va_list,你可以使用va_copygcc 支持的 C99 标准函数(但我不知道 MS 是否支持它) .

于 2010-12-27T04:56:11.417 回答
0

有一种方法可以做到这一点,它并不漂亮:

union {
      char *pa;
      va_list al;
      } au;

....
au.pa = new_args;
vsprintf(buffer, sFormat.c_str(), au.al);

使用联合而不是强制转换很难看,但如果 va_list 是数组类型,则不能强制转换。

于 2014-04-22T09:26:11.197 回答