Nestharus
o-o
- Reaction score
- 84
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?
What are disadvantages?
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:
Template
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
System Code
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(039;hpea039;)
//as you can see, fields of a given field can be accessed
set demo.open = 5
set demo.uType = 039;hfoo039; //open is now 0
//because of Field.push() in
//this property
set demo.open = 10
endmethod
endstruct
System Code
JASS:
//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