• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[System] Stacked Fields

Level 31
Joined
Jul 10, 2007
Messages
6,306
Stacked Fields

Credits to Lyerae for formatting and helping me write better docs =D

Introduction
When you try to visualize a stack, think of a stack of plates in a buffet. You can only ever put a plate on to the top of the stack and take a plate off of the top of the stack.

Stacked Fields takes a struct, turns it into a stack and then puts a delegate of that stack into your object.

What are advantages of this?
  • It's much easier to make objects with multiple stacked fields.
  • Because the fields are objects, you can organize data however you like.
  • Creating and destroying complex objects with multiple field stacks is simple.
  • You can have field stacks in field stacks

What are disadvantages?
  • These stacks aren't meant to be iterated through. The stack fields are more like adding undo functionality to your fields.
  • Crazy templates required to use this
  • one extra variable and one extra method call

The array version is faster but limits total instances of a given field to 8191
Hashtable version is slower but can have 8191*8192 total instances of a field (1 stack field per instance of your struct that can have up to 8191 nodes)

There is no library, so you can just paste this into your map and start using it.

Documentation:
JASS:
//////////////////////////////////////////////////////////////////////
//
//	Field Interface: Private
//
//	private keyword HASH_FieldStructName
//			For access to global that tells whether or not to use a hashtable.
//			Use this with static ifs on restore/archive.
//
//	public method restore takes hashtable h, integer instance returns nothing
// 		    Sets struct values to the hashtable record provided (Hashtable version only).
//
//	public method archive takes hashtable h, integer instance returns nothing
//  		Saves struct values into the hashtable record provided (Hashtable version only).
//
//	public method allocate takes nothing returns nothing
//  		Resets variables and create handles.
//
//	public method deallocate takes nothing returns nothing
// 		    Removes and recycles objects to prevent leaks.
//
//======================================
//
//	Protected API: (FieldName.method)
//
//	public method push takes nothing returns nothing
//
//	public method pop takes nothing returns nothing
//
//	public method popCount takes integer steps returns nothing
//
//	public method clear takes nothing returns nothing
//		Clears the stack.
//
//	public method operator empty takes nothing returns boolean
//		Checks whether the stack is empty or not.
//
//	public method operator size takes nothing returns integer
//		Returns the current size of the stack.
//
//	public static method create takes nothing returns thistype
//
//	public method destroy takes nothing returns nothing
//
//
//======================================
//
//	Private API
//
//	textmacro MAKE_FIELD_STACK takes FIELD_STRUCT, USE_HASH
//		Creates a private field stack within a scope as well as modules for using it
//		Put it below your FIELD_STRUCT!
//
//	module $FIELD_STRUCT$Stack
// 		Implement the field stack that you made
//             The name of the module is FieldStructName+Stack
//
//	$FIELD_STRUCT$
// 		Use this to retrieve base methods if you overrode any.
//      Also use this to retrieve stack methods from the protected API
//      Name of the field is FieldStructName
//
//////////////////////////////////////////////////////////////////////

Template
JASS:
scope Template
    globals
        private constant boolean USE_HASH = true
    endglobals
    
    private keyword HASH_Field
    private struct Field extends array
        public integer val
        
        public method allocate takes nothing returns nothing
            set val = 0
        endmethod
        
        public method deallocate takes nothing returns nothing
            set val = 0
        endmethod
        
        static if HASH_Field then
            private static key k_val
            
            public method restore takes hashtable h, integer instance returns nothing
                set val = LoadInteger(h, instance, k_val)
            endmethod
            
            public method archive takes hashtable h, integer instance returns nothing
                call SaveInteger(h, instance, k_val, val)
            endmethod
        endif
    endstruct
    //! runtextmacro MAKE_FIELD_STACK("Field", "USE_HASH")
    
    struct HashObject extends array
        implement FieldStack
        
        public static method create takes nothing returns thistype
            return allocateField()
        endmethod
        
        public method destroy takes nothing returns nothing
            call deallocateField()
        endmethod
    endstruct
endscope

Demo 1 - Example of a single field stack object that handles evolution/devolution for units
http://www.thehelper.net/forums/showthread.php?t=151988

