Download

Pass FString as a parameter to UObject::ProcessEvent(UFunction*, void*)

Hello,
I’ve been writing some c++ code that allows the user to call a function by name. Additionally, it allows the user to pass parameters and get the return values from OUT parameters. To make this easier, I’ve predefined some types that are allowed to be passed as arguments, such as bool or int32. For instance, a function with the declaration


void SomeFunc(int32 MyInt, bool& OUT_MyOutBool);

would be called like this using ProcessEvent (roughly):



UObject* Caller = // Get the caller.
UFunction* Function = // Get the ufunction associated with the given name.
int32 Value = // Get the int32 input.

uint8* Buffer = FMemory::Malloc(Function->ParamsSize);
FMemory::Memcpy(Buffer, &Value, sizeof(int32));

Caller->ProcessEvent(Function, Buffer);

bool OutBool;
FMemory::Memcpy(&OutBool, Buffer + sizeof(int32), sizeof(bool));

// Do things with the out parameter.

FMemory::Free(Buffer);


Now I want to introduce FString as an input as well. The problem is, it is an object. I can’t Memcpy the given FString into Buffer and pass it to ProcessEvent. Doing so (and using the FString inside the function) throws an exception. How can I pass objects as parameters to ProcessEvent?

I’ve tried checking engine code but intellisense is giving me a hard time looking up all the functions. Any help is appreciated.

Solved it. For anyone interested, basically anything can be passed as parameter, following these steps:


UObject* Caller = // Get the caller.
UFunction* Function = // Get the ufunction associated with the given name.

int32 IntValue = // Get the int32 input.
FString StrValue = // Get string input.

uint8* Buffer = (uint8*)FMemory_Alloca(Function->ParamsSize);
FMemory::Memzero(Buffer, Function->ParmsSize);

for (TFieldIterator<UProperty> It(Function); It && It->HasAnyPropertyFlags(CPF_Parm); ++It) {
    UProperty* FunctionProperty = *It;
    FString Type = FunctionProperty->GetCPPType();

    if (Type == "int32") {
        *Function->ContainerPtrToValuePtr<int32>(Buffer) = IntValue;
    } else if (Type == "FString") {
        *Function->ContainerPtrToValuePtr<FString>(Buffer) = StrValue;
    }
}

Caller->ProcessEvent(Function, Buffer);

Hello m.duman.

Your code has help me a lot. I was able to call my BP function. But I have some limitations here. I don’t understand what’s really going on in FMemory_Alloca and Memzero. And I was unable to use that “for” command. I know my function needs a ENUM type as argument, but I don’t get how to pass it.

So, if you see my post, please help me. Thank you

Hey! Thanks for the snippet. I found two mistakes.

  1. Use FunctionProperty instead of a Function when calling ContainerPtrToValuePtr.
  2. Function member ParamsSize does not exist, use ParmsSize.

Hello mpolaczyk,
Yes you are correct, I must have made some typos. Tazianemon, I haven’t looked at this code piece in a while, and it is not very scalable or reliable. Enums are basically integers though, so maybe you can have a workaround like that. I looked into the engine code to see how they actually call functions by name, and “borrowed” some code from there as well. But still, this system has many flaws and I suggest finding another way to do what you are intending to do, or purely copy what Epic has done inside the engine code. Take a look at:

ScriptCore.cpp under CallFunctionByNameWithArguments