UE4 Libraries You Should Know About!

Lead programmer is here to talk to you guys about all of the C++ libraries available in UE4. Head over to the blog for the original post with code.

[HR][/HR]
UE4 has plenty of great C++ libraries to use for game development, but it’s not always obvious in a large code project where to find them. In this post, I just want to call out a few that are especially useful and worth checking out. For more information, you might also want to read the “Core” part of the API document: Unreal Engine API Reference | Unreal Engine Documentation

Containers
To suit your storage needs, UE4 has a whole gamut of container classes available, many mirroring the basic functionality that would traditionally be a part of C++’s standard library. Honestly, just about the only thing you won’t be able to contain is your excitement from all of these classes!…I’m sorry.

Common Containers

**TArray **
(Engine\Source\Runtime\Core\Public\Containers\Array.h)

TArray is a templated, dynamically-sized array and easily the most commonly used of the UE4 containers. It has all the features you would expect from a dynamic array as well as full UPROPERTY support. TArray’s API additionally provides functionality for treating a TArray as a stack or heap structure.

As TArrays can be declared as UPROPERTYs, they are easily displayed in editor property windows and are eligible for network replication, as well as automatic UPROPERTY serialization. As a result of this feature, TArray is often the container of choice in gameplay code implementations.

If you’ve ever used the C++ Standard Template Library (STL) vector class, TArray is the friend you’re looking for.

TSet
(Engine\Source\Runtime\Core\Public\Containers\Set.h)

