Search code examples
c++reflectionpolymorphismunreal-engine4

How to safely downcast USTRUCTs


Unreal Engine supports downcasting of UObject* via Cast<T> and via a custom implementation of dynamic_cast<T*>.

When downcasting USTRUCT objects, both methods don't work:

  1. The methods of Cast<T> do not support conversions to UScriptStructs.
  2. UE4 compiles without RTTI (e.g. /GR- is set for cl.exe) and UE4 uses the dynamic_cast<T*> of the implementation for pointers to USTRUCTs. Therefore the compiler throws C4541 (see example below).

Is there a method in UE4.22 to safely downcast USTRUCTs using the reflection system of UE4 (so when static_cast<T*> etc is not an option)?

If not, then why UE4 doesn't support downcasting USTRUCTs by its Cast functions? E.g. shouldn't they be referenced or are the reasons Blueprint related?


Example for (2), use within an UE4 project:

#pragma once
#include "CoreMinimal.h"

USTRUCT()
struct MyStructBase
{
    virtual ~MyStructBase() = default;
};

USTRUCT()
struct MyStructDerived : public MyStructBase
{};

void TestFunc()
{
    auto lvalue = MyStructBase{};
    auto lvaluePtr = &lvalue;
    auto o = dynamic_cast<MyStructDerived*>(lvaluePtr); // cl.exe throws C4541
}

Solution

  • Is there a method in UE4.22 to safely downcast USTRUCTs using the reflection system of UE4 (so when static_cast<T*> etc is not an option)?

    I know it's not an ideal solution and might not suit your case, but if you don't want to use a bare static_cast, you could provide a templated function that does the cast and add a static_assert with TIsDerivedFrom inside to get a compile time error were the cast to fail.

    template<typename T, typename U>
    T* CastStruct(U* base)
    {
        static_assert(TIsDerivedFrom<T, U>::IsDerived, "Type T has to be derived from U.");
        return static_cast<T*>(base);
    }
    

    why UE4 doesn't support downcasting USTRUCTs by its Cast functions?

    That's because structures are supposed to be lightweight entities in Unreal Engine and thus reflection - which is required for Cast to work - is not provided provided in a minimal form for them.

    Be careful when using pointers to USTRUCTs as they're not only unsupported by reflection, but also garbage collector, serializers, UI etc. You have to know what you're doing when dealing with them.