14 years
edited 8 years
Many people continue to ask me what the difference is between struct Hello and struct Hello extends array is.
In vJASS, regular structs create some extra code in the background to make themselves work.
outputs
outputs
As can be seen, trigger evaluations pop up and useless variables get generated.
generates
constant integer si__a=1
Not bad, but this means that this sort of thing will no longer work
meaning that the allocation and deallocation of structs is left up to the coders.
So let's look on how structs are actually allocated (getting rid of the virtually useless double free protection and getting rid of allocate/deallocate as they might as well be put into create and destroy).
As structs are created, the total number of structs generated continues to increase. A counter is needed in order to track how many structs have been created.
private static integer instanceCount = 0
As structs are destroyed, their instances need to be recycled.
Code output from above (does same thing as first example w/o double free protection)
A bit more work, but a bit more optimal. What about extending structs?
Extending structs is done with delegates (also allows members to be overriden).
Outputs
No trigger arrays, no trigger evaluations, and no wasted local variables. It even allows you to make one struct extend off of multiple structs.
Multi-Struct Extension
Which happily outputs
In vJASS, regular structs create some extra code in the background to make themselves work.
1 2 struct a3 endstruct4
outputs
1 2 constant integer si__a=1
3 integer si__a_F=0
4 integer si__a_I=0
5 integer array si__a_V
6 7 //Generated allocator of a8 function s__a__allocate takes nothing returns integer
9 local integer this=si__a_F //first node on recycle stack
10 if (this!=0) then
11 set si__a_F=si__a_V[this] //set stack to next node (like stack.next)
12 else13 set si__a_I=si__a_I+1
14 set this=si__a_I
15 endif16 if (this>8190) then //protection against too many structs
17 return 0
18 endif19 20 set si__a_V[this]=-1 //set stack to -1 (stack.next = -1)
21 return this
22 endfunction23 24 //Generated destructor of a25 function s__a_deallocate takes integer this returns nothing
26 if this==null then //don't deallocate null instance
27 return28 elseif (si__a_V[this]!=-1) then //double free protection
29 return30 endif31 set si__a_V[this]=si__a_F //set this.next = stack
32 set si__a_F=this //set stack = this
33 endfunction34
1 2 struct b extends a
3 endstruct4
outputs
1 2 constant integer si__a=1
3 integer si__a_F=0
4 integer si__a_I=0
5 integer array si__a_V
6 constant integer si__b=2
7 integer array si__a_type
8 trigger array st__a_onDestroy //Trigger array?!?!
9 integer f__arg_this
10 11 //Generated allocator of a12 function s__a__allocate takes nothing returns integer
13 local integer this=si__a_F
14 if (this!=0) then
15 set si__a_F=si__a_V[this]
16 else17 set si__a_I=si__a_I+1
18 set this=si__a_I
19 endif20 if (this>8190) then
21 return 0
22 endif23 24 set si__a_type[this]=1
25 set si__a_V[this]=-1
26 return this
27 endfunction28 29 //Generated destructor of a30 function sc__a_deallocate takes integer this returns nothing
31 if this==null then
32 return33 elseif (si__a_V[this]!=-1) then
34 return35 endif36 set f__arg_this=this
37 call TriggerEvaluate(st__a_onDestroy[si__a_type[this]]) //AHH!!
38 set si__a_V[this]=si__a_F
39 set si__a_F=this
40 endfunction41 42 //Generated allocator of b43 function s__b__allocate takes nothing returns integer
44 local integer this=s__a__allocate()
45 local integer kthis //why??
46 if(this==0) then
47 return 0
48 endif49 set si__a_type[this]=2
50 set kthis=this //... doesn't actually do anything
51 52 return this
53 endfunction54
As can be seen, trigger evaluations pop up and useless variables get generated.
1 2 struct a extends array
3 endstruct4
generates
constant integer si__a=1
Not bad, but this means that this sort of thing will no longer work
1 2 local a myStruct = a.create()
3 call myStruct.destroy()
4
meaning that the allocation and deallocation of structs is left up to the coders.
So let's look on how structs are actually allocated (getting rid of the virtually useless double free protection and getting rid of allocate/deallocate as they might as well be put into create and destroy).
As structs are created, the total number of structs generated continues to increase. A counter is needed in order to track how many structs have been created.
private static integer instanceCount = 0
As structs are destroyed, their instances need to be recycled.
1 2 private static thistype recycle = 0 //next recycled instance
3 private thistype recycleNext //recycled stack
4
1 2 struct MyStruct extends array
3 private static integer instanceCount = 0
4 private static thistype recycle = 0
5 private thistype recycleNext
6 7 static method create takes nothing returns thistype
8 local thistype this
9 10 //first check to see if there are any structs waiting to be recycled11 if (recycle == 0) then
12 //if recycle is 0, there are no structs, so increase instance count13 set instanceCount = instanceCount + 1
14 set this = instanceCount
15 else16 //a struct is waiting to be recycled, so use it17 set this = recycle
18 set recycle = recycle.recycleNext
19 endif20 21 //perform creation code22 23 return this
24 endmethod25 26 method destroy takes nothing returns nothing
27 //add to recycle stack28 set recycleNext = recycle
29 set recycle = this
30 endmethod31 endstruct32
Code output from above (does same thing as first example w/o double free protection)
1 2 constant integer si__MyStruct=1
3 integer s__MyStruct_instanceCount= 0
4 integer s__MyStruct_recycle= 0
5 integer array s__MyStruct_recycleNext
6 7 function s__MyStruct_create takes nothing returns integer
8 local integer this
9 if ( s__MyStruct_recycle == 0 ) then
10 set s__MyStruct_instanceCount=s__MyStruct_instanceCount + 1
11 set this=s__MyStruct_instanceCount
12 else13 set this=s__MyStruct_recycle
14 set s__MyStruct_recycle=s__MyStruct_recycleNext[s__MyStruct_recycle]
15 endif16 return this
17 endfunction18 19 function s__MyStruct_destroy takes integer this returns nothing
20 set s__MyStruct_recycleNext[this]=s__MyStruct_recycle
21 set s__MyStruct_recycle=this
22 endfunction23
A bit more work, but a bit more optimal. What about extending structs?
Extending structs is done with delegates (also allows members to be overriden).
1 2 struct Mystruct2 extends array
3 //delegate stores pointers to parent struct4 //this means that one can extend off of multiple structs5 private delegate MyStruct MyStruct
6
7 static method create takes nothing returns thistype
8 //base instance off of parent struct9 local thistype this = MyStruct.create()
10 //store pointer into delegate11 set MyStruct = this
12
13 return this
14 endmethod15
16 method destroy takes nothing returns nothing
17 //simply destroy18 call MyStruct.destroy()
19 endmethod20 endstruct21
Outputs
1 2 constant integer si__MyStruct=1
3 integer s__MyStruct_instanceCount= 0
4 integer s__MyStruct_recycle= 0
5 integer array s__MyStruct_recycleNext
6 constant integer si__Mystruct2=2
7 integer array s__Mystruct2_MyStruct
8 9 function s__MyStruct_create takes nothing returns integer
10 local integer this
11 if ( s__MyStruct_recycle == 0 ) then
12 set s__MyStruct_instanceCount=s__MyStruct_instanceCount + 1
13 set this=s__MyStruct_instanceCount
14 else15 set this=s__MyStruct_recycle
16 set s__MyStruct_recycle=s__MyStruct_recycleNext[s__MyStruct_recycle]
17 endif18 return this
19 endfunction20 21 function s__MyStruct_destroy takes integer this returns nothing
22 set s__MyStruct_recycleNext[this]=s__MyStruct_recycle
23 set s__MyStruct_recycle=this
24 endfunction25 26 function s__Mystruct2_create takes nothing returns integer
27 local integer this= s__MyStruct_create()
28 set s__Mystruct2_MyStruct[this]=this
29 return this
30 endfunction31 32 function s__Mystruct2_destroy takes integer this returns nothing
33 call s__MyStruct_destroy(s__Mystruct2_MyStruct[this])
34 endfunction35
No trigger arrays, no trigger evaluations, and no wasted local variables. It even allows you to make one struct extend off of multiple structs.
Multi-Struct Extension
1 2 struct MyStruct extends array
3 private static integer instanceCount = 0
4 private static thistype recycle = 0
5 private thistype recycleNext
6 7 static method create takes nothing returns thistype
8 local thistype this
9 if (recycle == 0) then
10 set instanceCount = instanceCount + 1
11 set this = instanceCount
12 else13 set this = recycle
14 set recycle = recycle.recycleNext
15 endif16 return this
17 endmethod18 19 method destroy takes nothing returns nothing
20 set recycleNext = recycle
21 set recycle = this
22 endmethod23 endstruct24 25 struct MyStruct2 extends array
26 private static integer instanceCount = 0
27 private static thistype recycle = 0
28 private thistype recycleNext
29 30 static method create takes nothing returns thistype
31 local thistype this
32 if (recycle == 0) then
33 set instanceCount = instanceCount + 1
34 set this = instanceCount
35 else36 set this = recycle
37 set recycle = recycle.recycleNext
38 endif39 return this
40 endmethod41 42 method destroy takes nothing returns nothing
43 set recycleNext = recycle
44 set recycle = this
45 endmethod46 endstruct47 48 struct Mystruct3 extends array
49 private delegate MyStruct MyStruct
50 private delegate MyStruct2 MyStruct2
51
52 static method create takes nothing returns thistype
53 local thistype this = MyStruct.create()
54 set MyStruct = this
55
56 //use first pointer as current struct's instance57 set MyStruct2 = MyStruct2.create()
58
59 //creation code60
61 return this
62 endmethod63
64 method destroy takes nothing returns nothing
65 //destroy both structs66 call MyStruct.destroy()
67 call MyStruct2.destroy()
68 endmethod69 endstruct70
Which happily outputs
1 2 constant integer si__MyStruct=1
3 integer s__MyStruct_instanceCount= 0
4 integer s__MyStruct_recycle= 0
5 integer array s__MyStruct_recycleNext
6 constant integer si__MyStruct2=2
7 integer s__MyStruct2_instanceCount= 0
8 integer s__MyStruct2_recycle= 0
9 integer array s__MyStruct2_recycleNext
10 constant integer si__Mystruct3=3
11 integer array s__Mystruct3_MyStruct
12 integer array s__Mystruct3_MyStruct2
13 14 function s__MyStruct_create takes nothing returns integer
15 local integer this
16 if ( s__MyStruct_recycle == 0 ) then
17 set s__MyStruct_instanceCount=s__MyStruct_instanceCount + 1
18 set this=s__MyStruct_instanceCount
19 else20 set this=s__MyStruct_recycle
21 set s__MyStruct_recycle=s__MyStruct_recycleNext[s__MyStruct_recycle]
22 endif23 return this
24 endfunction25 26 function s__MyStruct_destroy takes integer this returns nothing
27 set s__MyStruct_recycleNext[this]=s__MyStruct_recycle
28 set s__MyStruct_recycle=this
29 endfunction30 31 32 function s__MyStruct2_create takes nothing returns integer
33 local integer this
34 if ( s__MyStruct2_recycle == 0 ) then
35 set s__MyStruct2_instanceCount=s__MyStruct2_instanceCount + 1
36 set this=s__MyStruct2_instanceCount
37 else38 set this=s__MyStruct2_recycle
39 set s__MyStruct2_recycle=s__MyStruct2_recycleNext[s__MyStruct2_recycle]
40 endif41 return this
42 endfunction43 44 function s__MyStruct2_destroy takes integer this returns nothing
45 set s__MyStruct2_recycleNext[this]=s__MyStruct2_recycle
46 set s__MyStruct2_recycle=this
47 endfunction48 49 50 function s__Mystruct3_create takes nothing returns integer
51 local integer this= s__MyStruct_create()
52 set s__Mystruct3_MyStruct[this]=this
53 set s__Mystruct3_MyStruct2[this]=s__MyStruct2_create()
54 return this
55 endfunction56 57 function s__Mystruct3_destroy takes integer this returns nothing
58 call s__MyStruct_destroy(s__Mystruct3_MyStruct[this])
59 call s__MyStruct2_destroy(s__Mystruct3_MyStruct2[this])
60 endfunction61