Published on

UE 中的 断言宏 Assertion Macros

Authors
  • avatar
    Name
    Zihan Li
    Twitter

在C和C++编程中,assert 可在开发期间帮助检测和诊断不正常或无效的运行时条件。这些条件通常检查是否指针为非空、除数为非零、函数并非递归运行,或代码要求的其他重要假设。但每次检查会使得效率十分低下。某些情况下,assert 会在延迟崩溃发生之前发现导致该崩溃的bug,例如删除未来tick所需的对象,协助开发人员发现引起崩溃的根本原因。assert 的关键特性之一是不存在于发布代码中,这意味着不但不会影响发布产品的性能,也没有任何副作用。对 assert 最简单的理解就是:"断言"必须一律为true,否则程序会停止运行。


1. check() 宏

  • 用途
    check() 用于验证某个条件是否为真。如果条件为假,程序会立即中断运行,并输出错误信息(包括文件名和行号),以便开发者快速发现问题。这通常用于那些“绝不应该发生”的错误情况。
  • 示例
    check(MyValue == 1); // 如果 MyValue 不等于1,则程序会在这里中断
    
  • 特点
    • 在开发和调试阶段非常有用,因为它可以使程序在出错时立即停止,防止错误传播。
    • 在最终发布的版本中,通常会将这些断言宏移除或禁用,以免对性能产生影响。

2. checkf() 宏

  • 用途
    checkf()check() 类似,但允许开发者提供格式化的错误信息。这在复杂条件下有助于输出更详细的上下文信息。
  • 示例
    checkf(MyPointer != nullptr, TEXT("MyPointer should not be null!"));
    
  • 特点
    • 格式化字符串可以帮助定位问题的具体原因和位置。

3. ensure() 宏

  • 用途
    ensure() 用于检测条件是否为真,但与 check() 不同的是,当条件不满足时,它不会立即中断程序的执行,而是记录一条警告信息。这使得程序可以在检测到问题时继续运行,适用于那些虽然不理想但不致命的情况。
  • 示例
    if (ensure(MyActorPointer)) {
        // 即使 MyActorPointer 为 null,程序依然会继续执行,但会在日志中记录警告信息
        MyActorPointer->DoSomething();
    }
    
  • 特点
    • 适合用于可能出现错误但不会导致程序崩溃的场景。
    • 可以帮助开发者在运行时观察潜在问题,而不必中断整个应用程序【citeturn0search0】。

4. ensureAlways() 宏

  • 用途
    ensure() 类似,但在同一条件多次不满足的情况下,每次都会记录警告,而不会像 ensure() 那样只触发一次。这在需要持续监控某个条件的场景下非常有用。

5. 为什么使用断言宏?

  • 调试定位问题
    断言宏能在程序运行时立即捕捉到逻辑错误和不符合预期的状态,帮助开发者尽早发现问题并调试。
  • 开发安全性
    在开发过程中,如果某个关键条件不满足,断言会阻止程序继续运行,防止错误扩散并导致更复杂的问题。
  • 性能影响
    这些断言在开发版中非常有用,但在最终产品中通常会被禁用或移除,从而确保不会对性能产生影响。

总结

在UE中,常见的断言宏如 check()checkf()ensure()ensureAlways(),各有侧重:

  • check()/checkf():用于那些“绝不允许出错”的情况,一旦条件不满足,立即中断程序。
  • ensure() / ensureAlways():用于检测非致命错误,记录警告后允许程序继续运行,便于在不中断程序的情况下进行问题追踪。