Walkthrough Demonstration
JASS:
scope Walkthrough
    //I typically either go all hash or all array
    //You can make some things specifically hash or specifically array
    
    //I use a global variable like this to determines whether to
    //use a hash stack or an array stack
    globals
        private constant boolean USE_HASH = true
    endglobals
    
    //Field struct is a stacked struct
    //  good advice for a Field object is that the only thing you should do within it is
    //  basic allocation (only create structs and handles and null everything else)
    //
    //  deallocateion (destroy structs and handles and null handles)
    //
    //  restore (load all fields)
    //
    //  archive (save all fields)
    
    //this keyword is so that the HASH_Field global can be accessed
    //use this global in a static if on hashtable only methods
    private keyword HASH_Field
    private struct Field extends array
        public integer val //val is set by main object
        public unit unit //unit is created by main object
        public integer open
        
        public method allocate takes nothing returns nothing
            //create your hanldes and structs here and null all fields that aren't otherwise set
            
            //if you have nothing in here, make a primitive and just set it to 0 so that
            //this method inlines because nulling a var is faster than calling a method
            
            set val = 0
            set open = 0
            set unit = null
        endmethod
        
        public method deallocate takes nothing returns nothing
            //this is where anything that leaks is cleaned up (structs and handles)
            //null your handles!
            call RemoveUnit(unit)
            set unit = null
        endmethod
        
        //if use hash then enable methods, otherwise disable them
        static if HASH_Field then
            //each hashtable field requires the use of a key for restore/archive
            //i use k_varName for keys
            private static key k_val
            private static key k_unit
            private static key k_open
        
            public method restore takes hashtable h, integer instance returns nothing
                //restore is used to restore the fields of your Data object
                //you should only be loading values here, nothing else
                //leave other stuff for your main object to deal with
                set val = LoadInteger(h, instance, k_val)
                set unit = LoadUnitHandle(h, instance, k_unit)
                set open = LoadInteger(h, instance, k_open)
            endmethod
            
            public method archive takes hashtable h, integer instance returns nothing
                //archive is used to archive your fields
                //you should only be saving values here, nothing else
                //leave other stuff for your main object to deal with
                call SaveInteger(h, instance, k_val, val)
                call SaveUnitHandle(h, instance, k_unit, unit)
                call SaveInteger(h, instance, k_open, open)
            endmethod
        endif
    endstruct
    //this is where you make your field stack
    //note I passed in Field
    //! runtextmacro MAKE_FIELD_STACK("Field", "USE_HASH")
    
    //this is where you make your actual object
    struct ObjectDemo extends array
        implement FieldStack //this is how you implement your field
                           //note that the name of the module is your field object + Stack
        
        //allocate and deallocate are protected
        
        //proprties
        //left the open field public
        
        //to access things you overrode, use the field object name
        public method operator unit takes nothing returns unit
            return Field.unit
        endmethod
        
        //to access stack methods, you must always use the Field name
        //as ambiguity occurs with multiple fields
        private method operator unit= takes unit u returns nothing
            call Field.push()
            set Field.unit = u
            set Field.val = GetUnitTypeId(u)
        endmethod
        
        public method operator uType takes nothing returns integer
            return Field.val
        endmethod
        
        public method operator uType= takes integer i returns nothing
            set unit = CreateUnit(Player(0), i, 0, 0, 0)
        endmethod
        
        //construction and destruction
        public static method create takes integer uType returns thistype
            //allocate is allocate+field object
            local thistype this = allocateField()
            set this.uType = uType
            return this
        endmethod
        
        public method destroy takes nothing returns nothing
            //deallocate is deallocate+field object
            call deallocateField()
        endmethod
    endstruct
endscope

struct Test extends array
    private static method onInit takes nothing returns nothing
        local ObjectDemo demo = ObjectDemo.create('hpea')
        
        //as you can see, fields of a given field can be accessed
        set demo.open = 5
        set demo.uType = 'hfoo' //open is now 0
                                //because of Field.push() in
                                //this property
        set demo.open = 10
    endmethod
endstruct

System Code
JASS:
library StackedFields

