Initialization with static storage duration may throw an exception cannot be caught

本文介绍如何在CLion中使用/*NOLINT*/或//NOLINT注释来消除特定警告,如Boost或GTest宏产生的静态存储警告。这种方法适用于C++17,可有效改善代码阅读体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我搭建了个人博客主页, 欢迎访问: blog.joelzho.com

正文

说实话, CLion 是一个不错的 IDE , 我特地花钱购买了一年正版授权。
不过有时候一些警告实在太恶心了,比如说我们在写 Boost 或者 GTest 测试的时候的静态存储警告。

例如下面的 Boost 测试代码:

BOOST_AUTO_TEST_SUITE(test_time_util)

BOOST_AUTO_TEST_CASE(get_now)
{
    // test code
}

BOOST_AUTO_TEST_SUITE_END()

这是再正常不过的代码了,但是CLion 会将上面代码中的 Boost 宏都标上以下类似的警告:

Clang-Tidy: Initialization of "xx" with static storage duration may throw an exception cannot be caught

很烦的好不好????
黄黄的东西一点也不好看有木有啊!

我最初尝试找有没有 属性值指示符 可以压制这些警告,然而并没有(我使用的是 C++ 17 )。
不过,我在github 上翻到了有人和我有一样的洁癖,下面有人给出了答案。

后来在 LLVM 官网介绍中找到了解释
https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics

那就是在代码的行末添加一个多行注释: /* NOLINT */,, 或者单行注释: //NOLINT

于是上面的代码就变成了:

BOOST_AUTO_TEST_SUITE(test_time_util) /* NOLINT */

BOOST_AUTO_TEST_CASE(get_now) /* NOLINT */
{
    // test code
}

BOOST_AUTO_TEST_SUITE_END() /* NOLINT */

Yes! 有效的!果然没有黄黄的警告了!!!

我测试过,只能加在行末,不能加在上一行。

题外话

我测试过,这个注释可以压制很多的警告,但是我目前只用来压制 Boost 或者 GTest 的宏警告。
因为 CLion 的大多数警告都是非常有用的,通过 CLion 建议的代码来解决警告才是正确的做法。
当然,如果别人的用户第三方库中头文件被警告了地话,就直接压制警告吧。

以下链接是我找到的这个解决方案的源地址:

Github: https://github.com/catchorg/Catch2/issues/921
Clang: https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics

顺便说一句, 我并不知道为什么这个注释可以压制某些警告。请知道的朋友联系我,给我科普以下,谢谢。

