Alright, 1 week later and no one saved me from having to dive into the source code to find out what I was missing . But I did figure out the problem
!
Turns out, it was a pretty simple thing I was missing. In order for the engine to detect if a non-property member (i.e. member variables that are not decorated with the UPROPERTY
macro) of a struct decorated with the USTRUCT
macro that you want to be serialized via a custom serializer (for example, because that struct is being used as a property in some other object) has changed (and therefore it needs to be serialized), you need to provide a comparison function and notify the engine of that comparison function’s existence.
That comparison function can either be an operator==
member function or an Identical(const T* Other, uint32 PortFlags)
member function in the struct class. Once you have one of these, you can inform the engine of its existence by adding WithIdenticalViaEquality = true
or WithIdentical = true
(respectively) to your TStructOpsTypeTraits
class (alongside the WithSerializer = true
you added to inform the engine of your custom serialization function’s existence).
For the example project I posted, this means making the following modifications:
- In our definition of
FStructTest
, we can simply add a default comparison operator like this:
USTRUCT( )
struct FStructTest
{
GENERATED_BODY( )
public:
FStructTest( ) = default;
FStructTest( uint8 unValue )
{
m_oCustomSerializedMember = TTemplateType{ unValue };
}
// !! Add this function !!
bool operator==( const FStructTest& rOtherStruct ) const = default;
// !! Add this function !!
bool Serialize( FArchive& Ar )
{
Ar << m_oCustomSerializedMember;
return true;
};
TTemplateType<uint8> m_oCustomSerializedMember;
};
In order to support this default comparison operator, we also add a default comparison operator to TTemplateType
’s definition, like this:
template<typename T>
struct TTemplateType
{
constexpr TTemplateType& operator=( const TTemplateType& rOther ) = default;
// !! Add this function !!
bool operator==( const TTemplateType<T>& rOtherStruct ) const = default;
// !! Add this function !!
T m_tTemplatedMember;
friend FArchive& operator<< <>( FArchive& rArchive, TTemplateType<T>& rBitSet );
};
Then we notify the engine of FStructTest
’s new comparison operator by modifying our TStructOpsTypeTraits
specialization to set WithIdenticalViaEquality = true
, like this:
template<> struct TStructOpsTypeTraits<FStructTest> : public TStructOpsTypeTraitsBase2<FStructTest>
{
enum
{
WithSerializer = true,
// !! Add this line !!
WithIdenticalViaEquality = true
// !! Add this line !!
};
};
And that’s it! Our custom property now properly saves and loads when you make changes to it in the details panel.