Catch2 如何在禁用异常的编译环境下使用?
2026/9/14 11:57:06 网站建设 项目流程

Catch2 如何在禁用异常的编译环境下使用?

【免费下载链接】Catch2A modern, C++-native, test framework for unit-tests, TDD and BDD - using C++14, C++17 and later (C++11 support is in v2.x branch, and C++03 on the Catch1.x branch)项目地址: https://gitcode.com/GitHub_Trending/ca/Catch2

如果你的目标平台(嵌入式系统、部分 RTOS 工具链等)关闭了 C++ 异常,而 Catch2 默认用异常向测试框架报错、并在REQUIRE系列断言失败时中止测试,那么直接编译就会出问题。Catch2 提供了一套编译期配置来支持这种场景:文档将其标注为实验性支持(自 Catch2 2.4.0 引入),核心做法是给测试目标加上无异常编译选项,必要时再显式定义CATCH_CONFIG_DISABLE_EXCEPTIONS或自定义异常处理函数。

先明确禁用异常后的两个行为变化(来自 configuration.md 的 “Disabling exceptions” 一节):

  1. 原本以异常形式报告的错误,其消息会写到Catch::cerr,随后调用std::terminate
  2. REQUIRE系列宏的断言失败时,当前 reporter 返回之后会调用std::terminate

也就是说,CHECK系列断言照常运行并汇报,但任何“需要抛异常”的路径都会直接终止进程。

先弄清哪些宏控制行为

docs/configuration.md 给出了三个相关宏:

作用
CATCH_CONFIG_DISABLE_EXCEPTIONS强制以无异常模式编译。Catch2 也能自动检测编译器是否禁用了异常,此宏用于强制
CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER启用自定义处理点:由用户提供Catch::throw_exception的定义,替代默认的“写Catch::cerrstd::terminate
CATCH_CONFIG_NO_DISABLE_EXCEPTIONS反向开关,取消自动检测出的无异常模式

自动检测逻辑见 catch_compiler_capabilities.hpp 第 375–379 行:当内部检测标记、CATCH_CONFIG_DISABLE_EXCEPTIONSCATCH_CONFIG_NO_DISABLE_EXCEPTIONS都未定义时,Catch2 自行定义CATCH_CONFIG_DISABLE_EXCEPTIONS。所以只要整个构建确实禁用了异常,通常不写宏也能工作;显式定义只是消除歧义。

无异常模式下 Catch2 的try/catch宏会被展开为空壳(见 catch_compiler_capabilities.hpp 第 456–464 行):

#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) #define CATCH_CATCH_ALL if ((false)) #define CATCH_CATCH_ANON(type) if ((false)) #else #define CATCH_TRY try #define CATCH_CATCH_ALL catch (...) #define CATCH_CATCH_ANON(type) catch (type) #endif

默认的“替代表抛异常”处理在 catch_enforce.cpp 第 15–22 行:

#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) [[noreturn]] void throw_exception(std::exception const& e) { Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" << "The message was: " << e.what() << '\n'; std::terminate(); } #endif

注意该定义只在“无异常且未定义CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER”时存在——一旦你启用了自定义处理点,就必须自己提供这个函数。

配置无异常编译

仓库自带的 tests/ExtraTests/CMakeLists.txt 第 91–102 行是官方测试目标的做法,可以直接照搬其编译选项:

add_executable(DisabledExceptions-DefaultHandler ${TESTS_DIR}/X03-DisabledExceptions-DefaultHandler.cpp) add_executable(DisabledExceptions-CustomHandler ${TESTS_DIR}/X04-DisabledExceptions-CustomHandler.cpp) foreach(target DisabledExceptions-DefaultHandler DisabledExceptions-CustomHandler) target_compile_options(${target} PUBLIC $<$<CXX_COMPILER_ID:MSVC>:/EHs-c-;/D_HAS_EXCEPTIONS=0> $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:QCC>,$<CXX_COMPILER_ID:AppleClang>>:-fno-exceptions> ) target_link_libraries(${target} Catch2_buildall_interface) endforeach() target_compile_definitions(DisabledExceptions-CustomHandler PUBLIC CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)

移植到自己的项目时,要点是:

  • 编译器开关分两类:MSVC 用/EHs-c-并定义_HAS_EXCEPTIONS=0;Clang / GCC / QCC / AppleClang 用-fno-exceptions。上面用的是 CMake 生成器表达式按编译器 ID 选择,等价的手写形式是 GCC/Clang 下加-fno-exceptions,MSVC 下加/EHs-c- /D_HAS_EXCEPTIONS=0
  • PUBLIC不能少:这里的选项是PUBLIC的,目的就是把无异常标志传递到 Catch2 库本身。官方链接的是Catch2_buildall_interface(即从源码构建 Catch2 的接口目标)。如果你链接的是已经用默认选项构建好的预编译 Catch2 库,库内部仍是异常开启状态,与你目标的无异常模式不一致——正确做法是像官方测试一样,把无异常选项传导到 Catch2 的编译中(以源码方式集成 Catch2,或自行用同样选项重编)。
  • 宏只加在需要的目标上CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER只定义在启用自定义处理的目标上(见上方target_compile_definitions行);默认处理目标不定义任何额外宏。

