When should I use Check()?

When should I use Check() ?

For example:

if (AMyGameMode* MyGameMode= UGameplayStatics::GetGameMode())

in this condition I use if() to check whether the cast succeeded. Can I use Check() to do the same thing? And if I can, then what’s the difference between these two kind of check? And can I put everything I wanna check in the if() and never use Check()?

Thanks!

Chenglin

2 Likes

Hi Chenglin,

check() is like assert() in C. If the condition is false, it will stop your program dead with a log message, an error window and - if a debugger is attached - a break into the debugger. You cannot continue execution after this point. It should be used to trap programmer errors that violate assumptions made by the following code - for example, indexing into a TArray with an invalid index will cause a check failure, because indexing outside of an array is always a programmer error. Additionally, the compiler can optimise code following a check() with the assumption that the condition is guaranteed true.

In contrast, if() should be used to handle conditions that are possible (no matter how unlikely) during the execution of your code.

So in the above code, you should use an if(), as null is a valid result from UGameplayStatics::GetGameMode(), e.g. you’re running on a client.

There is also the ensure() macro which is halfway between the two - i.e. you expect the condition to always be true, you want to handle it if it isn’t, but you also want to be told about it. For example:

AMyGameMode* MyGameMode = UGameplayStatics::GetGameMode();
if (ensure(MyGameMode))
{
    MyGameMode->Whatever();
}

This should be read in the same way as if (MyGameMode) except that it will log and break into the debugger if MyGameMode is null, i.e. ensure() will return the same thing (converted to bool) as it has been passed. Unlike check(), you may continue execution of your program after this break.

Each use of ensure() will also only fire once, so future failures will not keep popping up in the debugger or log. If you want a message, you can use ensureAlways().

check() and ensure() also come in checkf() and ensureMsgf() flavours which allow you to write out a printf-style helpful message to indicate the nature of the failure, e.g.:

if (ensureMsgf(MyGameMode, TEXT("Unexpected null game mode! (SomeState: %d)"), SomeState))

Hope this helps,

Steve

20 Likes

Good answer and thanks for letting me know about ensure()

Chenglin, if you want more information on the topic I’d recommend googling “C++ when to use assert” and such, since as Steve pointed out check() is like assert().

Thank you so much Steve and Henrik! The answer is very clear and now I think I need to check out the relative docs. Thanks again!

I would argue that there’s very rarely a good case for using check, ensure, or ensureMsg, except in the midst of iteration, or where it’s extremely obvious why the condition in the check should be true, because checkf and ensureMsgf exist.

Consider the difference between…

auto V = SomeContainer.Retrieve(K);
check(V && "V should not be null");

… and…

auto V = SomeContainer.Retrieve(K);
check (V, TEXT("V was null, but we expect function Foo to have already added %s to SomeContainer synchronously with the call to this function from Bar."), *V.GetName());

The whole point of doing a check is to crash the program as soon as something has gone wrong, rather than, for example, 10 frames later when the result of the flawed code is used by some other loosely related code. Moving the stacktrace closer to the code that was most responsible for instigating the failure makes things easier to track down when they do go wrong. But if your goal is to speed up diagnosis of a known potential failure case, then including a message with context is a far more effective solution.