Unreal – FName non-preserved case sensitivitiy

Peter Brooks
2 min readMay 25, 2020

--

FName in Unreal is an efficient hash map implementation. Used as a more efficient version of strings.

However one caveat of this type should always be in mind:

The case sensitive nature of FName is dependent on the first instance of it.

Sample

Say you have these definitions in the codebase :

FName TitleCaseName(L”MyActivity”);
FName camelCaseName(L”myActivity”);

Ignoring the fact that we have two programmers adopting different code styles. (But we could have mixed code from another plugin or dependency)

A genuine problem can arise, what will theses values be?

TitleCaseName.ToString();
camelCaseName.ToString();

The answer? Either both become “MyActivity” or both become “myActivity”.

As FName is fully dependent on which instance came first. There exists no case where both expressions of the cases are possible.

Reason

From the FName documentation, Unreal states:

FNames provide a very lightweight system for using strings, where a given string is stored only once in a data table

Given a case sensitive entry was added. When creating an entry, a case insensitive lookup is made for an existing entry.

Thus if it already exists, you get back the case of the original entry.

Digging deeper

A define exists within Engine/Source/Runtime/Core/Public/UObject/NameTypes.h, that alters the behavior the case preservation for FName.

/** 
* Do we want to support case-variants for FName?
* This will add an extra NAME_INDEX variable to FName, but means that ToString() will return you the exact same
* string that FName::Init was called with (which is useful if your FNames are shown to the end user)
* Currently this is enabled for the Editor and any Programs (such as UHT), but not the Runtime
*/
#ifndef WITH_CASE_PRESERVING_NAME
#define WITH_CASE_PRESERVING_NAME WITH_EDITORONLY_DATA
#endif

Thus, you can get different behaviours for FName depending if you’re in editor or running a build.

Auditing usage

With a little shell magic, we can find any inconsistent FName usage:

ack --type=cpp 'FName.*".+"' -h | sed 's/.*FName.*"\(.*\)".*/\1/' | sort | uniq | tr '[:upper:]' '[:lower:]' | sort | uniq -c | sort

Caveat: This is a limited search, it’s just matching strings within FName constructors. It will miss evaluating from other variables or Blueprint usage.

Conclusion

As the case preservation of FName can never be guranteed.

Use it wisely:

  • Use for it’s original intention, dealing with assets.
  • Don’t use it for keys in data structures, or in parameters for web requests.
  • Got an existing project?
    Consider linting through your codebase for non-matching case usage of FName.

--

--

Peter Brooks

Project Technical Operations Lead WUSHU studios. Network / API / Generalist / Infrastructure.