如何使用C++20指定初始化程序在结构体中初始化联合

4

我正在尝试使用C++20指定初始化程序初始化sigaction.h中的struct sigaction,但是出现了编译器错误。由于这是一个较大程序的一部分,因此我创建了一个简短的示例:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void onSIGINT(int signal, siginfo_t *siginfo, void *context) {
    printf("Caught signal\n");
}

int main(int argc, char** argv) {
    struct sigaction act = {
        .sa_sigaction = onSIGINT,
        .sa_flags = SA_SIGINFO
    };
    if(sigaction(SIGINT, &act, nullptr) != 0) {
        fprintf(stderr, "Failed to listen for SIGINT\n");
        return 1;
    }
    printf("Waiting for signal...\n");
    pause();
    printf("Exit\n");
    return 0;
}

根据https://gcc.gnu.org/projects/cxx-status.html#cxx20,在gcc8中可用指定初始化器。 使用-std=c++2a运行Debian buster上的g++(v8.3.0)会出现以下错误:
main.cpp: In function ‘int main(int, char**)’:
main.cpp:11:9: error: expected primary-expression before ‘.’ token
         .sa_sigaction = onSIGINT,
         ^
main.cpp:12:9: error: either all initializer clauses should be designated or none of them should be
         .sa_flags = SA_SIGINFO
         ^

只初始化sa_flags可以成功编译,只初始化sa_sigaction会导致编译失败(只有第一个错误)。

我还尝试直接初始化__sigaction_handler(不使用定义来访问联合成员):

struct sigaction act = {
    .__sigaction_handler = { .sa_sigaction = onSIGINT },
    .sa_flags = SA_SIGINFO
};

这会产生类似的错误:

main.cpp: In function ‘int main(int, char**)’:
main.cpp:12:34: error: expected primary-expression before ‘.’ token
         .__sigaction_handler = { .sa_sigaction = onSIGINT },
                                  ^

我想我在结构体内部的联合方面做错了什么,但我无法弄清楚是什么。

我知道我可以通过将结构体的内存清零,然后设置回调和标志来实现大致相同的功能,但这不是重点。


你的编译器版本是多少?运行 gcc --version - KaiserKatze
2个回答

3
问题在于 sa_sigaction 是一个宏, 它被定义为 __sigaction_handler.sa_sigaction。这意味着你的代码会扩展为 .__sigaction_handler.sa_sigaction = onSIGINT;这在C语言中是有效的,其中命名成员初始化器可以具有复杂结构,但在C++20中无效。
你可以通过 #undef 来解决这个问题:
#undef sa_sigaction
    .__sigaction_handler = { .sa_sigaction = onSIGINT },

然而这种方法不够可移植(取消定义标准库宏,更不用说使用双下划线前缀成员了),因此我建议不要采用。

谢谢解释,取消定义宏实际上使得编译成功了。我理解你的观点,在最后这确实很丑陋。所以整个问题产生的原因是 sa_sigaction 同时既是宏又是成员名称,而当给出 . __sigaction_handler = { . sa_sigaction = onSIGINT } 时编译器更倾向于使用宏? - wurzelgemuese
是的,但是宏定义是在成员名称定义之后定义的,因此从那时起,唯一输出成员名称的方法是取消定义该宏。 - ecatmur

0
尽管ecatmur在诊断问题时是正确的(sa_sigaction是一个将转换为嵌套指定初始化器的宏,这是C99的特性,但尚未添加到C++中,截至C++23),但解决这个问题最可行的方法确实是通过将结构体的内存清零,然后设置回调函数和标志位,就像问题所提出的那样。
    struct sigaction sa {};
    sa.sa_sigaction = onSIGINT;
    sa.sa_flags     = static_cast<int>(SA_SIGINFO | SA_RESETHAND),

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接