Skip to content

Templated types and functions

Cauê Waneck edited this page Apr 2, 2018 · 4 revisions

Unreal.hx supports defining externs to C++ templated non-uobject types.

Their extern declaration is very simple and close to how one would declare a generic type in Haxe:

// Example C++ template
template<class T>
class TMyTemplate {
public:
  TMyTemplate() { }
};

Will be externed as:

@:glueCppIncludes('path/to/include.h')
@:uextern extern class TMyTemplate<T> {
}

To actually be able to do something useful with this class, one probably would want to create a constructor. If you recall from the Extern definitions section, non-uobject constructors in ue4hx are just static functions with the @:uname("new") metadata - which brings us to the next section

Templated functions

Like templated classes declarations, declaring a templated function extern is very easy:

  @:uname(".ctor") public static function create<T>():TMyTemplate<T>;

The Extern baker will take care of generating the function correctly.

Calling a templated function

Unlike C++, Haxe doesn't allow you to call a templated function with an explicit type parameter; Rather, all type parameters are inferred by the call. Unfortunately not all C++ function calls can have their type parameter inferred from its arguments/return type, or this inference can become very difficult.

Because of this, the Extern baker will generate some optional helpers to help Haxe infer the function correctly. These helpers are of the unreal.TypeParam<> type, and they are no-ops that only help typing. They are optional but do not incur in any performance loss when using them.

So suppose we want to create an instance of TMyTemplate<int32> from the example above. We can do it in two different ways:

// Let haxe inference do its job
var x:TMyTemplate<unreal.Int32> = TMyTemplate.create();

// Use the TypeParam helper
var x = TMyTemplate.create(new unreal.TypeParam<unreal.Int32>());

typeName metadata

Now suppose we want to create an instance of TMyTemplate<UObject *>:

// Let haxe inference do its job
var x:TMyTemplate<unreal.UObject> = TMyTemplate.create();

// Use the TypeParam helper
var x = TMyTemplate.create(new unreal.TypeParam<unreal.UObject>());

ue4hx is smart enough to know that uobject-derived types are always used as pointers, not by reference, so it will add the pointer automatically.

However, in some cases we do want uobject-derived types to be called without a pointer. For example, on the global NewObject<> templated call - it needs to take the actual UObject type name instead of the expected type.

In this case, you must use the special @:typeName metadata:

  @:typeName @:global public static function NewObject<T>():PExternal<T>; // PExternal indicates that the returned type is a pointer

@:noTemplate

Sometimes, Unreal provides a templated function that provides no other benefit than allowing one to not have to call .Cast<> by themselves. For example, DuplicateObject works just as well if it's called with a <UObject> implementation, and the template only helps by already casting the result to the same type as the source. @:noTemplate allows you to define an extern that is generic on Haxe, but not used as a template in the C++ glue code. This is beneficial because it allows you to still have the benefit of the generic function definition, but without the downsides that come from it, which are the need to perform a C++ compilation every time a new template is used. DuplicateObject is defined as such in the Unreal.hx externs:

  @:glueCppIncludes("UObject/UObjectGlobals.h")
  @:uname("DuplicateObject<UObject>")
  @:noTemplate
  @:global public static function DuplicateObject<T : UObject>(sourceObject:T, destOuter:UObject, ?name:FName):T;