What's the point in using check() right before operator->?

Hi,

check()` is used to crash as soon as possible, to easier track down a bug. However, I often see this in tutorials and elsewhere:

AFoo* Foo = GetFoo<AFoo>(); 
check(Foo); 
Foo ->DoSomething();

Why bothering with adding it to the next line, when exactly the same line would otherwise be nullptr->DoSomething() and produce something like:


Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x000000000000006d

UnrealEditor_Engine!AActor::Destroy() [D:\build\++UE5\Sync\Engine\Source\Runtime\Engine\Private\Actor.cpp:4736]

UnrealEditor_Aura!AMyCharacter::AMyCharacter() [D:\Code\Unreal\_Projects\Foo\Source\MyCharacter.cpp:11]

Compared to, when using check():

Assertion failed: foo [File:D:\Code\Unreal\_Projects\Aura\Source\Aura\Private\Character\AuraCharacterBase.cpp] [Line: 10]

Yes, it looks a bit clearner. IMHO the programmer knows immediately when looking at the first message, that this is a nullptr exception becoming very obvious when going to MyCharacter.cpp:11:

AActor* foo = GetOwner<AActor>();
foo->Destroy();

You have this logic everywhere:

  1. Get something, that might be a nullptr
  2. use it on the next line.

By UE Convention/Documentation, you should be aware that these functions might return a nullptr. I’d rather put check() where things aren’t that obvious. What do you think?

Kind Regards

check() is an assertion, a way to enforce a condition be true or cause the application to stop. I personally use checkf() which allows for a custom message to be posted along with the application halt.

For your question, the pointer is checked, so if it is an invalid pointer, the application should halt. So unless you’re testing in a shipping build, ->DoSomething() should not fire. If Foo pointer is valid, and still crashes when invoking the function, the issue is inside the function. So open up the function DoSomething() and add some checks in there too.

Asserts are powerful in enforcing expected data is valid. There is no ambiguity, the condition fails when you expect some thing to be true. Developers must fix this issue to proceed. This will help you avoid a lot of headaches later on by using them as much as possible.

Please check out the documentation provided by Epic, it’s very helpful. Asserts in Unreal Engine | Unreal Engine 5.3 Documentation

Hi ZeroTheHero,

thank you for your answer. However, I know it’s an assertion, it crashes the application and there is checkf() to output a custom message.

My question is: Why would I use check() here?

I edited the original question and gave more details.

When there is a check on a variable you know that whoever wrote this never expected it to be invalid and in the case that it is then the issue is elsewhere.

If you don’t see a check before a variable then you don’t know if whoever wrote this expected it to always be valid or forgot to check if it was valid.

1 Like

In this case I know immediately that the pointer must be valid, because dereferencing a nullptr is always going to crash:

  1. Get a pointer
  2. operator->() in the very next line

Maybe I can answer my own question as follows:

Code A:

AActor* foo = GetOwner<AActor>();
foo->DoSomething();

Code B:

AActor* foo = GetOwner<AActor>();
check(foo)
foo->DoSomething();

Even though in the case of A vs. B check() it is not really helpful in finding the cause of the crash (it’s very obvious in both cases). Code can change to:

Code C (foo not directly derefenced after aquisition):

AActor* foo = GetOwner<AActor>();
check(foo)
// do more complicated stuff 
foo->DoSomething();

Code D (intentionally no check, because nullptr is handled without crash):

AActor* foo = GetOwner<AActor>();
if(foo)
{
   foo->DoSomething();
}
else 
{
  // ...
}

tl;dr:
Even though it’s obvious in the given example, it’s good to use check() here because coders intentions are a more explicit enforced documentation that this should never be a nullptr.

P.S. I saw check() used many times exactly this way in the LyraStarterGame.

1 Like