//2.1.0.0
//! textmacro MAKE_FIELD_STACK takes DATA, USE_HASH
    globals
        private constant boolean HASH_$DATA$ = $USE_HASH$
    endglobals
    
    private scope FieldScopeLayer1$DATA$
        private scope FieldScope
            private keyword create
            private keyword destroy
            
            //HASH CODE
            static if $USE_HASH$ then
            
                private struct FieldXXX extends array
                    private static hashtable fieldStack = InitHashtable()
                    private static integer instanceCount = 0
                    private static thistype array recycle
                    private static integer recycleCount = 0
                    
                    private integer count
                    
                    private delegate $DATA$ data
                    
                    private method operator instance takes nothing returns integer
                        return this*8192+count
                    endmethod
                    
                    private method allocate takes nothing returns nothing
                        call data.allocate()
                    endmethod
                    
                    private method deallocate takes nothing returns nothing
                        call data.deallocate()
                    endmethod
                    
                    public static method create takes nothing returns thistype
                        local thistype this 
                        if (recycleCount != 0) then
                            set recycleCount = recycleCount - 1
                            set this = recycle[recycleCount]
                        else
                            set instanceCount = instanceCount + 1
                            set this = instanceCount
                        endif
                        
                        set data = this
                        call allocate()
                        
                        return this
                    endmethod
                    
                    private method operator hasNext takes nothing returns boolean
                        return count != 0
                    endmethod
                    
                    public method operator empty takes nothing returns boolean
                        return count == 0
                    endmethod
                    
                    private method restore takes nothing returns nothing
                        call data.restore(fieldStack, instance)
                        call FlushChildHashtable(fieldStack, instance)
                    endmethod
                    
                    private method archive takes nothing returns nothing
                        static if $USE_HASH$ then
                        
                        call data.archive(fieldStack, instance)
                        
                        endif
                    endmethod
                    
                    public method push takes nothing returns nothing
                        call archive()
                        set count = count + 1
                        call allocate()
                    endmethod
                    
                    public method pop takes nothing returns nothing
                        if (hasNext) then
                            set count = count - 1
                            call deallocate()
                            call restore()
                        endif
                    endmethod
                    
                    public method operator size takes nothing returns integer
                        return count
                    endmethod
                    
                    public method destroy takes nothing returns nothing
                        loop
                            call deallocate()
                            exitwhen empty
                            set count = count - 1
                            call restore()
                        endloop
                        
                        set recycle[recycleCount] = this
                        set recycleCount = recycleCount + 1
                    endmethod
                    
                    public method clear takes nothing returns nothing
                        loop
                            exitwhen empty
                            set count = count - 1
                            call deallocate()
                            call restore()
                        endloop
                    endmethod
                    
                    public method popCount takes integer steps returns nothing
                        if (count <= steps) then
                            call clear()
                        else
                            set steps = count - steps
                            
                            loop
                                exitwhen count == steps
                                set count = count - 1
                                call deallocate()
                                call restore()
                            endloop
                        endif
                    endmethod
                endstruct
            
            //ARRAY CODE
            else
                
                private struct FieldXX extends array
                    private static integer instanceCount = 0
                    private static thistype array recycle
                    private static integer recycleCount = 0
                    
                    private static integer instanceCount2 = 0
                    private static thistype array recycle2
                    private static integer recycleCount2 = 0
                    
                    private delegate $DATA$ first
                    
                    private integer count
                    
                    private method allocateNode takes nothing returns nothing
                        local $DATA$ node
                        if (recycleCount2 != 0) then
                            set recycleCount2 = recycleCount2 - 1
                            set node = recycle2[recycleCount2]
                        else
                            set instanceCount2 = instanceCount2 + 1
                            set node = instanceCount2
                        endif
                        call node.allocate()
                        set thistype(node).next = first
                        set first = node
                        set count = count + 1
                    endmethod
                    
                    private method deallocateNode takes nothing returns nothing
                        set recycle2[recycleCount2] = first
                        set recycleCount2 = recycleCount2 + 1
                        call first.deallocate()
                        set first = thistype(first).next
                    endmethod
                    
                    //stack pointer
                    private thistype next
                    
                    public static method create takes nothing returns thistype
                        local thistype this 
                        if (recycleCount != 0) then
                            set recycleCount = recycleCount - 1
                            set this = recycle[recycleCount]
                        else
                            set instanceCount = instanceCount + 1
                            set this = instanceCount
                        endif
                        
                        call allocateNode()
                        set count = 0
                        
                        return this
                    endmethod
                    
                    private method operator hasNext takes nothing returns boolean
                        return thistype(first).next != 0
                    endmethod
                    
                    public method operator empty takes nothing returns boolean
                        return thistype(first).next == 0
                    endmethod
                    
                    public method push takes nothing returns nothing
                        call allocateNode()
                    endmethod
                    
                    public method pop takes nothing returns nothing
                        if (hasNext) then
                            call deallocateNode()
                            set count = count - 1
                        endif
                    endmethod
                    
                    public method operator size takes nothing returns integer
                        return count
                    endmethod
                    
                    public method popCount takes integer steps returns nothing
                        if (count <= steps) then
                            set steps = count
                            set count = 0
                        else
                            set count = count - steps
                        endif
                        
                        loop
                            exitwhen steps == 0
                            call deallocateNode()
                            set steps = steps -1
                        endloop
                    endmethod
                    
                    public method destroy takes nothing returns nothing
                        loop
                            exitwhen first == 0
                            call deallocateNode()
                        endloop
                        
                        set recycle[recycleCount] = this
                        set recycleCount = recycleCount + 1
                    endmethod
                    
                    public method clear takes nothing returns nothing
                        loop
                            exitwhen empty
                            call deallocateNode()
                        endloop
                        
                        set count = 0
                    endmethod
                endstruct
                
            endif
        
            public module FieldModStruct
                private delegate $DATA$ fieldX
                
                static if $USE_HASH$ then
                    public method operator $DATA$ takes nothing returns FieldXXX
                        return fieldX
                    endmethod
                else
                    public method operator $DATA$ takes nothing returns FieldXX
                        return fieldX
                    endmethod
                endif
                
                public static method allocate$DATA$ takes nothing returns thistype
                    static if $USE_HASH$ then
                        local thistype this = FieldXXX.create()
                    else
                        local thistype this = FieldXX.create()
                    endif
                    set fieldX = this
                    return this
                endmethod
                
                public method deallocate$DATA$ takes nothing returns nothing
                    static if $USE_HASH$ then
                        call FieldXXX(fieldX).destroy()
                    else
                        call FieldXX(fieldX).destroy()
                    endif
                endmethod
            endmodule
        endscope
        
        public module FieldModStruct3
            implement FieldScope_FieldModStruct
        endmodule
        
        private keyword allocate$DATA$
        private keyword deallocate$DATA$
        private keyword restore
        private keyword archive
    endscope
    
    private module $DATA$Stack
        implement FieldScopeLayer1$DATA$_FieldModStruct3
    endmodule
//! endtextmacro

endlibrary
 
Last edited:
Top