Thanks. I’m going to answer your questions in reverse order:
I think MakeFunction might be just what I’ve been looking for. I figured that, since you can create arbitrary functions in blueprint and then get them in code as UFunctions, there must be a way to do it, but I failed to find it before.
The use case is some large system has been implemented entirely in blueprint and we won’t/can’t convert it to code. But we develop a need to at least interact with this blueprint system from code, such as calling its blueprint functions and getting return values. The reason the signatures might change is that someone might literally open the blueprint and change the parameters on the blueprint function. (Yes, there are other approaches to this problem, like making some sidecar component that does nothing more than forward function calls back and forth between code and blueprint. But I’d like to explore other avenues.)
Yes, iterating over the UFunction params and making sure all the expected prams are there and in the right order would work. It’s just a cumbersome bit of code to write and maintain. I liken it to parsing text without regular expressions.
Here’s how I’m calling functions. A blueprint actor has a blueprint function named MyBlueprintFunction that takes a UBarConfigDataAsset and returns an int32. I make a UFUNCTION in code with the same signature just for reference. Then I call MyCppFunction which takes Actor and Config, attempts to call the blueprint version of the function on the actor, and then returns ResultId.
`// .h //
UCLASS()
class UFooComponent: public UActorComponent
{
[…]
int32 MyCppFunction(AActor* Actor, UBarConfigDataAsset* Config);
UPROPERTY(EditAnywhere)
FName MyFunctionName = TEXT(“MyBlueprintFunction”);
private:
// MyFunctionSignature exists just to reference the signature but is never directly called.
UFUNCTION()
int32 MyFunctionSignature(UBarConfigDataAsset* Config) { return INDEX_NONE; }
// .cpp //
int32 UFooComponent::MyCppFunction(AActor* Actor, UBarConfigDataAsset* Config)
{
int32 ResultId = INDEX_NONE;
UFunction* Signature = FindFunction(GET_FUNCTION_NAME_CHECKED(UFooComponent, MyFunctionSignature));
UFunction* Func = Actor->FindFunction(MyFunctionName);
if (Signature && Signature->IsSignatureCompatibleWith(Func, UFunction::GetDefaultIgnoredSignatureCompatibilityFlags() | CPF_ReturnParm))
{
uint8* ParamBuffer = (uint8*)FMemory_Alloca_Aligned(Func->ParmsSize, Func->GetMinAlignment());
FMemory::Memzero(ParamBuffer, Func->ParmsSize);
for (TFieldIterator ParamItr(Func); ParamItr && (ParamItr->PropertyFlags & CPF_Parm); ++ParamItr)
{
ParamItr->InitializeValue_InContainer(ParamBuffer);
if (Config)
{
if (FObjectProperty* ObjectProp = CastField(*ParamItr))
{
if (ObjectProp->PropertyClass == UBarConfigDataAsset::StaticClass())
{
ObjectProp->SetObjectPropertyValue_InContainer(ParamBuffer, Config);
}
}
}
}
Actor->ProcessEvent(Func, ParamBuffer);
for (TFieldIterator ParamItr(Func); ParamItr && (ParamItr->PropertyFlags & CPF_Parm); ++ParamItr)
{
if (FIntProperty* IntProp = ParamItr->PropertyFlags & CPF_OutParm ? CastField(*ParamItr) : nullptr)
{
IntProp->GetValue_InContainer(ParamBuffer, &ResultId);
}
ParamItr->DestroyValue_InContainer(ParamBuffer);
}
}
else
{
ensureMsgf(false, TEXT(“Function signature changed!”));
}
return ResultId;
}`