# 断言
此文为Assertions (opens new window)的原创翻译,本文内容版权归原文所有,仅供学习,如需转载望注本文地址,翻译不易,谢谢理解。
UE4中断言是一种工具,用于确认一段给定代码所依赖的假设,假如运行一段逻辑前必须要确保某个变量不为空,如果该变量为空则停止执行或给出提示消息。
Assertions是用来验证一段代码其所基于前提的工具。这可能同验证一个指针是否为NULL一样简单,也可能同验证一个特定函数是否能用一样复杂,UE4提供了一系列宏来执行这些验证,作为宏它们可以在一些特定配置中不编译,以便因为性能或最终构建版本不需要它们时就不编译它们。如果你想去看下这些宏,可以在这个地方找到它们:
/UE4/Engine/Source/Runtime/Core/Public/Misc/AssertionMacros.h
这些运行时断言宏分成三类:中断执行,调试模式下中断执行,报告错误但不中断执行。第一和第三种类型在DO_CHECK定义时被编译,第二种类型在DO_GUARD_SLOW有定义时被编译。如果DO_CHECK和DO_GUARD_SLOW被定义时设置为0,这些宏将被禁用而不会影响执行。
# 中断执行
现在来看下第一类宏,这些宏将在断言失败时终止执行,如果你是在一个调试器内运行,断言将会导致断点,这样你就可以检查导致失败的原因。
# check
check(expression);
这个宏先执行表达式,如果表达式为false,中断执行。这个表达式只有在该宏编译进源码时执行,也就是DO_CHECK=1的时候,下面是check()宏最简单的形式:
check(Mesh != nullptr);
check(bWasInitialized && "Did you forget to call Init()?");
2
# verify
verify(expression);
在DO_CHECK被启用了,这个宏跟check()一样,但是当DO_CHECK被禁用了,这个表达式仍旧被执行。你可以使用它来检查变量值是否符合你的假设:
verify((Mesh = GetRenderMesh()) != nullptr);
# checkf
checkf(expression, ...);
该宏允许打印一些额外的信息以便帮助调试,同check()能编译时行为一样。
checkf(WasDestroyed, TEXT( "Failed to destroy Actor %s (%s)"), *Actor->GetClass()->GetName(), *Actor->GetActorLabel());
checkf( TCString<ANSICHAR>::Strlen( Key ) >= KEYLENGTH( AES_KEYBITS ), TEXT( "AES_KEY needs to be at least %d characters" ), KEYLENGTH( AES_KEYBITS ) );
2
# verifyf
verifyf(expression, ...);
就像verify()一样,同样它在执行时可以打印额外的调试信息,就像checkf()一样。
verifyf(Module_libeay32, TEXT("Failed to load DLL %s"), *DLLToLoad);
# checkCode
checkCode(expression);
这个宏比check()有点小复杂,它在do/while循环中执行一次表达式,这个宏被放置在do/while括号中,它虽然在引擎中不常使用,一旦你需要它就会发现它很有用。就像check()宏一样,在DO_CHECK被禁用时不被编译。因为这不要使用有副作用效果的表达式,因为在DO_CHECK被禁用时它会被移除。
checkCode( if( Object->HasAnyFlags( RF_PendingKill ) ) { UE_LOG(LogUObjectGlobals, Fatal, TEXT("Object %s is part of root set though has been marked RF_PendingKill!"), *Object->GetFullName() ); } );
# checkNoEntry
checkNoEntry();
这个宏不接收表达式用来标记永不会执行的代码。
switch (MyEnum)
{
case MyEnumValue:
break;
default:
checkNoEntry();
break;
}
2
3
4
5
6
7
8
# checkNoReentry
checkNoReentry();
该宏被用来阻止一个给定函数重复被调用,为那些只应该被调用一次的函数使用它。
void NoReentry()
{
checkNoReentry();
}
2
3
4
# checkNoRecursion
checkNoRecursion();
同checkNoReentry执行一样的检查,它的名字更明白地表明了它的意图。
int32 Recurse(int32 A, int32 B)
{
checkNoRecursion();
return Recurse(A - 1, B - 1);
}
2
3
4
5
# unimplemented
unimplemented();
第一类中的最后一个宏,只在DO_CHECK启用时可用,它用来标记一个函数可以被覆写,或者不应该被某些类被调用因为这个函数还没有被实现。
class FNoImpl
{
virtual void DoStuff()
{
// You must override this
unimplemented();
}
};
2
3
4
5
6
7
8
# 调试时中断执行
第二类断言宏只在DO_GUARD_SLOW被启用时执行。DO_GUARD_SLOW只为调试模式的构建启用,你可以为你的项目改变这点。这些断言可能会慢点,在开发或者发布版本时可能不需要这些过于繁琐的检查。这些宏执行的和它们对应较快版本所执行的是一样的。这些宏是checkSlow(),checkfSlow(),和verifySlow()。
checkSlow(List->HasCycle());
checkfSlow(Class->IsA(AActor::StaticClass()), TEXT("Class (%s) is wrong type"), Class->GetName());
verifySlow(LastValue == Current);
2
3
# 报告错误但不中断执行
最后一类运行时断言并不中断执行,相反它们被用来创建调用栈报告来帮助追踪问题。这些宏里的表达式常被放在条件判断中,它们只有在DO_CHECK激活时才能被使用。
# ensure
ensure(expression);
验证表达式,查看是否生成到当前点的调用堆栈。
if (ensure( InObject != NULL ))
{
InObject->Modify();
}
2
3
4
# ensureMsg
ensureMsg(expression, message);
验证表达式,然后使用额外的信息生成调用栈来作为报告的一部分。
ensureMsg(Node != nullptr, TEXT("Node is invalid"));
# ensureMsgf
ensureMsgf(expression, message, ...);
验证表达式,使用包含更多信息的调用栈来生成报告。就像checkf()和verifyf(),你可以包含上下文信息来帮助追踪问题。
if (ensureMsgf(!bModal, TEXT("Could not create dialog because modal is set to (%d)"), int32(bModal)))
{
...
}
2
3
4
← UE4字符串调试日志 UE4中控制台变量 →