5

I am trying to initialize struct sigaction (from sigaction.h) using C++20 designated initializers, but there are compiler errors. As this is part of a larger program, I created a short example:

#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;
}

According to https://gcc.gnu.org/projects/cxx-status.html#cxx20 designated initializers are available in gcc8. Running g++ (v8.3.0 on Debian buster) with -std=c++2a gives the following error:

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
         ^

Initializing only sa_flags compiles successfully, initializing only sa_sigaction fails to compile (only first error).

I also tried to initialize __sigaction_handler directly (no using the define to access the union member):

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

That produces a similar error:

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

I suppose I am doing something wrong about the union inside the struct, but I can't figure out what.

I am aware that I could achieve roughly the same by zeroing the memory of the struct and then setting callback and flags, but thats not the point.

1
  • What's your compiler version? Run gcc --version. Commented Dec 14, 2020 at 11:58

2 Answers 2

4

The problem is that sa_sigaction is a macro, defined to __sigaction_handler.sa_sigaction. This means that your code expands to .__sigaction_handler.sa_sigaction = onSIGINT; this is valid C, where a named member initializer is allowed to have a complex structure, but it is not valid in C++20.

You can see that by #undefing it:

#undef sa_sigaction
    .__sigaction_handler = { .sa_sigaction = onSIGINT },

However this is non-portable (undefining a standard library macro, not to mention using a double-underscore prefixed member), so I would recommend against it.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the explanation, undef-ing the macro in fact made it compile. I see your point that this is quite ugly in the end. So the whole problem arises from sa_sigaction being a macro and a member name at the same time and the compiler prefering the macro when given .__sigaction_handler = { .sa_sigaction = onSIGINT } ?
Yes, but the macro is defined after the member name is defined so from that point the only way to output the member name specifically is to undef the macro.
0

While ecatmur is correct in diagnosing the issue (sa_sigaction is a macro that translates to a nested designated initializer, which is a C99 feature not yet added to C++, as of C++23), the most workable way to solve this is indeed by zeroing the memory of the struct and then setting callback and flags, as the question proposes.

    struct sigaction sa {};
    sa.sa_sigaction = onSIGINT;
    sa.sa_flags     = static_cast<int>(SA_SIGINFO | SA_RESETHAND),

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.