自定义处理点开启后,你需要提供一个定义,官方测试 X04-DisabledExceptions-CustomHandler.cpp 第 13–22 行给出的实现是:

namespace Catch { [[noreturn]] void throw_exception(std::exception const& e) { std::cerr << "====== CUSTOM HANDLER ====== run terminates because an exception was thrown.\n" << "The message was: " << e.what() << '\n'; // Avoid abort and other exceptional exits -- there is no way // to tell CMake that abort is the desired outcome of a test. exit(1); } }

函数签名与 docs/configuration.md 的要求一致:[[noreturn]] void Catch::throw_exception(std::exception const&)。你自己的实现可以记录日志、上报或按平台要求退出,但必须最终终止控制流。

用官方测试验证行为

仓库里有一对现成的验证样本:X03-DisabledExceptions-DefaultHandler.cpp(默认处理)和 X04-DisabledExceptions-CustomHandler.cpp(自定义处理)。X03 中有两个测试用例:

  • "Tests that run":4 条CHECK断言,其中 2 条失败,但测试应完整跑完并汇报;
  • "Tests that abort":第三条REQUIRE失败,预期触发终止。

tests/ExtraTests/CMakeLists.txt 为这两个目标登记了验收正则,也就是判断运行结果是否正确的依据:

运行命令期望(PASS_REGULAR_EXPRESSION不应出现(FAIL_REGULAR_EXPRESSION
DisabledExceptions-DefaultHandler "Tests that run"assertions: 4 \| 2 passed \| 2 failedabort;terminate;fatal
DisabledExceptions-DefaultHandler "Tests that abort"(仅非 Windows 登记)Catch will terminate
DisabledExceptions-CustomHandler "Tests that run"assertions: 4 \| 2 passed \| 2 failed====== CUSTOM HANDLER ======
DisabledExceptions-CustomHandler "Tests that abort"====== CUSTOM HANDLER ======

按此验证:先按上一节编译出两个可执行文件,再分别运行上面四个命令。默认处理跑"Tests that run"时应看到断言汇总assertions: 4 | 2 passed | 2 failed且没有 abort/terminate 字样——这说明无异常模式下普通的失败断言仍能正常汇报;跑"Tests that abort"时应看到 catch_enforce.cpp 中那条Catch will terminate because it needed to throw an exception.,对应REQUIRE失败后的终止行为。自定义处理则相反:"Tests that run"中不应出现CUSTOM HANDLER输出(CHECK失败不需要抛异常),只有"Tests that abort"才打印====== CUSTOM HANDLER ======并以exit(1)退出。

一个值得注意的细节:X03 的"Tests that abort"用例里先用std::set_terminate把终止行为改成exit(1)(见 X03 第 27 行),原因是 CMake 无法声明“abort 就是期望结果”;而默认处理引发的终止在 Windows 上会触发即时调试器弹窗,所以官方只在该用例上加了if(NOT WIN32)的登记(CMakeLists.txt 第 149–155 行)。如果你在 Windows 上做同样的手动验证,进程终止的表现形式可能不同,这属于文档已说明的平台差异。

限制与边界

  • 文档原文称这是experimental support(“We also provide an experimental support for disabling exceptions”),即行为不承诺向后兼容,使用时留意所锁定的 Catch2 版本。
  • 两类终止行为是固定语义,不是可配置的:内部错误 → 消息写入Catch::cerrstd::terminateREQUIRE失败 → reporter 返回后std::terminate。自定义处理点只能替换“替代表抛异常”的这一个入口,改变不了REQUIRE失败后终止的事实。
  • CATCH_CONFIG_DISABLE_EXCEPTIONS等配置宏属于编译期配置,和文档中其他CATCH_CONFIG_*宏一样,应通过编译定义统一给到参与链接的所有翻译单元,不要只加在个别文件上。
  • 文档没有给出性能数据或其他平台的专项说明;本文只覆盖“无异常编译 + 默认/自定义处理 + 运行验证”这一条路径。更多编译期开关(前缀宏、CATCH_CONFIG_DISABLE等)见 configuration.md。

【免费下载链接】Catch2A modern, C++-native, test framework for unit-tests, TDD and BDD - using C++14, C++17 and later (C++11 support is in v2.x branch, and C++03 on the Catch1.x branch)项目地址: https://gitcode.com/GitHub_Trending/ca/Catch2

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询