해당 문서는 언리얼 엔진의 공식 문서 "언리얼 엔진의 어서트 | 언리얼 엔진 5.4 문서 | Epic Developer Community (epicgames.com)"를 읽고 제가 이해한 바를 조금 덧붙여서 정리한 글입니다. 오류가 있다면 지적해주시면 감사하겠습니다.
프로그래밍을 할 때 반드시 null이 아니어야 한다던가, 이런 걸 개발 과정에서 테스트할 땐 Assert 문을 많이 사용한다. Log처럼 알림을 띄울 뿐만 아니라 아예 동작을 멈춰버리게 해서 오류를 강력하게 알려주기 때문이다.
언리얼 엔진에서 무언가를 검증할 땐 check와 ensure 같은 Assert 매크로를 사용한다.
언리얼 엔진에서 제공하는 Assert 매크로는 종류가 다양한데, check, verify, checkf, ensure 등 종류가 다양하다.
이를 간단하게 정리하면,
(1) check, verify 외 일반 Assert 시리즈: 실패하면 아예 실행을 중단해버린다.
(2) checkSlow, verifySlow 시리즈: 1과 같은데 디버그 빌드용
(3) ensure 시리즈: 실패해도 실행은 계속된다. 대신 도움이 되는 콜스택을 띄운다.
이렇게 정리할 수 있다. 이에 대해 좀 더 자세히 살펴보자.
Assert (1): 표현식이 안 맞으면 실행 중단
이 파트는 표현식이 false면 아예 실행을 중단해버린다. 그런 점에서 강력한 편이다.
대부분 DO_CHECK를 확인한다.
check(표현식)
: 표현식이 false이면 실행 중단.- 단, DO_CHECK = 1일 때만, 즉 매크로가 빌드 과정에서 컴파일될 때만 실행된다.
check(Mesh != nullptr);
check(bWasInitialized && "Did you forget to call Init()?");
verify(표현식)
: check와 똑같은데, DO_CHECK가 꺼져 있어도 실행을 중단한다. 플레이 중 변수 할당이 제대로 이뤄졌는지 확인하기 좋다.
verify((Mesh = GetRenderMesh()) != nullptr);
checkf(표현식, …)
: 표현식이 true가 아니면 디버깅에 도움이 되는 정보(디버그 메시지)를 제공하면서 실행을 중단한다. 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 ) );
verifyf(표현식, …)
: checkf와 유사하지만, verify처럼 언제나 실행된다.
checkCode(표현식)
: check보다 좀 더 복잡함. 한 번 실행되는 do/while 루프 안에서 표현식을 실행한다.- 실제 구현은 다음과 같다. 엔진에서 잘 사용하지는 않고, DO_CHECK가 꺼져 있으면 컴파일에서 제외됨.
- 코드를 한 번 실행해서 확인한다는 목적이라 보면 되겠다.
#ifndef checkCode
#define checkCode( Code ) do { Code; } while ( false );
checkNoEntry()
: 표현식이 없다. 절대 실행될 일 없는 곳에다가 넣는다.
switch (MyEnum)
{
case MyEnumValue:
break;
default:
checkNoEntry();
break;
}
checkNoReentry()
: 호출하고 있는 함수에 다시 진입하는 경우를 막는다.- 로딩을 하고 있는데 다시 로딩 함수를 호출하는 것을 막는 경우가 대표적일 것 같다.
- 실제 구현은 이렇다. 해당 블록을 실행하는데 또 하는 경우를 막는다.
#ifndef checkNoReentry
#define checkNoReentry() { static bool s_beenHere##__LINE__ = false; \
check( !"Enclosing block was called more than once" || !s_beenHere##__LINE__ ); \
s_beenHere##__LINE__ = true; }
#endif
checkNoRecursion()
: checkNoReentry()와 동일하다. 이름을 명시해서 재귀를 막을 목적임을 알리는 용도이다.unimplemented()
: 함수에 구현이 없어서 특정 클래스에서 호출하면 안되거나 덮어써야 하는 함수를 표시할 때 사용한다.- 함수를 구현하긴 해야 하는데 내용은 없고, 자식 클래스에서 덮어씌울 때 사용한다고 보면 되겠다.
class FNoImpl
{
virtual void DoStuff()
{
// You must override this
unimplemented();
}
};
Assert (2): 디버그 빌드에서만 확인
디버그 빌드에서만 확인하는 용도이다. 그래서 DO_CHECK 말고 DO_GUARD_SLOW를 확인한다는 특징이 있다.
checkSlow(), checkfSlow(), verifySlow()
: check, checkf, verify와 같지만 DO_CHECK가 아니라 DO_GUARD_SLOW가 켜졌을 때만 실행된다.
DO_GUARD_SLOW는 보통 디버그 빌드에만 켠다(프로젝트에서 직접 바꿀 수 있긴 하다).
위 코드는 느리면서 규칙을 꼼꼼하게 따질 것으로 보이는 코드에 넣는다고 가정한다.
그래서 DO_GUARD_SLOW는 Development나 Shipping 빌드에서는 굳이 안 쓰고 디버그 빌드에서만 켜진다.
Assert (3): 실행은 중단시키지 않지만 콜스택을 생성
세 번째 부류는 표현식이 실패(false)한다고 해서 아예 프로그램을 중단하진 않는다. 대신 그에 대한 콜스택을 만들어준다. 이름은 ensure로 시작한다.
이 매크로 표현식은 항상 실행되며 조건 식 안에도 넣을 수 있어서 특이하다.
(1) 종류처럼, DO_CHECK로 켤 수 있다.
ensure(표현식)
: 표현식을 검증해서 실패하면 그 지점까지 이르는 콜스택을 생성한다.- 특이한 것이, 아래 예시를 보면 아예 if문에 조건으로 넣을 수 있다. 조건문과 역할은 같은데, 실패시 콜스택을 생성해주는 추가 기능이 있다고 이해할 수 있다.
if (ensure( InObject != NULL ))
{
InObject->Modify();
}
ensureMsg(표현식, 메시지)
: 표현식을 검증하는 건 ensure와 같은데, 실패하면 메시지를 포함해서 콜스택을 생성해준다.- 콜스택 메시지 버전이라 볼 수 있다.
ensureMsg(Node != nullptr, TEXT("Node is invalid"));
ensureMsgf(표현식, 메시지, …)
: 이것도 표현식을 검증하는 건 ensure와 같은데, 콜스텍에 메시지와 상세 정보를 포함한다.- 예시를 보면, 추적하고 싶은 변수 같은 걸 넣는 식이다. 아래 코드를 보면, 세 번째 인자로
int32(bModal)
을 넣어서 이 변수의 값을 추적한다.
- 예시를 보면, 추적하고 싶은 변수 같은 걸 넣는 식이다. 아래 코드를 보면, 세 번째 인자로
if (ensureMsgf(!bModal, TEXT("Could not create dialog because modal is set to (%d)"), int32(bModal)))
{
...
}
참고
언리얼 엔진의 어서트 | 언리얼 엔진 5.4 문서 | Epic Developer Community (epicgames.com)
'Unreal Engine 5' 카테고리의 다른 글
GetWorld와 GetLevel은 어떻게 구현되어 있을까? (0) | 2024.04.25 |
---|
댓글