Skip to content

9B. Appendix Static or Dynamic instantiation

phu54321 edited this page Jan 18, 2018 · 5 revisions

In this appendix, we will use Piece struct defined as this.

object Piece {
    var player;
    var unitType;
    var lastMovedTurn;
};
```.

There are two ways to instantiate(=create) an object. Static instantiation, and Dynamic instantiation.

  • Static instantiation: const X = Piece();. We've already used this in chapter 4 when we create an array with const array = EUDArray(64);. You can use this when creating non-escaping instances, such as global instances.
  • Dynamic instantiation: const X = Place.alloc();. You can use this anywhere. You should free the allocated object with .free()

Using static instances is faster than using dynamic instances. Also you can avoid overhead of freeing objects. So you'd like to use static instance whenever possible. This appendix explain exactly when you can use static instance.

Every value in euddraft has fixed memory address. So every value is persistent. For example, consider this code.

function x() {
    var y;
    y++;
    return y;
}

Because every value in euddraft has fixed memory address, y also is fixed in memory. So every time x() is called, y refers to the same memory space.

This is equivalent to the following C code.

int x() {
    static int y = 0;  // Everything in euddraft is static. They have a fixed memory address
    y++;
    return y;
}

Initially y is initialized to 0, just like global variables in C. Calling x() increments y and returns the incremented value. So calling x() three times returns three different return values.

    var a = x();  // returns 1
    var b = x();  // returns 2
    var c = x();  // returns 3

Please don't use this property of euddraft to write your own code. The fact that every variable is persistent only holds when you give no initial value to the variable. If you give variable initial value like var y = 0;, then the value of y gets rewritten every time the function x is called. euddraft behaves differently when you give or don't give an initial value, so this could lead to confusion. You're not expected to write code relying on this behavior, and I reserve the right to change this behavior for optimization or new features, without any notice.

Same applies for objects. All objects, local, global, or temporary, have fixed memory space. Consider this code for example.

function main() {
    const X = EUDArray(10);
    for(var i = 0 ; i < 10 ; i++) {
        X[i] = EUDArray(10);
    })
}

You may think that we're assigning a separate EUDArray(10) instance for each cell of X, but this code don't act like that.The code above is equivalent to:

const _t0 = EUDArray(10);  // Even intermediate values are static
const _t1 = EUDArray(10);  // Even intermediate values are static

function main() {
    const X = _t0;
    for(var i = 0 ; i < 10 ; i++) {
        X[i] = _t1;
    })
}

When doing function call, assigning to members, or assigning to variable, only the address of the object gets transfered, so X[i] = _t1; means X[i] = (address of _t1);.

This is not what you might expect. All cells of X points to the same _t1 EUDArray.

If you're from C, then this concept may be hard to gasp. Think that except for numbers, everything is a pointer. _t1 is a pointer to an EUDArray. X[i] = _t1; means assigning the value of _t1 (which is the address of underlying EUDArray) to i'th item of EUDArray X is pointing to. So the above code translates to:

const EUDArray* _t1 = create_eudarray(10);
const EUDArray* _t2 = create_eudarray(10);

int main() {
    const EUDArray* X = _t1;
    for(int i = 0 ; i < 10 ; i++) {
        X->item[i] = (int)(_t2);  // _t2 is interpreted as a number.
    }
}

We need to use dynamic instantiation like X[i] = EUDArray.alloc(10); to assign 10 different EUDArray to each X cell. Sadly EUDArray cannot be dynamically instantiated, so it doesn't have .alloc(). So we cannot fix this code.

However, objects in epScript are dynamically constructible. Consider this code for example.

Think 'construct' as a synonym for 'instantiate' in euddraft.

const array = EUDArray(64);

function main() {
    for(let y = 0 ; y < 8 ; y++) {
        for(let x = 0 ; x < 8 ; x++) {
            const index = y * 8 + x;
            array[index] = Piece();  // Piece is some object type
        }
    }
}

This is equivalent to:

const array = EUDArray(64);
const _t1 = Piece();

function main() {
    for(let y = 0 ; y < 8 ; y++) {
        for(let x = 0 ; x < 8 ; x++) {
            const index = y * 8 + x;
            array[index] = _t1;
        }
    }
    
    const p = Piece.cast(array[0]);
    p.player = 1;  //??
}

So we're assigning the same piece for all cells of array. So for example, if you modify one of the pieces with Piece.cast(array[0]).player = 1;, then every player member of pieces stored in array gets changed to 0 because, in fact all cells of array points to the same Piece object.

This code is equivalent to the following C code.

const EUDArray* array = create_eudarray(64);
const Piece* _t1 = create_piece();

int main() {
    for(int y = 0 ; y < 8 ; y++) {
        for(int x = 0 ; x < 8 ; x++) {
            const int index = y * 8 + x;
            array->item[index] = (int)_t1;
        }
    }

    const Piece* p = (Piece*)(array->item[0]);
    p->player = 1;
}

Every instance and variable acts like a global variable (like in const _t1 = Piece();) internally. Most simple way to avoid instances from unexpectedly being reused, is to not make local instances visible out of their declared loop. For example, array[index] = Piece(); won't work because the address of Piece() goes out of its declared inner for loop, and Piece() can be accessed outside its declared for loop via array[i]. Since new Piece is not generated every loop, the same piece gets reused every loop and the problem arises.

In summary, you can statically instantiate an type when you're certain that there are no pathway that a object could be mistakenly reused. In shorter terms, you can statically instantiate a type when the instance don't escape out of loops or functions. Hence, non-escaping instances. Most common cases of this are global instances. They can be statically instantiated since they are not inside any loops or functions.

It's up to you to make sure that the statically allocated instance won't escape any functions or loops. For example, you can statically instantiate EUDFuncPtr(2, 1) in the following code.

function adder(p, q) { return p + q; }
function x() {
    const fptr = EUDFuncPtr(2, 1)(adder);  // EUDFuncPtr(2, 1) is an object type, and we're statically instantiating its instance fptr.
    return fptr(3, 5);
    // Statically instantiated objects don't need to be freed.
}

EUDFuncPtr is discussed on chapter [TODO:Link to some chapter]. Basically, instance of EUDFuncPtr(2, 1) can hold point to any function accepting two parameters and returns 1 number.

On return fptr(3, 5);, only the computed result of function pointed by fptr is returned. fptr itself never escapes out of x function, so they can be statically instantiated.