Cogito, ergo sum

Operators

From Unreal Wiki, The Unreal Engine Documentation Site
Revision as of 03:04, 5 March 2011 by Wormbo (Talk | contribs) (some clarifications here and there)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

An operator in UnrealScript is a special kind of function. Besides these regular operators, UnrealScript also provides a number of special operators.

Operator declarations[edit]

Most of the operators in UnrealScript are declared in the Object class. Only a few special operators are hard-coded in the compiler.

Syntax[edit]

There are three types of regular operators in UnrealScript.

  • infix operators (a + b):
[modifiers] operator(precedence) [returntype] operatorname ( left operand, right operand ) body_or_semicolon
  • prefix operators (-x):
[modifiers] preoperator [returntype] operatorname ( operand ) body_or_semicolon
  • postfix operators (i++):
[modifiers] postoperator [returntype] operatorname ( operand ) body_or_semicolon

Infix operators always have two operands, while prefix and postfix operators have exactly one operand. All operator declarations must include the modifier final. The operator name can either be a single symbol character (^, !, $, %, &, /, ?, *, +, ~, @, -, >, <, |), a symbol combination (!=, $=, /=, ==, @=, +=, *=, ~=, -=, ^^, &&, ++, **, --, <<, >>, >>>, ||) or a standard function name. (examples in the engine are dot, cross and clockwisefrom)

Operators can be overloaded, that means multiple operators with the same name or symbol can be defined, as long as they differ in their operand types. Overloading with identical operand types is not allowed as the engine would not be able to distinguish between the definitions.

Operator precedence[edit]

Operator precedence controls, how tightly an operator binds its operands. A precedence value can only be specified for infix operators (pre/postfix operators bind as tightly as possible), with lower numbers binding more tightly. For example, * has a precedence of 16, while + has 20. That means * binds more tightly than +, so a * b + c * d is evaluated as (a * b) + (c * d). It is possible to define different precedence values for overloaded operators. For example the -= operator as combined subtract and assign for numeric values has a precedence of 34, while the -= operator for removing occurrences of a string in another string has a precedence of 45.

The following table lists the operator precedence for the default operators defined across the various engine versions. Operators further up in the table bind more tightly.

Precedence Operators Notes
12 ** exponentiation
16 *, /, Dot, Cross multiplication, division, dot product, cross product
18 % modulo operation
20 +, - addition, subtraction
22 <<, >>, >>> bit shifting, vector transformation
24 <, >, <=, >=, ==, ~=, ClockwiseFrom comparison
26 != inequality
28 &, |, ^ bitwise AND, OR and XOR
30 &&, ^^ logical AND and XOR
32 || logical OR
34 +=, -=, *=, /= combined arithmetic assignment
40 $, @ string concatenation
44 $=, @= combined concatenation and assignment
45 -= substring removal
- = assignment

Note that string operations, bitwise integer operations and logical AND, OR and XOR bind less tightly than comparison operators. See Object operators(RTNP, U1, UT, U2, U2XMP, UE2Runtime, UT2003, UT2004, UDK, UT3) for detailed descriptions of the operators.

Operator associativity[edit]

To make it short, all regular infix operators of the same precedence are left-associative and there's no way to change that. a + b + c + d is always evaluated as ((a + b) + c) + d. While this doesn't change the general evaluation order of the operands, it may have an impact on the type of operation and the result. For example, consider the following code:

local vector u, v;
local float f, g;
 
u = v * f * g;

This code evaluates as (v * f) * g, that means both operators are vector * float. If this was written as v * (f * g), the second operator would actually be float * float. It doesn't matter much for the result in this case, but you will have to keep this effect in mind for int operations like division or bit shifting. For these operations it matters which order you perform the operations. For example the expression i / n * n returns the largest multiple of n that is less than or equal to i if both variables are declared of type int, while i * n / n results in i again.

You might find the left-associativity counter-intuitive if you want to do something like:

local float a, b, c;
 
a *= b *= c;

This won't compile because it's equal to (a *= b) *= c and you cannot pass complex expressions into out parameters. What you really want is a *= (b *= c);, which requires the parentheses due to left-associativity of all operators.

Special operators[edit]

The following operators are not declared anywhere in UnrealScript, they "simply exist" and have special behavior that cannot be put in any kind of declaration available in the language.

Assignment operator[edit]

The assignment operator = implicitly exists for all possible data types, except static arrays. It could informally be described with the following declaration:

