Function pointers
Note: This feature is for ZScript only. |
Function pointers are useful for several things:
- Detecting if certain objects have functions defined, allowing implementation of cross-mod support (similar to Service) without creating dependencies
- Enable Callbacks - functions with the same name but different parameters.
Of note, these are pointers to standard ZScript functions that are already defined.
The only supported types of functions are static and normal functions. They cannot be action, virtual, or variadic functions (variadic meaning Console.Printf, Screen.DrawText, etc. Anything with an ellipsis ('...') that allows for expanding the function).
Definitions
Functions can be defined as such:
Function<void> myFunc; Function<scope RetTypes(ArgTypes) myFunc;
- scope - Can be play, ui, or clearscope. See Object scopes and versions for more details.
- RetTypes - The return types to expect.
Functions can also be used in properties with the format ClassName::FunctionName
as a string.
Function casting
Casting is done similar to casting for classes (i.e. Class<>). This needs two sets of () such as:
(Function<...>)(value)
Functions can have their returns widened with casts:
Function<play Actor()> someFn = ...; // insert code for function here Function<play Object()> someFn2 = (Function<play Object()>)(someFn); // widens the return from an Actor to an Object
And functions can have their argument types narrowed with casts:
Function<play void(Object)> someFn = ...; Function<play void(Actor)> someFn2 = (Function<play void(Actor)>)(someFn);
A simpler way to think about it is basically performing casting like this.
Actor getActor() // assuming this is within an Actor class { return self; } Object getObject() { return getActor(); // Normally one would need to perform <Classname>() but not in this case. }
Function finding
Functions can be found with this static function:
native static Function<void> FindFunction(Class<Object> cls, Name fn);
Or they can be referenced directly if the class is known.
let someFn = someObject.someFunction; let someFn = someFunction; // Coming from the same class
Examples
version "4.12" class TestHandler : StaticEventHandler { int c; clearscope double AddDouble(double a, double b) {return a+b+c;} clearscope int AddInt(int a, int b) {return a+b+c;} override void OnRegister() { c = 25; let test_function_double = TestHandler.AddDouble; if(test_function_double.call(self, 10.5, 20) != (30.5 + c)) { ThrowAbortException("Failed to call AddDouble"); } let test_function_int = TestHandler.AddInt; if(test_function_int.call(self, 10, 20) != (30 + c)) { ThrowAbortException("Failed to call AddInt"); } } } class TestPlayer : DoomPlayer { Function<play double(TestHandler,double,double)> test_function_double; Function<play double(TestHandler,double,double)> test_function_double2; Function<play int(TestHandler,int,int)> test_function_int; property test_function_double2 : test_function_double2; Default { TestPlayer.test_function_double2 "TestHandler::AddDouble"; } Function<play double(TestHandler,double,double)>, Function<play int(TestHandler,int,int)> getFns() { return TestHandler.AddDouble, TestHandler.AddInt; } Function<play double(TestHandler,double,double)>, Function<play int(TestHandler,int,int)> getPtrs() { Function<play Function<play double(TestHandler,double,double)>, Function<play int(TestHandler,int,int)>(TestPlayer)> testfnptr = getFns; let [a, b] = testfnptr.call(self); return a, b; // doing return testfnptr.call(self); directly doesn't work, bug #2210 } override void PostBeginPlay() { super.PostBeginPlay(); [test_function_double, test_function_int] = getPtrs(); } override void Tick() { super.Tick(); let handler = TestHandler(StaticEventHandler.Find("TestHandler")); console.printf("test_function_double.call(10.5, 20) = "..test_function_double.call(handler,10.5, 20)); console.printf("test_function_double2.call(10.5, 20) = "..test_function_double2.call(handler,10.5, 20)); console.printf("test_function_int.call(10, 20) = "..test_function_int.call(handler,10, 20)); } }