如何用 #pragma 实现 RAII?

How to achieve RAII with #pragma's?

提问人:neutrion 提问时间:11/1/2023 最后编辑:Remy Lebeauneutrion 更新时间:11/1/2023 访问量:93

问:

目前,我有以下一段代码:

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

我想使用这样的东西:

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

该代码在 C++20 下编译良好,但不会生成错误:

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

为什么?有可能实现我想要的吗?

C++ pragma raii

评论

4赞 Pepijn Kramer 11/1/2023
RAII 是一个运行时概念。您正在尝试在编译期间运行C++代码,这需要 constexpr/consteval,但不适用于 #pragma。不过,我有点喜欢你的想法:)因此,在 #pragma 情况下,它又回到了手动管理您的推送和弹出
1赞 Eljay 11/1/2023
实现您想要的一种方法是创建一个代码生成器,然后该生成器将在所需位置发出编译指示。
1赞 Pepijn Kramer 11/1/2023
此外,禁用警告是您应该仅对无法控制的代码(第三方库)执行的操作。在所有其他情况下,只需修复警告,它们的存在是有原因的。缺少默认/“C”大小写绝对值得警告;)

答:

1赞 Blindy 11/1/2023 #1

不,在进行任何编译之前,所有预处理器指令都会被读取和解释(这实际上是某些编译器上的一个单独步骤)。换言之,你已经完全应用于你在代码中看到的开始部分和结束部分之间的行。#pragma

你所追求的是源代码生成,这需要一个额外的工具来预处理你的文件,并将这些语句插入到你想要的地方(或者使用一个编译器,允许你在构建时注入钩子来生成它们,比如clang)。

1赞 StoryTeller - Unslander Monica 11/1/2023 #2

编译指示的处理时间过早,无法将保护对象之类的东西应用于块。你能做的最好的事情就是编写你的宏,在样板上剪掉一点

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

从 C++11 开始,我们有了指令的形式,将编译指示嵌入到其他扩展中。 代替守卫对象,并划定区域(而不是大括号)。_PragmaDIAG_ERR_PUSH(...)DIAG_POP()

不完全是 RAII,但肯定比必须为实用语输入所有细节要好。这是一个活生生的例子