Search code examples
unreal-engine4

When I should define operations-type-traits for a USTRUCT


For some USTRUCT structs within UnrealEngine, type traits TStructOpsTypeTraits<T> are defined. These provide a list of bools (encapsulated in an enumeration) about the implemented capabilities of struct T.

  1. What is the usage of those traits?
  2. When I should define those traits for my custom USTRUCTs within my project?

*Example usage from within the Engine:

struct TStructOpsTypeTraitsBase2
{
    enum
    {
        WithZeroConstructor            = false,                         // struct can be constructed as a valid object by filling its memory footprint with zeroes.
        WithNoInitConstructor          = false,                         // struct has a constructor which takes an EForceInit parameter which will force the constructor to perform initialization, where the default constructor performs 'uninitialization'.
        WithNoDestructor               = false,                         // struct will not have its destructor called when it is destroyed.
        WithCopy                       = !TIsPODType<CPPSTRUCT>::Value, // struct can be copied via its copy assignment operator.
        // ...
    }
}

Which is used like

template<>
struct TStructOpsTypeTraits<FGameplayEffectContextHandle> : public TStructOpsTypeTraitsBase2<FGameplayEffectContextHandle>
{
    enum
    {
        WithCopy = true,        // Necessary so that TSharedPtr<FGameplayEffectContext> Data is copied around
        WithNetSerializer = true,
        WithIdenticalViaEquality = true,
    };
};

It seems that traits are used for USTRUCTs that are used in blueprints; and that they are required for structs which have a NetSerialize() function. I made spot checks:

  • WithIdenticalViaEquality -> UScriptStruct::HasIdentical() -> EStructFlags::STRUCT_IdenticalNative is used only in ::IdenticalHelper() which is intended for Blueprints
  • EStructFlags::STRUCT_NetSerializeNative is used for error messages (when the structs are used in blueprints) and in FObjectReplicator and FRepLayout, where this trait is required to be present for custom property replication
  • the description of TStructOpsTypeTraitsBase2 seems to tell, that these traits are only important, when the USTRUCTs are used within blueprints

    type traits to cover the custom aspects of a script struct

UnrealEngine defines also a number of specialized traits for its container classes (e.g. TTypeTraitsBase). A comparison with c++ type_traits might be meaningful.


Solution

  • Many of the features available "out-of-the-box" in Unreal Engine 4 (e.g. replication, initialization, serialization, etc.) rely on information specific to each class. This allows different classes to - for example - customize how to serialize their own data.

    For classes inheriting from the base UObject class, all the needed information are stored into properties or returned by overridable methods. For example, if you want to customize how your UObject-derived class manages serialization, you can simply override its virtual Serialize() method. This is enough for UE4 to be able to invoke your custom implementation when it need to serialize an instance of your class.

    The problem with structs in UE4, is that they don't inherit from a common base class/interface. So UE4 doesn't have any pre-declare property or method to call. Trying to call an undeclared method will of course cause a compilation error in C++. Following the previous example on the custom serialization - UE4 can't in general invoke the Serialize() method on structs because some/most of them will not have such method and the compiler will report it as an error.

    TStructOpsTypeTraitsBase2 is the solution to the above problem. You declare a specialization of it for your custom structs to inform UE4 of which methods are available. When done, using a mix of template meta-programming and auto-generated code, UE4 will be able to call such methods to provide again out-of-the-box services or allow you to customize default behaviors. For example, declaring WithSerializer = true you're informing UE4 that your struct has a custom Serialize() method and so UE4 will be able to automatically call it every time it needs to serialize an instance of your struct.

    TStructOpsTypeTraitsBase2 is not limited to structs used with Blueprints, but is used also with USTRUCT() used in C++.

    On when to use it, you need to declare a custom specialization of TStructOpsTypeTraitsBase2 when the default behavior of UE4 on your structure is not what you want (e.g. you want to serialize its data in a different way while the default implementation serializes all the not-transient UPROPERTY() using "standard" formats).

    While the "form" is similar, the scope of TStructOpsTypeTraitsBase2 is different than C++ type_traits: type_traits is used by the compiler to inform the program/programmer about platform characteristics. TStructOpsTypeTraitsBase2 is used by the programmer to inform UE4 about available "extra" features of a custom struct.