Property compatibility from Blueprint to C++ conversion

Context: We are trying to convert blueprint actors to C++, after which we would reparent the blueprint to the C++ version and expect the variables and their values (set from the map) to be persisted over.

Generally variables starting with letters AND without special characters (like “foo”, “bar”) would be automatically compatible with the C++ version. However it doesn’t work for those with special characters, such as “_someVariable” because in C++ it’s syntactically wrong to start a variable with special characters, and variables that have “=” or “/” or other special characters in the names.

We are able to solve this issue with property redirects, but would like to know if there’s a better way like using a metadata specifier.

What would be the best/seamless approach to converting blueprint variables to C++? How to deal with the edge cases?

Hello [mention removed]​,

As far I’m aware, using Core Property Redirects (https://dev.epicgames.com/documentation/en-us/unreal-engine/core-redirects-in-unreal-engine) remains the official and recommended solution for handling edge cases when converting Blueprint variables to C++. That’s the safest way to prevent data loss during serialization and asset loading.

Using Metadata specifiers like only exists in the editor, and doesn’t affect serialization (https://dev.epicgames.com/documentation/en-us/unreal-engine/metadata-specifiers-in-unreal-engine). Epic itself indicates to not use it for final applications, just for Editor work. Data is saved based on internal UProperty name, so during reparenting, the variables will be lost unless there is a Property Redirect.

A possible solution I see, is to create and generate this property redirects entries from an Editor Tool. You could use an EditorUtilityWidget to speed up the process and use Unreal Reflection system (https://dev.epicgames.com/documentation/en\-us/unreal\-engine/reflection\-system\-in\-unreal\-engine) to relate the Blueprint and C++ variables, and generate this Property redirects.

Here are some of the code I’ve been using to check if this was possible:

1- Get CDO of the unreal class:

static UObject* ResolveToInstance(UObject* Object)
{
    if (UBlueprint* BP = Cast<UBlueprint>(Object))
    {
        if (BP->GeneratedClass)
            return BP->GeneratedClass->GetDefaultObject();
    }
    return Object;
}

2. Sanitize Property Names (Replace Ilegal characters):

static FString SanitizeCppIdentifier(const FString& In)
{
    FString Out;
    Out.Reserve(In.Len());
 
    for (TCHAR C : In)
    {
        if (FChar::IsAlnum(C) || C == TEXT('_'))
            Out.AppendChar(C);
        else
            Out.AppendChar(TEXT('_')); // replace illegal characters with underscore
    }
 
    while (Out.StartsWith(TEXT("_")))
        Out.RightChopInline(1);
 
    if (!Out.IsEmpty() && FChar::IsDigit(Out[0]))
        Out = TEXT("N") + Out;
 
    return Out;
}

3- Loop through Blueprint Properties:

for (TFieldIterator<FProperty> It(Class); It; ++It)
{
    FProperty* Property = *It;
 
    if (Property->GetOwnerClass() != Class)
        continue;
 
    FString RawName = Property->GetName();
    FString Type = Property->GetClass()->GetName();
    FString Display = Property->GetMetaData(TEXT("DisplayName"));
}

4. Detect Edge Cases

FString BaseName = Display.IsEmpty() ? RawName : Display;
FString Clean = SanitizeCppIdentifier(BaseName);
 
bool bIsEdgeCase = !Clean.Equals(BaseName);
if (bIsEdgeCase)
{
    Result.Add(FString::Printf(TEXT("C++ Name: %s, Type: %s, Blueprint Name: %s"),
        *RawName, *Type, *Display));
}
else
{
    Result.Add(FString::Printf(TEXT("C++ Name: %s, Type: %s"),
        *RawName, *Type));
}

5- Generate Core Property Redirects:

(Build sanitized OldName and NewName for .ini entries)

FString SafeOld = SanitizeCppIdentifier(RawName);
if (SafeOld.IsEmpty())
    SafeOld = TEXT("RenamedProperty");
 
FString ClassName = Class->GetName();
FString OldFull = FString::Printf(TEXT("%s.%s"), *ClassName, *SafeOld);
FString NewFull = FString::Printf(TEXT("%s.%s"), *ClassName, *Clean);
 
FString Redirect = FString::Printf(TEXT("+PropertyRedirects=(OldName=\"%s\", NewName=\"%s\")"),
    *OldFull, *NewFull);
 
RedirectOutput.Add(Redirect);

To integrate this within your Utility Widget, just plug the exposed InspectProperties(Object) function upon clicking a button in your Utility Widget, add an instance editable UObject variable (to specify which Blueprint Actor to scan), and print the output in a textbox of the Widget. You might also prefer to a TSubclass instead of the UObject*, that is what you prefer the most.

Here’s an example output with a variable using an illegal “$” character:

C++ Name: $TEST, Type: StrProperty, Blueprint Name: BP_TestActor_C.$TEST 
----- Redirect Suggestions (For DefaultEngine.ini) -----
+PropertyRedirects=(OldName="BP_TestActor_C._TEST", NewName="BP_TestActor_C.TEST")

The suggested workflow would be to:

1- Carefully review the output with edge-case variables

2- Update illegal variables & resave blueprints

3- Copy redirects to DefaultEngine.ini

4- Rebuild the project.

Please let me know if this helps and don’t hesitate to ask if you need any further assistance.

Best,

Joan