Difference between revisions of "Function Reference"
imported>Jlundin |
imported>ZeroMemory (Added a further elaboration on the concept of the "Self" variable.) |
||
Line 15: | Line 15: | ||
The [[Identifier Reference|identifier]] used to name the function cannot conflict with any other function in the current script. If the identifier matches a function in the parent script, then the return type and parameters much match the parent script's version of the function - and the function will override the parent's function. | The [[Identifier Reference|identifier]] used to name the function cannot conflict with any other function in the current script. If the identifier matches a function in the parent script, then the return type and parameters much match the parent script's version of the function - and the function will override the parent's function. | ||
The "Global" flag indicates a function that does not actually run on an in-game object, and has no "Self" variable. The "Native" flag indicates a function that does not have a function body, because the function is implemented by the game itself. If you add the native flag to a function the game does not expose, the compiler won't complain, but the game will error at you. The same flag cannot be specified more then once. | The "Global" flag indicates a function that does not actually run on an in-game object, and has no "Self" variable. | ||
The "Native" flag indicates a function that does not have a function body, because the function is implemented by the game itself. If you add the native flag to a function the game does not expose, the compiler won't complain, but the game will error at you. The same flag cannot be specified more then once. | |||
<br><br> | |||
::{|style="border-collapse: separate; border-spacing: 0; border-width: 1px; border-style: solid; border-color: #000; padding: 0" | |||
|- | |||
|style="border-style: solid; border-width: 0"|[[Image:InDepth.jpg|48px]] | |||
|style="border-style: solid; border-width: 0"|If you're wondering what is a "Self" variable, it is a natural consequence of object oriented programming which propagates, along with everything else, the concept of a class and an instance of that class, also called an object. A very common example is the difference between a blueprint of a car and the actual car which is made from that blueprint. You can have a bunch of cars based upon a single blueprint. For anything useful, you can't know all the cars that are going to be created from your blueprint, so you need a method of determining upon which object has the function been invoked. And the way this is resolved is by silently pushing in an additional hidden parameter to a function which contains the memory address of the object on which the function is being invoked, of the logical name "Self", because you can't make a change to the data of a class (it's pointless, futile, resistance, Borg... Wait, what?), only the data of an instance of that class. A class is an idea. An object is a manifestation of an idea. A script is an idea. An object it gets appended to, on the other hand, is real (well, virtually real, you get the point). It's a manifestation of that idea. In some languages, '''self''' is called '''this'''. | |||
One could ask: "'''Why should I ever want to use this "Self" variable when I can simply change any variable I want directly, because I know that variable exists in the blueprint, therefore it's also in the instance. That's the whole point, is it not?'''" True that, young samurai. But here's just a simple example from general programming, imagine that you've got a function invoked on an object (sometimes called a '''non-static member function/method''' or perhaps an '''instance-level member function/method''') that takes a parameter that points to an object of the same class. And you want to make sure you're not being fed the very object the function has been invoked upon. That mouthful is referred to as "self-reference", so you can check whether pObject is equal to self and prevent damaging or unnecessary code from executing. If you can't think of a useful example, it comes in handy with dynamic memory management and functions called setters. | |||
Here, imagine that you are trying to change an object crucial to your application and you want to release the old one because you're a good memory citizen who cares about his users and their limited memory resources. So, you want to first release the old object and then set the pointer to the new one, right? But what if a nasty scenario occurs where you replace the object with itself? You release the old object, set the Self pointer to the memory address of the "new" object (which is, actually, the old object), try to dereference it (a fancy word for "use it") and boom - a wild unicorn appears and destroys your application '''if you're lucky'''. Usually, the memory is just cleared to be overwritten, the system might not use it immediately, therefore, in some cases - your application could work and make your life miserable by reports of crashing. You made a dangling pointer which references an object that was released (by you, a moment ago). Do you, perhaps, see how you could solve this? Yes, exactly. If newObject is NOT equal to Self (newObject != Self), then release the object referenced by Self and change the Self pointer/variable to point to newObject (Self = newObject). And you've solved all of your life's troubles. Kittens are purring. Birds are singing. And gamers are cursing at the dragon that killed them because your game or mod is awesome. | |||
Scripts in Papyrus are called '''[[:Category:Script_Objects|Objects]]''', which might be confusing to some people. Their name was probably chosen because the developers wanted to emphasize the fact that scripts are added to objects in the game, they describe their behaviour and functionality. They are the "soul" of a static mesh made out of triangles and covered with a few textures which make it '''look real'''. Papyrus scripts are what makes it '''real'''. What makes it an '''object'''. | |||
|} | |||
=== Parameters === | === Parameters === |
Revision as of 01:03, 11 February 2012
Functions are units of work that are larger then a single expression, and may take various parameters and return a value to their caller.
Function Definition
<function> ::= <function header> [<function block> 'endFunction']
Function headers must always be followed by a block and an "EndFunction" keyword, unless they are native functions (which are exposed by the game).
Function Header
<function header> ::= [<type>] 'Function' <identifier> '(' [<parameters>] ')' ('global' | 'native')* <flags>*
A function header starts (optionally) with the return type of the function, and is then followed by the name of the function, its parameters (if any), and any modifiers and flags.
The identifier used to name the function cannot conflict with any other function in the current script. If the identifier matches a function in the parent script, then the return type and parameters much match the parent script's version of the function - and the function will override the parent's function.
The "Global" flag indicates a function that does not actually run on an in-game object, and has no "Self" variable.
The "Native" flag indicates a function that does not have a function body, because the function is implemented by the game itself. If you add the native flag to a function the game does not expose, the compiler won't complain, but the game will error at you. The same flag cannot be specified more then once.
If you're wondering what is a "Self" variable, it is a natural consequence of object oriented programming which propagates, along with everything else, the concept of a class and an instance of that class, also called an object. A very common example is the difference between a blueprint of a car and the actual car which is made from that blueprint. You can have a bunch of cars based upon a single blueprint. For anything useful, you can't know all the cars that are going to be created from your blueprint, so you need a method of determining upon which object has the function been invoked. And the way this is resolved is by silently pushing in an additional hidden parameter to a function which contains the memory address of the object on which the function is being invoked, of the logical name "Self", because you can't make a change to the data of a class (it's pointless, futile, resistance, Borg... Wait, what?), only the data of an instance of that class. A class is an idea. An object is a manifestation of an idea. A script is an idea. An object it gets appended to, on the other hand, is real (well, virtually real, you get the point). It's a manifestation of that idea. In some languages, self is called this. One could ask: "Why should I ever want to use this "Self" variable when I can simply change any variable I want directly, because I know that variable exists in the blueprint, therefore it's also in the instance. That's the whole point, is it not?" True that, young samurai. But here's just a simple example from general programming, imagine that you've got a function invoked on an object (sometimes called a non-static member function/method or perhaps an instance-level member function/method) that takes a parameter that points to an object of the same class. And you want to make sure you're not being fed the very object the function has been invoked upon. That mouthful is referred to as "self-reference", so you can check whether pObject is equal to self and prevent damaging or unnecessary code from executing. If you can't think of a useful example, it comes in handy with dynamic memory management and functions called setters.
Here, imagine that you are trying to change an object crucial to your application and you want to release the old one because you're a good memory citizen who cares about his users and their limited memory resources. So, you want to first release the old object and then set the pointer to the new one, right? But what if a nasty scenario occurs where you replace the object with itself? You release the old object, set the Self pointer to the memory address of the "new" object (which is, actually, the old object), try to dereference it (a fancy word for "use it") and boom - a wild unicorn appears and destroys your application if you're lucky. Usually, the memory is just cleared to be overwritten, the system might not use it immediately, therefore, in some cases - your application could work and make your life miserable by reports of crashing. You made a dangling pointer which references an object that was released (by you, a moment ago). Do you, perhaps, see how you could solve this? Yes, exactly. If newObject is NOT equal to Self (newObject != Self), then release the object referenced by Self and change the Self pointer/variable to point to newObject (Self = newObject). And you've solved all of your life's troubles. Kittens are purring. Birds are singing. And gamers are cursing at the dragon that killed them because your game or mod is awesome.
Scripts in Papyrus are called Objects, which might be confusing to some people. Their name was probably chosen because the developers wanted to emphasize the fact that scripts are added to objects in the game, they describe their behaviour and functionality. They are the "soul" of a static mesh made out of triangles and covered with a few textures which make it look real. Papyrus scripts are what makes it real. What makes it an object.
Parameters
<parameters> ::= <parameter> (',' <parameter>)* <parameter> ::= <type> <identifier> ['=' <constant>]
The parameter list is a comma-separated list of types and identifiers that indicate the various parameters that a function takes. Each parameter may be optionally followed by an equals sign and a constant, which indicates that the parameter has a default value. If a parameter has a default value, every parameter after it must also have a default value.
Parameters are essentially variables the function has access to that the caller gives initial values to.
Function Block
<function block> ::= <statement>*
The function block contains zero or more statements. This performs the actual work of the function.
Examples
; A simple function that adds the two values together and returns the result
; Global, because it doesn't need a self variable
int Function AddTwo(int a, int b) global
return a + b
endFunction
; A function that increments a value on this script by the specified amount.
; The amount has a default value of 1 (so the caller doesn't have to pass it)
Function IncrementValue(int howMuch = 1)
myValue += howMuch
endFunction
Special Variables
There are two special variables in a function, but only in a non-global one. "Self" refers to the instance of the script that the function is running on, and is useful if you want to pass yourself off to another function somewhere else.
"Parent" is only used to call a parent script's version of a function, in the case where you extend the parent.
Examples
; Pass our self off to another function
SomeObject.OtherFunction(self)
; Call the parent's version of DoStuff, ignoring our local definition
Parent.DoStuff()
Calling Functions
Global function:
[<identifier> '.'] <identifier> '(' [<parameters>] ')'
Non-global function:
[<expression> '.'] <identifier> '(' [<parameters>] ')'
Calling a function simply involves using the function's identifier, followed by parenthesis, and any parameters that the function takes. The return value of the function is the result of the function call and can be assigned to a variable, or used to call another function or property.
If you are calling a global function and the function's owning script isn't the current script or isn't imported, then you must prefix it with the name of the script the function resides in.
If you are calling a non-global function and it isn't on yourself, then you must prefix it with the object you want to call it on.
Parameters
<parameters> ::= <parameter> (',' <parameter>)* <parameter> ::= [<identifier> '='] <expression>
The parameter list is a comma-separated list of expressions in the same order as the parameters are listed in the function definition. If a parameter is optional, it does not have to be passed (the default value is inserted by the compiler into the call location). You may specify parameters out of order by prefixing the expression with the identifier of the parameter (matching the name of the parameter in the definition) followed by an equals sign.
Examples
; Call the function: MyFunction(int a, int b) and get the result and put it in x
x = MyFunction(1, 2)
; Call the function DefaultFunction(float a, float b, float c = 0.0, float d = 1.0) on MyObject,
; but only pass in the first three parameters
MyObject.DefaultFunction(4.0, 2.0, 1.0)
; Call the function DefaultFunction(float a, float b, float c = 0.0, float d = 1.0), but specify
; argument d out of order because we want c to keep the default it has
DefaultFunction(5.0, 2.4, d = 2.0)
; Call the global function MyGlobal() in the Utility script
Utility.MyGlobal()