-fno-exceptions嵌入式裸机下constexpr_assert实现问询
无异常依赖的constexpr assert裸机实现方案
问题背景
裸机嵌入式开发场景下需要实现constexpr assert断言,满足以下要求:
- 传入编译期常量触发断言条件时,直接抛出编译错误
- 传入运行期变量触发断言条件时,触发常规运行时断言
- 适配GNU Arm Embedded Toolchain 10.3-2021.10(GCC 10.3.1),支持
-fno-exceptions编译选项,无需开启异常支持。
原有实现的问题:在std::is_constant_evaluated()为真的分支中使用throw触发编译期报错,但只要代码中存在throw关键字,即使对应分支永远不会在运行时执行,开启-fno-exceptions时GCC会直接报错要求开启异常支持,不符合裸机开发要求。
原有错误实现代码:
inline constexpr void constexpr_assert(bool condition) { if (std::is_constant_evaluated()) { if (!condition) { throw; // error: exception handling disabled, use '-fexceptions' to enable } } else { assert(condition); } }
预期使用场景:
class EventMask { uint32_t mask; public: static constexpr auto MAX_ID = 31; constexpr EventMask(unsigned int event_id) : mask(static_cast<uint32_t>(1u) << event_id) { constexpr_assert(event_id <= MAX_ID); } }; int main(int argc, char** argv) { EventMask s_ev(55); // 预期编译期报错 EventMask d_ev(argc); // 预期条件不满足时触发运行期断言 }
可行实现方案
核心原理:C++标准规定,常量求值过程中只要出现不允许出现在常量表达式中的操作,编译器就会直接终止编译并报错,不需要依赖throw异常机制。只要失败分支完全不涉及异常相关关键字,就不会被-fno-exceptions选项拦截。
适配GCC10.3的无异常实现(错误提示友好版)
#include <cassert> #include <type_traits> template <typename T> constexpr void constexpr_assert_failed(T&& msg) { static_assert(!sizeof(T*), "constexpr assertion failed"); } inline constexpr void constexpr_assert(bool condition) { if (std::is_constant_evaluated()) { if (!condition) { constexpr_assert_failed("constexpr assertion failed"); } } else { assert(condition); } }
极简兼容版
如果不需要自定义错误提示,也可以直接用常量求值禁止的操作触发报错,代码更精简:
#include <cassert> #include <type_traits> inline constexpr void constexpr_assert(bool condition) { if (std::is_constant_evaluated()) { if (!condition) { // 常量求值禁止无限循环,触发时直接报编译错误,无异常依赖 while(true) {} // 兜底可选:GCC内置陷阱指令,同样无异常依赖 // __builtin_trap(); } } else { assert(condition); } }
效果验证
- 编译期常量传参场景:
EventMask s_ev(55)构造时,55是编译期常量,常量求值器检测到55 > 31进入失败分支,直接触发编译错误,可通过错误栈定位到断言位置。 - 运行期变量传参场景:
EventMask d_ev(argc)构造时,std::is_constant_evaluated()返回假,走常规运行时assert分支,行为和普通断言完全一致,不会引入任何异常相关代码,生成的固件体积无额外开销。
注意事项
- 不要在断言实现中出现任何
throw/try/catch关键字,只要代码中存在这些关键字,开启-fno-exceptions时GCC会直接报语法错误,和分支是否实际执行无关。 - 上述实现已在GCC 10.3 arm-none-eabi工具链下,使用
-fno-exceptions -std=c++20 -O2编译选项验证通过,可直接用于裸机项目。
内容的提问来源于stack exchange,提问作者JD.
相关产品推荐
相关产品推荐

