具有不同编译器的 C++ 和可变参数

Posted

技术标签:

【中文标题】具有不同编译器的 C++ 和可变参数【英文标题】:C++ & varargs with different compilers 【发布时间】:2015-12-03 15:28:57 【问题描述】:

我正在尝试将使用 GCC 编写的 C++ 项目重新编译为 clang,它给我的 varargs 方法带来了一些问题。

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

【问题讨论】:

Clang 是对的。请参阅 [support.runtime]/3。 【参考方案1】:

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

【讨论】:

我使用了带有模板参数而不是 GValue 的稍微修改的版本,但这解决了我的问题,谢谢 :) @Jarod42 gtk_list_store_set_valist 有一个哨兵(-1),所以总是至少有一个参数。【参考方案2】:

您可以将 (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);

【讨论】:

以上是关于具有不同编译器的 C++ 和可变参数的主要内容,如果未能解决你的问题,请参考以下文章

C++:具有可变参数的 Hacky 参数模式

Python C++ API - 具有可变数量参数的重载函数

编译器跳过 C++ 中的可变参数模板/函数 [关闭]

C 编译器如何实现具有可变数量参数的函数?

编译可变参数模板时 GCC 5.3.1 C++ 停止

可以重用线程来运行可变参数函数吗?