TSet is a templated implementation of the concept of a mathematical set and offers the expected set operations, such as Intersect, Union, and Difference, as well as a quick way to check if an element is a member of a set or not (PeopleWhoLoveTSet.Contains(Me); // Always evaluates to true).

Caveat: Unlike TArray, TSets (and TMaps) are not directly supported as UPROPERTYs and so cannot be automatically replicated, serialized, etc. If a TSet (or TMap) is used with hard UObject references (example: TSet<UObject*>), it is up to the user to make sure those references are properly serialized for the purposes of garbage collection. Someone has to take out the trash…

TSet is analogous to the C++ STL set class, however the UE4 implementation is based on hashing. If you make a new type and need to use it in a TSet (or TMap), you need to implement a simple function to hash the type: uint32 GetTypeHash(const YourType& TypeVar). There are plenty of examples in the code base if you want to check them out.

TMap
(Engine\Source\Runtime\Core\Public\Containers\Map.h)

TMap is a templated data structure allowing the mapping of one type to another (key-value pairs) with fast element addition, removal, and look-up. If coming from another programming language, you might also know the structure the TMap represents as a “dictionary.”

Like the TSet, the TMap cannot be declared as a UPROPERTY.

TMap is comparable to the C++ STL map class, however the UE4 implementation is based on hashing.

Iterators

The UE4 containers provide iterator support, though the usage is not exactly the same as in the C++ STL. You can check each container type for its supported iterators, but in general const and non-const iterators are available.

Example:


// Example direct from the engine source:
// Initialize an iterator from the provided array (InPackages)
for (TArray<UPackage*>::TConstIterator PkgIter(InPackages); PkgIter; ++PkgIter)
{
     // Access the element at the current position of the iterator with the * operator
     UPackage* CurPackage = *PkgIter;

If you’re a fan of C++11 (and being lazy), you can also use the auto keyword with iterators:


for (auto FileIt = Files.CreateConstIterator(); FileIt; ++FileIt)
{
     const FString FileExtension = FPaths::GetExtension(*FileIt);

Sorting

In addition to a default sorting option, the UE4 containers that support sorting also allow custom sorting via a predicate object.

Example:


// Custom struct written to serve as the predicate for sorting. Given two constant references to elements
// in the data structure (anim notify events), sort them according to their trigger time.
struct FCompareFAnimNotifyEvent
{
        FORCEINLINE bool operator()(const FAnimNotifyEvent& A, const FAnimNotifyEvent& B) const
	{
		return A.GetTriggerTime() < B.GetTriggerTime();
	}
};

// Sort the notifies array (TArray<FAnimNotifyEvent>) with the custom predicate
Notifies.Sort(FCompareFAnimNotifyEvent());

Other Containers

TArray, TSet, and TMap are the most commonly used UE4 containers, but certainly not the only ones! If you want to check out the source code for these three and the others, you’ll want to look in the Engine\Source\Runtime\Core\Public\Containers directory.

String Handling

(Engine\Source\Runtime\Core\Public\Containers\UnrealString.h)

(Engine\Source\Runtime\Core\Public\UObject\NameTypes.h)

(Engine\Source\Runtime\Core\Public\Internationalization\Text.h)

UE4 provides three different classes for interacting with strings that you should be aware of: FString, FName, and FText. Each has their own particular purpose and optimal use case, which is explained in great detail in the documentation here: https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/StringHandling/index.html (and in the reference guides linked to from that page).

Math

(Engine\Source\Runtime\Core\Public\Math\UnrealMathUtility.h)

(Engine\Source\Runtime\Core\Public\GenericPlatform\GenericPlatformMath.h)

What’s a game without math?! Luckily, UE4 has a very robust, cross-platform math library, generally implemented as a series of static functions within FMath. FMath encompasses a very large set of math operations from the very simple to the more complex. It’s definitely worth browsing both header files to get a full grasp of what’s already written for you to use before you begin anything involving math!

Closing

Hopefully this was a useful mini-tour of important libraries to be aware of in UE4, but it’s really just scratching the surface. The engine is full of code you can use for all types of purposes. I’d love feedback on this post also, if you have any! Too broad? Too confusing? Jokes entirely too lame? Questions? Comments? Let’s hear about it below or harass me on Twitter!: @](/).

TArray here I come :). Thanks. Also busted out laughing at the container joke (Awesome).

Thank you…

Another side effect of TMap not being able to be declared as UPROPERTY is that you cannot let it be specified at the Blueprint defaults level, but here is a trick:

Create a struct that can hold the data that you need to create the TMap and then define a TArray of such struct type (both can be defined at BP level). Then, in the PostInitializeComponents of the blueprint base class you can iterate over the TArray and initialise the TMap with the data from the struct.

HiH :smiley:

A very helpful post providing some pointers (pun intended :slight_smile: ) to libraries that might be otherwise overlooked.

I’d suggest using the code tag in the future, to help separating code sections from the rest of the post.
Something like this (the following was copied from the OP):

The UE4 containers provide iterator support, though the usage is not exactly the same as in the C++ STL. You can check each container type for its supported iterators, but in general const and non-const iterators are available.

Example:


// Example direct from the engine source:
// Initialize an iterator from the provided array (InPackages)
for (TArray<UPackage*>::TConstIterator PkgIter(InPackages); PkgIter; ++PkgIter)
{
// Access the element at the current position of the iterator with the * operator
UPackage* CurPackage = *PkgIter;

As you’ve requested :slight_smile:

Hi Asaf,

Most of our containers also support C++11 range-based for syntax now, so you could rewrite that as:


for (const UPackage* CurPackage : InPackages)
{
    // Do something with CurPackage
}

Cheers,
Michael Noland

Thanks for the feedback so far everyone and for pointing out additional tips and tricks. Thanks for the range-based option as well Michael, totally forgot that amidst all the new-fangled C++11 options! Support for the terrible container joke only is going to encourage me to make worse jokes in future blog posts :).

Wanted to chime in and say this post is much appreciated. I am a recent convert and this is exactly the sort of content I look forward to reading.

The only thing missing for me is why I would use a TArray instead of std::vector. I realize you’ve mentioned specifics (acts as UPROPERTY) but as someone new to the engine this doesn’t mean a lot to me.

Being able to declare it as UPROPERTY means you can get all the goodies that UE4 can provide you, like having automatic replication, being able to set the values from the UE Editor (so artists can play with values without touching code), you can also access it from Blueprint code…

oh i was hoping they had a public Library in Washington DC already dedicated to Unreal Engine 4 knowledge and books :frowning: so disappointed. On that note i hope some nice unreal engine 4 physical books come out soon like what was done with the 3rd engine :-p.

Hi,

Are you really happy with *TArray *? It gave me some of my worst nightmares on an UE3 project… Ok not the worst, but i was really unhappy with it and by looking at the UE4 version, nothings really change apart from replacing operator() with operator] and some support for moveable only object.

First i understand the old way of having your own, with the chaotic template debut in compiler, various STL implementation, etc, but it was really a long time ago…

