c++pragmaraii

How to achieve RAII with #pragma's?


Currently, I have the following piece of code:

enum class Letters {
    A,
    B,
    C
};

#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
        
Letters e(Letters::A);
switch(e){
    case Letters::A: break;
    case Letters::B: break;
}

#pragma GCC diagnostic pop

I want to use something like this:

class DiagnosticError {
public:
    constexpr DiagnosticError() {
        #pragma GCC diagnostic push
        #pragma GCC diagnostic error "-Wswitch"
    }

    constexpr ~DiagnosticError() {
        #pragma GCC diagnostic pop
    }
};

enum class Letters {
    A,
    B,
    C
};

{
    constexpr DiagnosticError switchError;
        
    Letters e(Letters::A);
    switch(e){
        case Letters::A: break;
        case Letters::B: break;
    }
}

The code compiles fine under C++20, but without generating the error:

error: enumeration value 'C' not handled in switch [-Werror=switch]
   38 |         switch(e){

Why is that? Is it possible to achieve what I want?


Solution

  • Pragmas are handled by too early a translation phase to apply something like a guard object to a block. The best you can do is write your macros to cut a bit on the boiler-plate

    #define DIAG_ERR_PUSH(flags) _Pragma("GCC diagnostic push") DIAG_ERR_PUSH_(GCC diagnostic error flags)
    #define DIAG_ERR_PUSH_(str) _Pragma(#str)
    
    #define DIAG_POP() _Pragma("GCC diagnostic pop")
    
    enum class Letters {
        A,
        B,
        C
    };
    
    DIAG_ERR_PUSH("-Wswitch")
        
    Letters e(Letters::A);
    switch(e){
        case Letters::A: break;
        case Letters::B: break;
    }
    
    DIAG_POP()
    

    Since C++11 we have the _Pragma form of the directive, to embed pragmas into other expansions. DIAG_ERR_PUSH(...) takes the place of your guard object, and DIAG_POP() delimits the region (instead of curly braces).

    Not quite RAII, but sure better than having to type all the minutia for the pragma. Here's a live example.