不同编译器的 C++ 和可变参数
C++ & varargs with different compilers
我正在尝试将一个用 GCC 编写的 C++ 项目重新编译为 clang,它给我带来了一些关于可变参数方法的问题。
NB:该项目也应该使用非 c++-11 兼容的编译器进行编译,所以我不能在这里使用花哨的 C++11 语法。
GCC 在没有警告的情况下接受以下代码 (1):
void Set(TreeIter &it, ...) {
va_list va;
va_start(va, it);
gtk_list_store_set_valist(*this, const_cast<TreeIter *>(&it), va);
va_end(va);
}
和 (2):
void AddTail(...) {
TreeIter it;
gtk_list_store_append(*this, &it);
va_list va;
va_start(va, this);
gtk_list_store_set_valist(*this, &it, va);
va_end(va);
}
虽然 CLANG 对两者发出警告:
(1):
./ootree.h:444:30: warning: 'va_start' has undefined behavior with reference types [-Wvarargs]
(2):
./ootree.h:476:30: warning: second parameter of 'va_start' not last named argument [-Wvarargs]
我可以通过用副本替换 TreeIter 引用来轻松修复 (1)(标准似乎要求我这样做),但我找不到修复 (2) 的简单方法,这里是一个显示行为的最小独立示例,这个程序不仅适用于 gcc,还适用于 clang,但是那些警告非常可怕...
#include <stdio.h>
#include <stdarg.h>
struct T {
void print(...) {
va_list va;
va_start(va, this);
vprintf("%d %d %d\n", va);
va_end(va);
}
};
int main() {
T().print(2, 4, 6);
}
(2) 的解决方案是显式地获取前 2 个参数,可能提供 1 个参数重载:
void AddTail(gint column, GValue *value, ...) {
TreeIter it;
gtk_list_store_append(*this, &it);
gtk_list_store_set_value(*this, &it, column, value);
va_list va;
va_start(va, value);
gtk_list_store_set_valist(*this, &it, va);
va_end(va);
}
void AddTail(gint column) {
TreeIter it;
gtk_list_store_append(*this, &it);
assert(column == -1);
}
您可以将 (2) 更改为
static void AddTail(T* that, ...) {
TreeIter it;
gtk_list_store_append(*that, &it);
va_list va;
va_start(va, that);
gtk_list_store_set_valist(*that, &it, va);
va_end(va);
}
并将调用从 T().print(2, 4, 6);
更改为 T::print(T(), 2, 4, 6);
。
甚至在第一个参数处添加一个虚拟标签:
struct variadic_tag {};
void AddTail(variadic_tag, ...) {
TreeIter it;
gtk_list_store_append(*that, &it);
va_list va;
va_start(va, that);
gtk_list_store_set_valist(*that, &it, va);
va_end(va);
}
并将 T().print(2, 4, 6);
更改为 T().print(variadic_tag{}, 2, 4, 6);
。
我正在尝试将一个用 GCC 编写的 C++ 项目重新编译为 clang,它给我带来了一些关于可变参数方法的问题。
NB:该项目也应该使用非 c++-11 兼容的编译器进行编译,所以我不能在这里使用花哨的 C++11 语法。
GCC 在没有警告的情况下接受以下代码 (1):
void Set(TreeIter &it, ...) {
va_list va;
va_start(va, it);
gtk_list_store_set_valist(*this, const_cast<TreeIter *>(&it), va);
va_end(va);
}
和 (2):
void AddTail(...) {
TreeIter it;
gtk_list_store_append(*this, &it);
va_list va;
va_start(va, this);
gtk_list_store_set_valist(*this, &it, va);
va_end(va);
}
虽然 CLANG 对两者发出警告:
(1):
./ootree.h:444:30: warning: 'va_start' has undefined behavior with reference types [-Wvarargs]
(2):
./ootree.h:476:30: warning: second parameter of 'va_start' not last named argument [-Wvarargs]
我可以通过用副本替换 TreeIter 引用来轻松修复 (1)(标准似乎要求我这样做),但我找不到修复 (2) 的简单方法,这里是一个显示行为的最小独立示例,这个程序不仅适用于 gcc,还适用于 clang,但是那些警告非常可怕...
#include <stdio.h>
#include <stdarg.h>
struct T {
void print(...) {
va_list va;
va_start(va, this);
vprintf("%d %d %d\n", va);
va_end(va);
}
};
int main() {
T().print(2, 4, 6);
}
(2) 的解决方案是显式地获取前 2 个参数,可能提供 1 个参数重载:
void AddTail(gint column, GValue *value, ...) {
TreeIter it;
gtk_list_store_append(*this, &it);
gtk_list_store_set_value(*this, &it, column, value);
va_list va;
va_start(va, value);
gtk_list_store_set_valist(*this, &it, va);
va_end(va);
}
void AddTail(gint column) {
TreeIter it;
gtk_list_store_append(*this, &it);
assert(column == -1);
}
您可以将 (2) 更改为
static void AddTail(T* that, ...) {
TreeIter it;
gtk_list_store_append(*that, &it);
va_list va;
va_start(va, that);
gtk_list_store_set_valist(*that, &it, va);
va_end(va);
}
并将调用从 T().print(2, 4, 6);
更改为 T::print(T(), 2, 4, 6);
。
甚至在第一个参数处添加一个虚拟标签:
struct variadic_tag {};
void AddTail(variadic_tag, ...) {
TreeIter it;
gtk_list_store_append(*that, &it);
va_list va;
va_start(va, that);
gtk_list_store_set_valist(*that, &it, va);
va_end(va);
}
并将 T().print(2, 4, 6);
更改为 T().print(variadic_tag{}, 2, 4, 6);
。