How to create new custom generic blueprint containers like TArray<>

Hello,

I was wondering for some time now how I could create custom UE generic types like TArray<>.
To be specific I would like to create a :

  • TOptional<> generic type to avoid sentinel values (-1, null, etc…).

TOptional seems to be doable since i don’t see a very big implementation difference with a TArray<>.

How would one start to implement this ? I remember to have read another post (here) that there would be a need for a type-erasure implementation that received UObject as arguments and that I would need to create a blueprint-compiled function / CustomThunk that uses those type-erased function ?

Do any of you have any experience or knowledge in this and direct me to a few ressources, failed/successful attempts or documentation / header files ?

Thanks in advance and have a great day !

Ps: if you also have some idea on how to implemente a TVariant<> on blueprint i am really interested but since its a templated-list of arguments i’d suppose you cant except by making a variant1, variant2, variant3, variant4, variant5, variant6, variant7, etc… implementation

EDIT 1 :
Right now i have understood how unreal engine generifies its function call but not how to create the data structure inside unreal itself
I recommend looking at Engine\Source\Runtime\Engine\Classes\Kismet\KismetArrayLibrary.h

Ex: adding a “RemoveFirst” Function

  • Create a blueprintfunctionlibrary with the function Array_RemoveFirst(const TArray& TargetArray) and BlueprintCallable, CustomThunk, ArrayParm = “TargetArray” metaspecifier
    // Note that const and int32 dont mean const and int32, i suppose they are simply placeholder
  • create an implementation that errors when its called, since we specified CustomThunk its going to call the following glue code :
  • Create the CustomThunk glue code like the following which basically fetches array data (i suppose as blueprint represents it) and inserts them into a generic function which we are going to create
DECLARE_FUNCTION(execArray_Add)
{
	Stack.MostRecentProperty = nullptr;
	Stack.StepCompiledIn<FArrayProperty>(NULL);
	void* ArrayAddr = Stack.MostRecentPropertyAddress;
	FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Stack.MostRecentProperty);
	if (!ArrayProperty)
	{
		Stack.bArrayContextFailed = true;
		return;
	}
	P_FINISH;
	P_NATIVE_BEGIN;
	*(bool*)RESULT_PARAM = GenericArray_RemoveFirst (ArrayAddr, ArrayProperty);
	P_NATIVE_END;
}
  • Create the declaration GenericArray_RemoveFirst but with different parameters, in our case void GenericArray_RemoveFirst (void* TargetArray, const FArrayProperty* ArrayProp);
  • Create the function implementation like the following:
if(!TargetArray )
	return;

FScriptArrayHelper ArrayHelper(ArrayProp, TargetArray);
if(!ArrayHelper.IsValidIndex(0))
{
	FFrame::KismetExecutionMessage(
		*FString::Printf(TEXT("Attempted to remove an item from an invalid index from array %s [/%d]!"),
			 *ArrayProp->GetName(), GetLastIndex(ArrayHelper)),
		ELogVerbosity::Warning, RemoveOutOfBoundsWarning);
	return;
}
ArrayHelper.RemoveValues(0, 1);

Looking forward from that knowledge, I believe I must continue looking into how to create a custom unreal engine type (such as FArrayProperty) and blueprint node (such as ArrayParm specifier)

EDIT 2 : Alright after debugging a lot in the generated cpp code, understanding a bit better how the interpreter works I seem to have found a interesting fact.

I checked deepter and deepter in definitions (thx Rider) and I tried to debug code from a Test Function with a const TArray<ATarget*>& parameter called form blueprint (inherited from APawn)
I believe that the interpreter’s Array is not the same as the c++ TArray, or at least is some way. Interpreter value are “script” reference by the XXXProperty mentionned above.
(ex: \Engine\Source\Runtime\Core\Public\Containers\ScriptArray.h)
And when you call a function, it create a native c++ array and copies its data from the script to the native (using CopyCompleteValueToScriptVM function) even when its a TArray reference). There might be a small doubt on the fact that it copies only the underlying pointer data but i wouldn’t believe that.

So basicaly, considering the initial problem, I believe that there are 2 problems
→ creating an optional script and being able to use it in blueprint
→ creating a bridge between it and the optional object

I have a local branch for UE5 where I added support for TOptional by adding a new FOptionalProperty to the UnrealTypes/reflection system. However I never got around to fully finishing it. There were just way too many places that do a bunch of casts for all the property types and then perform special handling depending on that result. Things I remember of the top of my head were the python integration, json/bson serialization and some plugins.

If anyone was willing to help finish it I could push it to my github fork.

1 Like

Hi,

As I am mainly doing it for fun and to have a better understanding of the Unreal Engine’s blueprint interpreter and blueprint type system, I won’t make any promises. However if you would be okay to share it with me so I can see what modifications you did, I would greatly appreciate it.

Honestly depending of the complexity and my understanding I might help you finish your work !

Thank you for your response !

Amazing, I was just trying to update my branch to the last ue5-main. Only to run into merge conflicts on a new file I created: PropertyOptional.cpp. Since Epic finally seems to have implemented this themselves I won’t have to update my branch. Although their implementation isn’t quite finished yet either looking at the comment in Engine\Source\Runtime\CoreUObject\Public\UObject\PropertyOptional.h:

//
// A property corresponding to UE's optional type, TOptional<T>.
// NOTE: this property is not yet handled by all UE subsystems that produce or consume properties.
//

Epic’s implementation also seems to handle the NonNullablePointer optimization case by adding a new property flag CPF_NonNullable. Hopefully this will be in the 5.3 release.

If you’re interested in the details also look at https://github.com/EpicGames/UnrealEngine/blob/ue5-main/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyOptional.cpp

1 Like

Wow, that is such an incredible news ! I’m really excited to write better blueprint soon !

I’ll try to find the relevant commits to track down all the changes they did :smiley:

Thanks a lot @UnrealEverything !

Edit: For those interested, here are the relevant commits i could find
(https://github.com/EpicGames/UnrealEngine/commits/ue5-main/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyOptional.cpp)

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.