You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

-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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 19:48:22