Method References
Function pointers
Function pointer types can point to static methods, non-static methods with explicit ‘this’ parameters, external functions, or functions dynamically generated by libraries. Function pointers are generated with the =>
operator.
/* Local variable `funcPtr` */
function void() funcPtr = => StaticMethod;
funcPtr();
/* Passed as an argument to another method */
void UseFuncPtr(function void() funcPtrB) => funcPtrB();
UseFuncPtr(=> StaticMethodB);
/* Note that 'ca' is not captured here. We could also specify 'ClassA.MemberMethod' */
ClassA ca = new ClassA();
function void(ClassA this, float f) funcPtr2 = => ca.MemberMethod;
funcPtr2(ca, 1.2f);
/* Note that structs need to differentiate between mut/non-mut */
StructA sa = StructA();
function void(mut StructA this, float f) funcPtr3 = => sa.MemberMethod;
funcPtr3(mut sa, 2.3f);
/* Note that for interop, all [CRepr] structs will be passed by pointer no matter whether mut is specified or not, so calling convention is identical for both */
Delegates
Delegates are more general, and are defined as class types which can not only refer to anything a function can, but can also refer to instance methods and can hold local variable captures when they point to local methods or lambdas. Delegates are allocated using the =>
operator preceded by an allocation specifier.
delegate void() delegateVal = scope => MemberMethod;
Events
Events can be thought of as multicast delegates. The System.Event<T>
struct wraps a delegate type and can contain zero or many delegate references.
Event<delegate void(int)> evt = default;
/* Note the use of 'new =>' because the Event takes ownership of the delegates */
evt.Add(new => obj.MethodA);
evt.Add(new => obj.MethodB);
/* This will invoke the delegates in the order they were added */
evt(intVal);
/* This removes a single delegate. Note the use of 'scope' because this argument is only used for comparison and no ownership is transferred */
evt.Remove(scope => obj.MethodA, true);
/* Dispose will delete all remaining delegates */
evt.Dispose();
Lambdas
Lambdas are a shorthand for creating a local method and then allocating a delegate to point to it, except a lambda also allows you to define a lambda destructor for freeing any resources required during the lifetime of the lambda.
static void Test(StringView str)
{
int i = 0;
/* Locally defined method which captures 'i' by reference */
char8 GetNext()
{
if (i >= str.Length)
return 0;
return str[i++];
}
/* Locally defined method with no captures */
char8 GetEmpty()
{
return 0;
}
/* Allocates a delegate bound to GetNext() */
delegate char8() strDlg = scope => GetNext;
/* Bind emptyFunc to GetEmpty(). Binding to GetNext() would fail because function pointers cannot hold captures */
function char8() emptyFunc = => GetEmpty;
/* Allocate lambda */
strDlg = scope () =>
{
return 'A';
};
/* Allocate lambda that captures by reference, which allows the GetNext call to capture 'i' */
strDlg = scope [&] () =>
{
return GetNext();
};
/* This lambda owns a string, which gets cleaned up after lambda goes out of scope */
String tempStr = new String(str);
tempStr.EnsureNullTerminator();
/* capture 'i' by reference, str and tempStr by value */
strDlg = scope [&i, =str, =tempStr] () =>
{
return tempStr[i];
}
~
{
delete tempStr;
};
}
Valueless Method References
Valueless method references can be used to specialize certain types of generic methods such that they directly call the referenced method rather than calling indirectly through a delegate. This can improve performance in some types of critical code paths.
static T Max<T, TFunc>(T lhs, T rhs, TFunc func) where TFunc : delegate int(T lhs, T rhs)
{
return (func(lhs, rhs) >= 0) ? lhs : rhs;
}
int CmpVec(Vector2 lhs, Vector2 rhs)
{
return lhs.x <=> rhs.x;
}
/* The 'func' argument of this call is valueless - a specialized 'Max' method will be generated that directly calls CmpVec */
Vector2 max = Max(vec0, vec1, => CmpVec);