Here are some of my grief on *TArray *:

  • TIndexedContainerIterator is not even compliant to the iterator definition, the reference to a container break the following requirements : "— X satisfies the CopyConstructible, CopyAssignable, and Destructible requirements (17.6.3.1) and lvalues of type X are swappable (17.6.3.2)". This only may prevent his use in some STD algorithms, it miss also several type definition like, the iterator_tag, need to help the standard library to peek the most efficient algorithm if any.*Because of the nature of TArray, a single pointer member is enough in optimized build, and is not incompatible with range checking in a debug build like we have in the Microsoft version of the std.

  • Most of the services should be free functions, if std::map have a find method, it is because it knows it outperforms the free std::find, but on TArray, with a really compliant RandomAccessIterator, a lot of this is not even necessary. Example are Sort, Heapify, Find, Contains, … Less code to maintains is less bugs, less duplication, more time to optimize it once for different containers, …

  • *TArray *is an array not a TStack. Multiplying services and synonym for methods in an object to mimick another semantic and it will be easy to break it by using a non allowed method at some point, and mistakes always happen. *Push *synonym to *Add *is a good example as with the Emplace addition ( not variadic ? ), you miss also the Emplace version of Push, so someone need to come back to *TArray * services even when he uses it as a stack. std::stack is just an adapter over a container to restrict the object to the stack semantic and this is the real good way.

  • Several API are orthogonal to the standard vector, *Empty *is the worst, and several methods run with a shrink mode by default, not very performance oriented by default.

  • std::string in a game is not an option but the usual small string optimization is a good example to why FMemory::Memmove in methods like RemoveSingle are really dangerous.

Your feedback is much appreciated, galopBn. There’s a lot of history behind the decisions made regarding our containers and the decision to not use STL was made 15 years ago :slight_smile: There’s obvious differences between STL and UE containers which are often a result of optimizations tailored specifically for the engine and more generally, object model that permits higher levels of performance, although we try to keep balance between performance and memory footprint. They’re easier to maintain than third party code. We also try to make them more approachable than STL for people who don’t have much experience with C++.

I agree that standards are nice, and even here there are people who prefer STL but it would be a daunting task to change that now.

As it was already mentioned in this thread, we’ve recently made the containers C++ 11 friendly and we’re always looking for areas where we can improve them.

I can understand the arguments, in the past i myself wrote containers on game projects, even if i creates more problems that it solves at the end.

The more affordable argument is not a good one. Documentation on the standard is of higher quality, with lot of literature and a wide field of expert, like on stack-overflow, able to help on any topic related to the use of the std. Because of lack of documentation, programmers have to look at the code, and TArray is full of copy/past that bloat the code. As a point of comparison, a c++1y std::vector is 59 public member functions ( 12 for iterators ! ), 6 free comparator function and one free swap. TArray is more than 100 public member functions ( only 2 iterator ! ). The members that should be free functions are 4 sort related, 13 heap related, and 13 find related plus the 4 stack synonyms that should be an other class. Even without that, we still ends with more functions for less services… Not really a good definition of more affordable.

You should consider the incoming deprecated attribute, compiler specific ones already exists, and start a background cleaning by tagging improper API in it with a comment redirecting to the new one.

TArray is also a complicate problem in regards to the engine serialization process, adding a single member to it ( like for special debug tracking ) is just impossible as it breaks memory layouts, and they are even more destructor calls than constructor calls at runtime ( really !!! ).

I do not suggest to replace TArray by std::vector, it would be impossible, but it had evolve to a fearsome hydra, and it is sane to cut some heads.

As a small example, the remove erase idiom is a powerful generic way to remove element to containers, and with proper iterators, will work on all kind of containers, … RemoveSwap is doable too by replacing std::remove by with std::partition. This allow to simplify the TArray API again, and factorize a lot of inner job that is redundant in the current implementation.

Less code is less bug; C++11, C++1y changes in regards to rvalue reference and noexcept add new overloads almost on all containers services. Keep them as few as possible and rely on algorithms is a good way to limit new bugs at the transition.

Hi GalopBn,

I am one of the main maintainers of the UE4 containers and also one of the aforementioned ‘people who prefer STL’ who work on UE4. I’m agree with your points, and I have many more of my own reasons for STL adoption. Be assured that your views are being raised in discussions towards the future of the UE containers.

One thing I do want you to ask you about though is your comment “they are even more destructor calls than constructor calls at runtime ( really !!! )”. Do you mean that the number of constructors calls of TArray elements does not match the number of destructor calls for those elements? I find this very concerning and haven’t seen any evidence of this in the past. If you can provide a repeatable test case, I will ensure that it gets fixed.

Thanks,

Steve

Hi Steve,

i answered to you privately about that quite surprising behavior.