How to cast TArray to another type

Hello :slight_smile:

I want to cast TArray< AActor* > to TArray< ABatteryPickup* > is this possible?
i tried to to

CollectedActors = Cast<TArray< ABatteryPickup* >>(CollectedActors); // this gives an error :(

TArray<ABatteryPickup*>Batteries{ CollectedActors.begin(), CollectedActors.end() }; // no begin and end iterators

 TArray<ABatteryPickup*>Batteries = std::move(CollectedActors); // this seems wrong to me because it doesnt use ue4 libs and i think its just wrong ;)

What i am looking for is a elegant solution to this problem

//  Get all overlapping actors of type ABatteryPickup and work with them
TArray<AActor*>CollectedActors;
CollectionSphere->GetOverlappingActors(CollectedActors, ABatteryPickup::StaticClass());

for (auto batteryPickup : CollectedActors)
{
	//if (!batteryPickup->IsPendingKill && batteryPickup->bIsActive)
}

If you’re already looping, why not just do the cast in the loop body rather than try and convert the whole array?

for(AActor* Actor : CollectedActors)
{
	ABatteryPickup* batteryPickup = Cast<ABatteryPickup>(Actor);
	/** Your code */
}

If you really want to cast the array, you could probably abuse the fact that all pointers are the same size, and reinterpret_cast it (although I don’t really recommend this):

TArray<ABatteryPickup*>& CastedArray = reinterpret_cast<TArray<ABatteryPickup*>&>(CollectedActors);

Hi Jamie,

your solution works. I personally find that casting the array once to the type i am working with clearer. I find that casting things in for loops is not very elegant. But i will adapt to this style if this is the way to go. :slight_smile:

thanks for the fast answer :slight_smile:
danbo

uff, then i think there is a reason why Cast(…) from ue4 exists :slight_smile:
i think i stick to your first answer :slight_smile: its pretty straight and using reinterpret cast also would require that i check against nullptr :slight_smile: and i dont like an abuse :stuck_out_tongue:

The Cast function is only for UObject types - TArray isn’t a UObject, so you shouldn’t use Cast on it.

TArray is not a standard container, so doesn’t have begin/end member functions. It does have begin/end non-member functions so that it can be used in ranged-for loops (e.g. for (auto Actor : CollectedActors)), but these are not intended to be called directly.

std::move is fine, but the UE equivalent is MoveTemp. However, it wouldn’t work in either case because you can’t move between container types of different types.

The reinterpret_cast method wouldn’t require that you check against nullptr, unless the array already contains nullptrs, which it shouldn’t do in your example case.

Fundamentally, TArray (and in general, C++) does not support type covariance. C++ does a limited form support return type covariance of overridden virtual functions, but that’s not applicable here.

I’d recommend going with what Jamie wrote except that I’d do this:

for(AActor* Actor : CollectedActors)
{
    ABatteryPickup* batteryPickup = static_cast<ABatteryPickup*>(Actor);
    /** Your code */
}

… because the GetOverlappingActors function guarantees that the all of the Actors returned are of the right type, so you don’t need the safety (slowness) of Cast.

Cast makes it safer for code maintenance, because if the surrounding code evolves such that non-ABatteryPickup objects somehow get added to your array then you will get null pointers instead of illegal pointers. However, if that is a future concern then you should add a null check too:

for(AActor* Actor : CollectedActors)
{
    ABatteryPickup* batteryPickup = Cast<ABatteryPickup>(Actor);
    if (batteryPickup)
    {
        /** Your code */
    }
}

Steve

2 Likes

Wow Steve. A hell of an explanation :wink:
Well i often heard that it’s good to use references. My actual goal was to get all the overlapping actors, convert them to references instead of pointers which i hoped would safe me from checking for null. I’m new to game programming and i have no background in that. Its said that use pointers when objects can be null.
but couldn’t i use references instead and destroy my objects when i dont need them anymore? Maybe this sounds a bit silly… i just try to figure out what is the easiest way for me to cope with all the new shiny things :smiley:
Thanks for the great explanation :slight_smile: and for the quick response :slight_smile:
danbo

Just in case if someone stumbles on this old post and decides to do this:

This kind of a reinterpret_cast of pointers to polymorphic objects would lead to corrupt pointers, if I understand correctly. Doesn’t proper casting of such pointers require a slight adjustment of the pointers, which would not occur without a proper dynamic_cast?

Potentially, in the case of multiple inheritance. Casting the array itself is pretty evil, particularly if dealing with non-POD types.

Kinda necroing this thread, however, I think I found a good way of doing this using a template function:

template<typename To, typename From>
FORCEINLINE TArray<To*> CastArray(TArray<From*> InArray)
{
	TArray<To*> OutArray;
OutArray.Reserve(InArray.Num());

	for (auto& Elem : InArray)
	{
		To* ThisCastedObject = Cast<To>(Elem);
		if (ThisCastedObject)
		{
			OutArray.Emplace(ThisCastedObject);
		}
	}
	return OutArray;
}

Located in the header file of whatever class you want to use it from. It’s basically the same as a normal Cast…

It’s my first time using template functions, so maybe I am doing it wrong haha, hopefully this helps someone.

2 Likes

You method is the safestest possible, thing is it’s not “same as a normal Cast” because you latterly allocating new space in memory and copy entire array element by element, while you can copy memory more efficiently in C++, but most impotently casting should not copy any data, casting only changes how data is treated and raw cast don’t take any CPU instructions, it only functions as a information for compiler how deal with the data. Proper solution would need to be implemented in TArray itself to be more optimal

Ah okay, I had no idea that’s how it works.
Actually, now that I think of it, usually when you cast, don’t you allocate space for a new pointer for the cast to place the casted pointer in? Or does the compiler optimize that away?

Just needed that myself and found a solution. Though, only use it if you know whats going on!
Types must be of the same size to prevent memory issues.

	check(sizeof(aiVector3D) == sizeof(FVector));
	TArray<FVector> vertices;
	TArray<aiVector3D> target;
	target = MoveTemp(*reinterpret_cast<TArray<aiVector3D>*>(&vertices));
	check(vertices.Num() == 0); // just to check that the move worked
1 Like

Another trick, you can also use a blueprint function library for this.

.h

UFUNCTION(BlueprintPure, Category = "Utilities", meta = (ArrayTypeDependentParams="OutArr"))
static void ArrayCast(TArray<UObject*> InArr, TArray<UObject*>& OutArr);

.cpp

void UMyBlueprintFunctionLibrary::ArrayCast(TArray<UObject*> InArr, TArray<UObject*>& OutArr)
{
	OutArr = InArr;
}

In the blueprint, connect the source array to the function, and the function’s output to a Set Variable / Set Local Variable node of the desired type.

EDIT - Nevermind, the above code works in editor but fails when building (packaging) the game. If anyone know an easy fix to this, let us know!