native static final operator(255) = (out type variable, type value);

Note that assignments in UnrealScript do not return any value. Unlike in many other languages where you could "chain" assignments together, an expression like a = b = c; results in a compiler error. Also note that assignment (unlike the combined assignment operators declared in the Object class) has the highest possible precedence value, so you never need to "help" the compiler by putting the value to assign (or the variable to assign to) in parentheses.

The assignment operator is the only valid way to alter the length property of a dynamic array. Using out parameters, including those of the combined assignment operators, can cause memory leaks and GPF crashes.

Struct comparison operators[edit]

If the operators == and != are not declared for the operands' types and both operands are of the same struct type, the hard-coded struct (in)equality operators are used. These simply compare the in-memory data of the two struct values.

Note that this type of comparison does not work for dynamic arrays as struct members, which will always look different unless both compared arrays are empty. Also, there is a compiler bug that allows you to compare dynamic arrays of structs, while the compiler would throw an error if dynamic arrays of any non-struct type are compared. The compiler will apply generic struct comparison to the two dynamic array variables, which has the same result (equal only if empty) as if the arrays were struct members.

Conditional operator[edit]

In Unreal Engine 3 and some Unreal Engine 2 games, a special ternary operator exists that works similar to the If...Else statement, except that it returns a value. The general syntax for this conditional operator is:

condition ? expression_if_true : expression_if_false

(Instead of ?, you might need to use ?? in Unreal Engine 2 implementations.)

Like If...Else, this operators first evaluates its condition expression and then only evaluates one of the two other expressions, depending on the condition. The condition must return a bool value, the return types of the other two expressions must match exactly as they are used as the return type of the conditional operator. Automatic type conversion for numeric values does not apply here!

New operator[edit]

The special operator new can be used to create non-Actor objects. In its simplest form it works like a prefix operator, but it can also take additional parameters similar to a function call. The general syntax for using the new operator is as follows:

new [( [outer [, name [, flags]]] )] class [( template )]

Arguments:

outer 
The value for the new object's Outer variable. The default value is None, which means the object is created within the "transient package", a virtual package for runtime objects.
If the class to create an object from is declared within another class, then the outer argument must be an object of that class or a subclass.
name 
The name for the new object. Unreal Engine 1 expects a name value here, but starting with Unreal Engine 2 this must be a string value. Omitting the name or passing "None" or the empty string assigns a default name consisting of the class name and a number, in Unreal Engine 3 separated by an underscore character. Unreal Engine 2 might not append the number unless the engine was started with the commandline parameter -makenames. Specifying the name of an existing object of the same class within the same outer object may have unexpected results if the existing object is still in use as it will be replaced by the new object.
flags 
Specifies object flags for the new object. Only use this if you know what you're doing!
class 
The class to create an object of. The specified class must not be abstract or extend Actor. (Actors must be created with the Spawn() function.)
template 
(only Unreal Engine 3) An object whose properties (except those declared in the Object class) are copied to the new object. The template must be of the same class as the new object or of a parent class.

Some examples for Unreal Engine 3:

local Object O;
O = new class'TestClass';
O = new(Self) class'TestClass'; // O.Outer == Self
O = new(None, 'ExampleObject') class'TestClass'; // O.Name == 'ExampleObject'
O = new(None, '', RF_Transient) class'TestClass'; // makes O transient even if TestClass isn't
O = new(None, '', 0) class'TestClass' (TestClass'TemplateObject'); // duplicates TemplateObject

The (compile-time) return type of the new operator corresponds to the (compile-time) metatype of the specified class argument:

local class<WebApplication> WAClass< SEMI >
local class<Object> OClass< SEMI >
local Object O;
local WebApplication WA;
 
WAClass = class'UTServerAdmin';
OClass = class'UTServerAdmin';
 
WA = new class'UTServerAdmin'; // works because UTServerAdmin extends WebApplication
WA = new WAClass< SEMI > // works because the metaclass of WAClass is WebApplication
O  = new WAClass< SEMI > // works because WebApplication extends Object
O  = new OClass< SEMI >  // works because the metaclass of OClass is Object
WA = new OClass< SEMI >  // type mismatch error (Object doesn't extend WebApplication!)

Only in Unreal Engine 2 the newly created object receives a call to its Created() function before the new operator returns. Other engine generations do not provide any generic initialization events.