Casting UStruct to derrived struct

I’m trying to implement a method where I pass along a base struct, which might be of type ‘derrived struct’.

However, when casting, I’m getting the following exception:



'To *TCastImpl<From,To,ECastType::UObjectToUObject>::DoCast(UObject *)': cannot convert argument 1 from 'From *' to 'UObject *'


To give you some basic information, here are my struct definitions:

Please be aware, these are just test structs, so the derrived struct doesn’t even have extra parameters, the message struct doesn’t even have message text.

FMessageStructBase:



#pragma once

#include "CoreMinimal.h"
#include "MessageStruct.generated.h"

USTRUCT()
struct FMessageStructBase
{
    GENERATED_BODY()

public:
    UPROPERTY()
    int Id;
};


FMessageStructDerrived:



#pragma once

#include "CoreMinimal.h"
#include "MessageStruct.h"
#include "MessageStructDerrived.generated.h"

USTRUCT()
struct FMessageStructDerrived : public FMessageStructBase
{
    GENERATED_BODY()

public:
};


The following code all contains the required include files for the structs in the cpp file:



#include "MessageStruct.h"
#include "MessageStructDerrived.h"


The code I use to test the casting is the following:



FMessageStructDerrived messageStruct;
FMessageStructBase* messageStructBase = &messageStruct;
FMessageStructDerrived* derrived = Cast<FMessageStructDerrived>(messageStructBase);


A “fix” I found, is to use static_cast instead, which compiles, and the cast succeeds. However, in the case of the message not being of the type FMessageStructDerrived, it will crash, since it’s not a safe cast:



FMessageStructDerrived* derrived = static_cast<FMessageStructDerrived*>(messageStructBase);


Luckily, I can of course use a dynamic_cast! … Oh. Wait. No I can’t, because if I use the following:



FMessageStructDerrived* derrived = dynamic_cast<FMessageStructDerrived*>(messageStructBase);


It will give me the following error:



'dynamic_cast': 'FMessageStructBase' is not a polymorphic type


I hope someone can help me figure out how I can properly cast these UStructs.

Thanks in advance!

You can’t use “normal” casts for UObjects. You need to use the Cast methods you were originally using.

You can either check for null after you do the Cast (to prevent accessing a failed cast), or do a IsChildOf check before the cast (which may be a bit redundant).




if (FMessageStructDerived* derivedStruct = Cast<FMessageStructDerived>(&myStruct))
{
   // Cast was successful, continue on.
}

// Same logic as above, but using IsChildOf
if (myStruct.IsChildOf<FMessageStructDerived>())
{
    FMessageStructDerived* derivedStruct = CastChecked<FMessageStructDerived>(&myStruct); // CastChecked will crash on a failed cast, but our IsChildOf guarantees that our cast will be safe.
}



^ the above will not work, since these are ustructs, not uobjects

There is no dynamic cast for ustructs in unreal. Basically, you’re not supposed to use them this way.

If the goal is to expose this to blueprint, you also won’t be able to pass a FMessageStructDerived to a blueprint method expecting a FMessageStructBase. If it isn’t, I guess you could do some “manual” runtime type checking by adding a EMessageStructType to each and checking that, but it’s pretty ugly. You could also use uobjects instead, but that has a pretty huge overhead for low level things.

The best way would be to design the system in a way that it does not need dynamic casts with simple structs at all.

polymorphic type means you need at least a virtual destructor.

It’s annoying why you can’t cast UStructs like this and even worse, if you try to get the StaticStruct of a Struct Pointer, it will always return the StaticStruct of the base pointer type.

For example:

FActorRecord Record = FActorRecord();
Record::StaticStruct() // will be FActorRecord (good).

FRecord* RecordPtr = &Record;

RecordPtr::StaticStruct() // will be FRecord (bad).

The best solution I have come up with is to add a UStruct* ActualStaticStruct variable in your base type (in my case FRecord) and initialise this variable for every derived type by doing ActualStaticStruct = StaticStruct() in every constructor.

I’m not entirely sure what the built-in version of StaticStruct() does but it’s definitely undesirable.

Your example doesn’t make any sense. StaticStruct is a static function and isn’t something that you should call on an instance like that. You’d call it on FActorRecord or FRecord instead which would always give you the right thing. I’m surprised the syntax even works.

The same is true of UObjects and calling StaticClass. The only difference is that UObjects also have a member function that you can call on an instance, GetClass, which returns the actual type. In fact, if you called StaticClass on UObject instances like you are with your example you’ll get the “wrong” behavior there too (because that’s not how StaticClass or StaticStruct are supposed to work).

But all of this is sort of moot since you’re not supposed to use structs polymorphically in Unreal.

Right. Got it. Understood now.

I know you’re not supposed to do it but I need UPROPERTY serialization and I don’t want to be making UObjects as I’ll be creating hundreds of these structs at once.

If you’re on latest, you can look at InstancedStructs (StructUtils.h). They’re sort of like Instanced Objects but structures. They’re used to do some quasi-polymorphism with structures but only in native.

Thanks for that. I never knew about this. I’m using native so this is great.

I found that in source. Epics use for that c-style cast

An important thing to note here is this C-style cast is wrapped in an if statement that makes the cast safe.
It’s using the FDamageEvent’s virtual function IsOfType(), comparing its static int FDamageEvent::ClassID against the inputted FPointDamageEvent::ClassID.

This means the type has been validated by this point, and you are fine to go ahead with the C-style cast, conveying the broader intention that in these scenarios Epic wants you to add your own type information to structs, to substitute the disabling of RTTI.