<think>我们正在处理一个关于在CLion中使用WindowsAPI时遇到的编译错误和Clang-Tidy警告的问题。具体涉及的API包括CreateFontA,DrawTextA,MessageBoxA,CreateWindowExA,以及类型转换问题(如LPCSTR和wchar_t),还有静态初始化可能抛出异常的问题,以及使用nullptr的建议。首先,我们需要理解这些API的使用方式以及可能出现的类型问题。WindowsAPI中有许多函数有ANSI和Unicode版本,例如`MessageBoxA`(ANSI)和`MessageBoxW`(Unicode)。在CLion中,默认情况下可能使用Unicode字符集,但如果我们显式调用了ANSI版本的函数,则需要注意字符串的类型。主要问题可能包括:1.函数调用不匹配:可能是由于参数类型不正确导致。2.类型不兼容:例如,将`char*`传递给需要`wchar_t*`的函数,或者反过来。3.静态初始化可能抛出异常:这通常发生在全局或静态对象的构造函数可能抛出异常时。4.Clang-Tidy警告:可能包括建议使用nullptr代替NULL,或者检查到可能的类型不匹配。解决方案:1.确保正确的字符集设置:在CMake中,我们可以设置字符集。但注意,如果我们使用ANSI函数,那么应该使用多字节字符集。然而,现代Windows应用程序推荐使用Unicode。因此,建议使用宽字符版本的API(即函数名以W结尾的版本)并统一使用宽字符串。2.类型转换:当我们需要在ANSI和Unicode之间转换时,可以使用Windows提供的转换函数(如MultiByteToWideChar和WideCharToMultiByte)。但在可能的情况下,统一使用一种字符集更好。3.静态初始化问题:避免在全局作用域使用可能抛出异常的复杂初始化。可以将这些对象改为局部静态(在函数内部)或者使用指针并在运行时初始化。4.Clang-Tidy警告:针对具体的警告进行修改。例如,使用`nullptr`代替`NULL`;检查函数参数类型是否匹配;对于静态存储期对象的初始化,确保不会抛出异常,或者捕获异常。具体步骤:###1.使用Unicode版本API建议使用宽字符版本的API,例如`CreateWindowExW`、`MessageBoxW`等。这样,我们需要将字符串字面量改为宽字符形式,例如`L"Hello"`。###2.类型转换如果必须使用ANSI版本,并且字符串是宽字符,则需要转换。但通常,我们可以直接使用宽字符字符串。###3.静态初始化问题例如,如果有一个全局对象,其构造函数可能抛出异常,我们可以改为在函数内使用静态对象,或者使用指针并在首次使用时初始化。###4.Clang-Tidy警告处理-对于函数调用不匹配:检查函数原型,确保传递的参数类型和数量正确。-类型不兼容:使用正确的字符串类型,或者进行显式转换。-使用nullptr:将所有的`NULL`替换为`nullptr`。示例代码修改:假设原始代码可能类似于:```cppHWNDhwnd=CreateWindowExA(0,"MyClass","Title",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);```修改为使用Unicode版本:```cppHWNDhwnd=CreateWindowExW(0,L"MyClass",L"Title",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,nullptr,nullptr,hInstance,nullptr);```同时,在项目中设置使用Unicode字符集(在CMake中可能不需要特别设置,因为WindowsAPI会根据是否定义了UNICODE宏来决定使用哪个版本,但显式调用W版本可以避免宏的影响)。###5.静态初始化问题的解决例如,有一个全局对象:```cppMyClassmyGlobal;//如果构造函数可能抛出异常```可以改为:```cppMyClass&getMyGlobal(){staticMyClassinstance;//第一次调用时构造,C++11保证线程安全returninstance;}```或者使用指针:```cppMyClass*getMyGlobal(){staticstd::unique_ptr<MyClass>instance=std::make_unique<MyClass>();returninstance.get();}```###6.Clang-Tidy检查配置在CLion中,我们可以配置Clang-Tidy检查项。对于某些我们不想看到的警告,可以禁用。但建议先尝试修复代码。例如,如果我们看到关于静态初始化的警告,我们可以按照上述方式修改代码。关于Clang-Tidy的检查,我们可以参考引用[2]中提到的,有些检查与CLion内置检查重复,可以关闭。但这里的问题主要是代码问题,建议修改代码。###7.编译错误处理常见的编译错误可能是由于函数参数类型不对。例如,`DrawTextA`需要`LPCSTR`,而我们传递了宽字符串。因此,统一使用宽字符版本(`DrawTextW`)和宽字符串。总结步骤:1.将所有的WindowsAPI调用改为宽字符版本(W版本)。2.将字符串字面量改为宽字符串(使用`L`前缀)。3.将`NULL`替换为`nullptr`。4.检查所有函数调用的参数类型和数量是否正确。5.处理全局对象,避免静态初始化异常。6.运行Clang-Tidy,根据警告信息进一步调整代码。如果问题仍然存在,请提供具体的错误信息和警告信息,以便进一步分析。相关问题:1.如何在CLion中配置Clang-Tidy检查选项?2.WindowsAPI中ANSI和Unicode版本的区别是什么?如何选择?3.在C++中如何处理全局对象的初始化可能抛出异常的问题?4.如何将现有的使用ANSIWindowsAPI的代码迁移到Unicode版本?5.在CMake中如何设置Windows程序的字符集?</think>### 解决 Windows API 编译错误与 Clang-Tidy 警告 以下是针对在 CLion 中使用 Windows API 时常见问题的解决方案,涵盖函数调用不匹配、类型兼容性和静态初始化问题: #### 1. **字符编码问题处理** Windows API 有 ANSI(`A`后缀)和 Unicode(`W`后缀)版本: ```cpp // 错误:混合使用 ANSI 和 Unicode 类型 MessageBoxA(nullptr, L"宽字符文本", L"标题", MB_OK); // 正确方案:统一字符类型 // 方案1:全部使用 ANSI (LPCSTR) MessageBoxA(nullptr, "ANSI 文本", "标题", MB_OK); // 方案2:全部使用 Unicode (LPCWSTR) MessageBoxW(nullptr, L"Unicode 文本", L"标题", MB_OK); ``` #### 2. **类型不兼容修复** `LPCSTR` 与 `wchar_t*` 的转换: ```cpp // 创建字体示例 HFONT hFont = CreateFontA( 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, // 明确字符集 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Arial" // LPCSTR 类型 ); // 宽字符版本 HFONT hFontW = CreateFontW( 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, L"Arial" // LPCWSTR 类型 ); ``` #### 3. **Clang-Tidy 静态初始化警告** 解决 `static storage duration` 对象的异常风险: ```cpp // 危险:全局静态对象可能抛出异常 static HBRUSH hBrush = CreateSolidBrush(RGB(255,0,0)); // ⚠️ Clang-Tidy 警告 // 安全方案:使用延迟初始化 HBRUSH GetSafeBrush() noexcept { static HBRUSH hBrush = nullptr; if (!hBrush) { hBrush = CreateSolidBrush(RGB(255,0,0)); if (!hBrush) { // 错误处理而非抛出异常 MessageBoxW(nullptr, L"画刷创建失败", L"错误", MB_ICONERROR); } } return hBrush; } ``` #### 4. **空指针最佳实践** 使用 `nullptr` 代替 `NULL`: ```cpp // 创建窗口示例 HWND hWnd = CreateWindowExW( 0, L"MyWindowClass", L"窗口标题", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, nullptr, // 父窗口 (推荐 nullptr) nullptr, // 菜单句柄 (推荐 nullptr) hInstance, nullptr // 附加数据 (推荐 nullptr) ); ``` #### 5. **Clang-Tidy 配置建议** 在 CLion 中调整检查规则(`.clang-tidy` 文件): ```yaml Checks: > -*,clang-diagnostic-*, bugprone-*, performance-*, modernize-use-nullptr, modernize-avoid-c-arrays, windows-* WarningsAsErrors: "*" HeaderFilterRegex: ".*" ``` --- ### 关键解决原则 1. **字符集一致性** - 始终统一使用 ANSI(`char*`)或 Unicode(`wchar_t*`)API 家族 - 避免混用 `LPCSTR` 和 `LPCWSTR` - 使用 `TCHAR` 宏实现自适应代码(需定义 `UNICODE`) 2. **异常安全** - 静态对象初始化使用 `noexcept` 包装 - 用返回错误码代替抛出异常(Windows API 惯例) 3. **现代 C++ 实践** - 用 `nullptr` 替代 `NULL` - 对资源使用 RAII 包装器(如 `std::unique_ptr` 自定义删除器) 4. **编译器兼容** - 添加 Windows 头文件前定义 `NOMINMAX` 和 `WIN32_LEAN_AND_MEAN` ```cpp #define NOMINMAX #define WIN32_LEAN_AND_MEAN #include <Windows.h> ``` --- ### 相关问题 1. 如何在 Windows API 开发中正确处理 UTF-8 和 UTF-16 编码转换? 2. 哪些 RAII 包装器模式适用于管理 Windows API 资源句柄(如 `HANDLE`, `HWND`)? 3. 如何配置 CLion 的 Clang-Tidy 使其忽略特定的 Windows API 警告? 4. 使用 `CreateWindowEx` 时如何避免出现 "未处理的窗口消息" 错误? 5. Windows API 中的 `LRESULT CALLBACK` 函数应该如何声明以符合现代 C++ 标准? [^1]: Qt Creator 集成了 Clang Tidy 用于静态分析 [^2]: CLion 原生检查与 Clang-Tidy 的重合项处理建议
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值