I’m still new to C++ and Unreal but I come from a C# background. I’m trying to implement 2 functions to serialize and deserialize a TMap<FString,FVariant> into a TArray and back.
The problem is I get this compile error when I attempt to build:
|Error||Unrecognized type ‘FVariant’ - type must be a UCLASS, USTRUCT, UENUM, or global delegate
This is in my .h file where I am defining these functions
UFUNCTION(BlueprintCallable, Category = “Conversion Helpers”)
static TMap<FString, FVariant> DeserializeByteData(TArray byteData);
UFUNCTION(BlueprintCallable, Category = "Conversion Helpers")
static TArray<uint8> SerializeMapData(TMap<FString, FVariant> mapData);
Is FVariant even usable in this way? Essentially I want to make a function that I can call in a blueprint that will handle converting a TMap<FString, AnyDataType> to a byte array, after some research I found out about FVariant and thought it was exactly what I needed, but for some reason I can’t compile this code. What am I doing wrong and is there possibly another way to achieve this?
You cannot use FVariant
in a UFUNCTION
or UPROPERTY
context because that class hasn’t been exposed to Unreals reflection system by either UCLASS
or USTRUCT
.
You can create your own wrapper USTRUCT
but you’d have to write additional functions for working with the wrapped FVariant in Blueprint (e.g. custom setter, getter and make functions). Edit: Forgot to mention there’s also the StructUtils experimental plugin that already works similar to this.
Of course you could also extend/modify the engine to add support for actual TVariant<>
properties but that’s a much bigger task.
Edit: Ignore the following, I somehow missed that you wouldn’t be able to have different types for each of the map values. I’ll leave it there for now so you may get an idea what can be done with wildcards. The process for creating the functions to work with an FVariant wrapper USTRUCT
would be very similar to that.
You could instead create the functions with a wildcard parameter and then use the property system to serialize/deserialize.
// disclaimer: untested, uncompiled code
UFUNCTION(BlueprintCallable, CustomThunk, meta=(MapParam = "MapData"), Category = "Conversion Helpers")
static TArray<uint8> SerializeMapData(TMap<FString, int32>& MapData);
DECLARE_FUNCTION(execSerializeMapData)
{
Stack.MostRecentProperty = nullptr;
Stack.StepCompiledIn<FMapProperty>(nullptr);
void* MapAddr = Stack.MostRecentPropertyAddress;
const FMapProperty* const MapProperty = CastField<FMapProperty>(Stack.MostRecentProperty);
if (!MapProperty || !CastField<FStrProperty>(MapProperty->KeyProp))
{
Stack.bArrayContextFailed = true;
return;
}
P_FINISH;
P_NATIVE_BEGIN;
if(MapAddr)
{
FMemoryWriter Writer(*(TArray<uint8>*)RESULT_PARAM, true);
FScriptMapHelper MapHelper(MapProperty, MapAddr);
int32 Size = MapHelper.Num();
for(int32 Index = 0; Size; ++Index)
{
if(MapHelper.IsValidIndex(Index))
{
// TODO: also serialize the key string?
MapProperty->ValueProp->SerializeItem(FStructuredArchiveFromArchive(Writer).GetSlot(), MapHelper.GetValuePtr(Index), nullptr);
--Size;
}
}
}
P_NATIVE_END
}
1 Like