Best way to check class type in C++

Lets take a look at the below snippet of code:

	//Check if there's a valid object to grab
	if (actorHit != NULL)
	{
		//Retain a reference to the owning character and the grabbed item.
		myOwningCharacter = Cast<ABaseCharacter>(actorThatIsGrabbing);
		myGrabbedItem = Cast<ABaseItem>(actorHit);

		if(Cast<ABaseWeapon>(myGrabbedItem) != NULL)
		{
			this->GrabWeapon(myGrabbedItem);
		}
		else
		{
			this->GrabItem(myGrabbedItem);
		}
	}

As you can see, we are checking to see if myGrabbedItem is ABaseWeapon or not. My question is “what is the best way to do this, best being most efficient, simple, and understandable”?

1 Like

Do you know if this is faster and more efficient then my solution? With this one we are calling a function to get the static class, then we are calling another function called IsA().

Not saying its wrong, I’m just not sure what is better.

Probably best to go with “IsA”

if(myGrabbedItem->IsA(ABaseWeapon::StaticClass()))
{
    this->GrabWeapon(myGrabbedItem);
}
else
{
    this->GrabItem(myGrabbedItem);
}
1 Like

Cast has a few different implementations, and which one is used depends on some engine flags, and on context. By default, for non-Editor builds, the implementation for UObjectToUObject casts (such as casting AActors) calls the templated IsA method internally, which expands to IsA(T::StaticClass()).

In this case, if you don’t need to cast to the weapon type, you’ve saved yourself the overhead of the rest of the cast implementation, as well as avoided the template expansions, by calling IsA(ClassName::StaticClass()) directly. At the same time, if you do need to perform the cast, you can then call CastChecked, which skips the IsA check internally (unless DO_CHECK is flagged on, which forces the type check regardless - by default this is set to 1 for development builds, and set to 0 [via USE_CHECKS_IN_SHIPPING] for shipping builds).

There’s also a Cast implementation that relies on an enum flag set in the source class that allows you to bypass the IsA check, but this only runs if UCLASS_FAST_ISA_IMPL is set to UCLASS_ISA_OUTERWALK; which I believe is only set for Editor builds to allow for hot reloads. Otherwise, you’re doing the IsA check in every Cast.

Regardless, unless you are performing many, many of these checks in a single frame, you’re not going to experience a performance bottleneck here.

You can see the source here:

Cast and CastChecked
…\Engine\Source\Runtime\CoreUObject\Public\Templates\Casts.h

IsA
…\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectBaseUtility.cpp

Engine flags for IsA
…\Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectMacros.h

Engine flags for Cast checks
…\Engine\Source\Runtime\Core\Public\Misc\Build.h

4 Likes

Very good and detailed explanation, very much appreciated.

In my case, my checks happen pretty often but not every single frame. I’ll make the change anyway since like you said, it uses IsA anyway in the cast function call.

Thanks again!

A useful and readable pattern for casting can be to do this:

if (ABaseWeapon* BaseWeapon = Cast<ABaseWeapon>(myGrabbedItem))
{
    // Item is a weapon
    BaseWeapon->DoWeaponThing();
}
else
{
    // Item isn't a weapon
    myGrabbedItem->DoItemThing();
}

Here, the type test and case has already been tested and cast for you, and the BaseWeapon pointer can only be used within the if/else block. This code is as efficient as you’ll get.

Hope this helps,

Steve