GMS 2.3+ [TEXT TUTORIAL] Keyword "static" Explained (Part 2 : Execution Order)

GM Version: Studio 2.3+
Target Platform: Windows / All
Download: n/a
Links: n/a

[PROBLEM]
Today we will be talking about static variables initialization order... now you might ask:
"What? Is there a special order for static variables?"

[EXPLANATION]
So static keyword again! If you don't know what it is you should first take a look at my first tutorial on the subject:

[TEXT TUTORIAL] Keyword "static" Explained

Regarding execution order there are two rules to take into consideration:
  • static declarations in a given scope occur before everything else.
  • among themselves static declarations occur in the order they appear in the code.

Now that we have the basics lets look at some code.

(1) INITIAL SETUP
One of the places where we can experience the execution order and probably the most common one is inside constructor functions.
GML:
function MyConstructor() constructor {

    static staticReal = 10;

    instanceReal = 1;

    static staticString = "Hello";

    instanceFunc = function() {}

    static staticFunc = function() {}

}
The example above can look a little bit strange but believe me, it has its purpose.

(2) EXECUTION ORDER
I will now number the order in which lines get executed:
GML:
function MyConstructor() constructor {

    static staticReal = 10;  // (1)

    instanceReal = 1;   // (4)

    static staticString = "Hello";  // (2)

    instanceFunc = function() {}  // (5)

    static staticFunc = function() {}  // (3)
}
So as you can see static variables declarations are executed before the rest of the code.

(3) A LITTLE TIP
So when working with static always imagine you are pulling the static variables to start of the function/constructor, like so:
GML:
function MyConstructor() constructor {

    static staticReal = 10;  // (1)
    static staticString = "Hello";  // (2)
    static staticFunc = function() {}  // (3)

    instanceReal = 1;   // (4)
    instanceFunc = function() {}  // (5)

}

(4) IMPLICATIONS + INHERITANCE
So what are the implications of this?
GML:
function MyConstructor() constructor {

    show_debug_message(staticReal); // (2) Even though 'staticReal' definition is placed below this does NOT throw an error.

    static staticReal = 10;  // (1)

    show_debug_message(instanceReal) // (3) [ERROR] with non-static the execution flow is normal.

    instanceReal = 1;   // (4)

}
Though this doesn't look like a big deal it is when you start using inheritance, for example:
GML:
function MyParentConstructor() constructor {

    print();

    static value = 10;
    static print = function() {
        show_debug_message(value);
    }

    print();
}

function MyChildConstructor() : MyParentConstructor() constructor {

    print();

    static value = "hello"
    static print = function() {
        show_debug_message(value + " world");
    }

    print();

}

var _instance = new MyChildConstructor();
Before going any further, go ahead and try to find out what is the output of the above code.
I will wait... ;)

10
10
hello world
hello world

For those who didn't manager to follow I will again number the line execution.
Here is what happens when you create the instance of MyChildConstructor
GML:
function MyParentConstructor() constructor {

    print(); // (3) This is the first print -> 10

    static value = 10; // (1) This is the first one because parent code is execute before child code

    static print = function() { // (2)
        show_debug_message(value);
    }

    print(); // (4) This is the second print -> 10

}

function MyChildConstructor() : MyParentClass() constructor {

    print(); // (7) This is the third print -> "hello world"

    static value = "hello" // (5)

    static print = function() { // (6) this is overriding the parent function
        show_debug_message(value + " world");
    }

    print(); // (8) This is the fourth print -> "hello world"
}

(5) OVERRIDING
In the example above I introduced the concept of override this concept consists into changing the value of some function/variable inside a child constructor.
So a good practice when you want to override a function is using the following:
GML:
function MyParentConstructor() constructor {

    static print = function() { // (1) This function is declared
        show_debug_message("Hello");
    }
}

function MyChildConstructor() : MyParentClass() constructor {

    static print_MyParentClass = print; // (2) Here I store the old function in a new static variable (OPTIONAL)
    static print = function() { // (3) Now I redeclared the print function

        print_MyParentClass(); // As I stored the old declaration (2) I can still call it here.
        show_debug_message("World");
    }
}

var _instance = new MyChildConstructor();
_instance.print();

// Hello
// World
It is important to note one thing, in execution line (2) when I store the old function reference, I use a static variable and this is actually a MUST otherwise you would end with the code:
GML:
function MyParentConstructor() constructor {
    static print = function() { // (1) This function is declared
        show_debug_message("Hello");
    }
}

function MyChildConstructor() : MyParentClass() constructor {

    print_MyParentClass = print; // (3) This line now is executed after
    static print = function() { // (2) Now I redeclared the print function
        print_MyParentClass(); // As we didn't use static print_MyParentClass points to the child's definition (this function)
        show_debug_message("World");
    }
}

var _instance = new MyChildConstructor();

_instance.print(); // This will hang execution as the function will enter in a recursive loop.

[CONCLUSION]
And that's it, static keyword declarations are executed before every other line of code in a given scope.
And though this can allow for a lot of cool stuff it also needs attention.


That was quite a lot of information and a lot of examples so remember to go through it a couple of times to understand it well.

Here xD from xDGameStudios,
Good coding to you all.
 
Last edited:

gnysek

Member
We definitely need some way of overriding constructors, so both aren't executed...

But going back to article topic, it's 10/10! Are you also planning maybe article about structs/constructors and scoping (how self/other is tied to what and when, including method() )? This one also causes thousands of combinations, depending of where struct was created (game start, instance, another struct etc.).
 

gnysek

Member
If it will be same quality, then they both should be linked on YYG homepage, as how structs works isn't that intuitive in many aspects.
 
Excellent tutorial! Really easy to follow despite how complicated the topic is. Storing the parent's overridden function in a static var is exactly something I was struggling with figuring out a good way to do recently.

That will actually be the next tutorial :) I’m aiming for next week!
Awesome, the "other/self" stuff is some Inception level brain melting at times lol

If it will be same quality, then they both should be linked on YYG homepage, as how structs works isn't that intuitive in many aspects.
Also I agree with this, this explanation should be in the manual or on the blog or something!
 
Last edited:
Top