• 🏆 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!

[vJASS] LinkedList Modules

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
I made this since I can't quite find a linked-list library that provides users enough flexibility for various use-cases. This is one reason why Nestharus has so many variants in his linked-list scripts (Static/Non-static lists, Unique/Instantiated, etc.). While it's good in terms of modularity, it is tedious work to add so many libraries as requirements for the same general task, only with slight differences. This library does not claim to cover all those variants but it definitely provides sufficient flexibility in one library.

Another alternative would be Dirac's [Snippet] LinkedListModule that covers instantiated and static lists however with some restrictions. Implementing structs are required to extend array and it provides its own internal node allocator even in cases when there's no need for such allocation (this is what Nes' Unique list variants are for). Lastly, the way it modulates its features - by using textmacros, is not very neat imo.

This library does not have any of those restrictions in the mentioned libs.


LinkedList.j
JASS:
library LinkedList /* v1.3.0 https://www.hiveworkshop.com/threads/linkedlist-modules.325635/


    */uses /*

    */optional ErrorMessage /*  https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j


    *///! novjass

    /*
        Author:
            - AGD
        Credits:
            - Nestharus, Dirac, Bribe
                > For their scripts and discussions which I used as reference

        Pros:
            - Feature-rich (Can be)
            - Modular
            - Safety-oriented (On DEBUG_MODE, but not 100% fool-proof ofcourse)
            - Flexible (Does not enforce a built-in allocator - allows user to choose between a custom Alloc
              or the default vjass allocator, or neither)
            - Extensible (Provides interfaces)

        Note:
            If you are using using Dirac's 'LinkedListModule' library, you need to replace its contents with
            the compatibility lib provided alongside this library for all to work seamlessly.

    */
    |-----|
    | API |
    |-----|
    /*
    Note: All the fields except from 'prev' and 'next' are actually operators, so you might want to
          avoid using them from the interface methods that would be declared above them.
    =====================================================================================================
    List Fields Modules (For those who want to write or inline the core linked-list operations themselves)

      */module LinkedListFields/*

          */readonly thistype prev/*
          */readonly thistype next/*


      */module StaticListFields extends LinkedListFields/*

          */readonly static constant thistype sentinel/*
          */readonly static thistype front/*
          */readonly static thistype back/*
          */readonly static boolean empty/*


      */module ListFields extends LinkedListFields/*

          */readonly thistype front/*
          */readonly thistype back/*
          */readonly boolean empty/*

    =====================================================================================================
    Lite List Modules (Should be enough for most cases)

      */module LinkedListLite extends LinkedListFields/*

          */optional interface static method onInsert takes thistype node returns nothing/*
          */optional interface static method onRemove takes thistype node returns nothing/*
          */optional interface method onTraverse takes thistype node returns boolean/*

          */static method insert takes thistype prev, thistype node returns nothing/*
          */static method remove takes thistype node returns nothing/*

          */method traverseForwards takes nothing returns nothing/*
          */method traverseBackwards takes nothing returns nothing/*
                - Only present if onTraverse() is also present


      */module StaticListLite extends StaticListFields, LinkedListLite/*

          */static method pushFront takes thistype node returns nothing/*
          */static method popFront takes nothing returns thistype/*

          */static method pushBack takes thistype node returns nothing/*
          */static method popBack takes nothing returns thistype/*


      */module ListLite extends ListFields, LinkedListLite/*

          */method pushFront takes thistype node returns nothing/*
          */method popFront takes nothing returns thistype/*

          */method pushBack takes thistype node returns nothing/*
          */method popBack takes nothing returns thistype/*


      */module InstantiatedListLite extends ListLite/*

          */interface static method allocate takes nothing returns thistype/*
          */interface method deallocate takes nothing returns nothing/*
          */optional interface method onConstruct takes nothing returns nothing/*
          */optional interface method onDestruct takes nothing returns nothing/*

          */static method create takes nothing returns thistype/*
          */method destroy takes nothing returns nothing/*

    =====================================================================================================
    Standard List Modules

      */module LinkedList extends LinkedListLite/*

          */static method isLinked takes thistype node returns boolean/*


      */module StaticList extends StaticListLite, LinkedList/*

          */static method clear takes nothing returns nothing/*
          */static method flush takes nothing returns nothing/*


      */module List extends ListLite, LinkedList/*

          */static method makeHead takes thistype node returns nothing/*
          */method clear takes nothing returns nothing/*
          */method flush takes nothing returns nothing/*


      */module InstantiatedList extends InstantiatedListLite, List/*
 
    =====================================================================================================
    Feature-rich List Modules (For those who somehow need exotic linked-list operations)

      */module LinkedListEx extends LinkedList/*

          */static method move takes thistype prev, thistype node returns nothing/*
          */static method swap takes thistype nodeA, thistype nodeB returns nothing/*


      */module StaticListEx extends StaticList, LinkedListEx/*

          */static method contains takes thistype node returns boolean/*
          */static method getSize takes nothing returns integer/*
          */static method rotateLeft takes nothing returns nothing/*
          */static method rotateRight takes nothing returns nothing/*


      */module ListEx extends List, LinkedListEx/*

          */method contains takes thistype node returns boolean/*
          */method getSize takes nothing returns integer/*
          */method rotateLeft takes nothing returns nothing/*
          */method rotateRight takes nothing returns nothing/*


      */module InstantiatedListEx extends InstantiatedList, ListEx/*


    *///! endnovjass

    /*========================================= CONFIGURATION ===========================================
    *   Only affects DEBUG_MODE
    *   If false, throws warnings instead (Errors pauses the game (Or stops the thread) while warnings do not)
    */
    globals
        private constant boolean THROW_ERRORS = true
    endglobals
    /*====================================== END OF CONFIGURATION =====================================*/

    static if DEBUG_MODE then
        public function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
            static if LIBRARY_ErrorMessage then
                static if THROW_ERRORS then
                    call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
                else
                    call ThrowWarning(condition, SCOPE_PREFIX, methodName, structName, node, message)
                endif
            else
                if condition then
                    static if THROW_ERRORS then
                        call BJDebugMsg("|cffff0000[ERROR]|r [Library: " + SCOPE_PREFIX + "] [Struct: " + structName + "] [Method: " + methodName + "] [Instance: " + I2S(node) + "] : |cffff0000" + message + "|r")
                        call PauseGame(true)
                    else
                        call BJDebugMsg("|cffffcc00[WARNING]|r [Library: " + SCOPE_PREFIX + "] [Struct: " + structName + "] [Method: " + methodName + "] [Instance: " + I2S(node) + "] : |cffffcc00" + message + "|r")
                    endif
                endif
            endif
        endfunction
    endif

    private module LinkedListUtils
        method p_clear takes nothing returns nothing
            set this.next.prev = 0
            set this.prev.next = 0
            set this.prev = this
            set this.next = this
        endmethod
        method p_flush takes nothing returns nothing
            local thistype node = this.prev
            loop
                exitwhen node == this
                call remove(node)
                set node = node.prev
            endloop
        endmethod
    endmodule
    private module LinkedListUtilsEx
        implement LinkedListUtils
        method p_contains takes thistype toFind returns boolean
            local thistype node = this.next
            loop
                exitwhen node == this
                if node == toFind then
                    return true
                endif
                set node = node.next
            endloop
            return false
        endmethod
        method p_getSize takes nothing returns integer
            local integer count = 0
            local thistype node = this.next
            loop
                exitwhen node == this
                set count = count + 1
                set node = node.next
            endloop
            return count
        endmethod
    endmodule

    private module LinkedListLiteBase
        implement LinkedListFields
        debug method p_isEmptyHead takes nothing returns boolean
            debug return this == this.next and this == this.prev
        debug endmethod
        static method p_insert takes thistype this, thistype node returns nothing
            local thistype next = this.next
            set node.prev = this
            set node.next = next
            set next.prev = node
            set this.next = node
        endmethod
        static method p_remove takes thistype node returns nothing
            set node.next.prev = node.prev
            set node.prev.next = node.next
        endmethod
        static method insert takes thistype this, thistype node returns nothing
            debug call AssertError(node == 0, "insert()", "thistype", 0, "Cannot insert null node")
            debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "insert()", "thistype", 0, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
            call p_insert(this, node)
            static if thistype.onInsert.exists then
                call onInsert(node)
            endif
        endmethod
        static method remove takes thistype node returns nothing
            debug call AssertError(node == 0, "remove()", "thistype", 0, "Cannot remove null node")
            debug call AssertError(node.next.prev != node and node.prev.next != node, "remove()", "thistype", 0, "Invalid node [" + I2S(node) + "]")
            static if thistype.onRemove.exists then
                call onRemove(node)
            endif
            call p_remove(node)
        endmethod
        static if thistype.onTraverse.exists then
            method p_traverse takes boolean forward returns nothing
                local thistype node
                local thistype next
                debug local thistype prev
                debug local boolean array traversed
                if forward then
                    set node = this.next
                    loop
                        exitwhen node == this or node.prev.next != node
                        debug call AssertError(traversed[node], "traverseForwards()", "thistype", this, "A node was traversed twice in a single traversal")
                        debug set traversed[node] = true
                        debug set prev = node.prev
                        set next = node.next
                        if this.onTraverse(node) then
                            call remove(node)
                            debug set traversed[node] = false
                        debug elseif next.prev == prev then
                            debug set traversed[node] = false
                        endif
                        set node = next
                    endloop
                else
                    set node = this.prev
                    loop
                        exitwhen node == this or node.next.prev != node
                        debug call AssertError(traversed[node], "traverseBackwards()", "thistype", this, "A node was traversed twice in a single traversal")
                        debug set traversed[node] = true
                        debug set prev = node.next
                        set next = node.prev
                        if this.onTraverse(node) then
                            call remove(node)
                            debug set traversed[node] = false
                        debug elseif next.prev == prev then
                            debug set traversed[node] = false
                        endif
                        set node = next
                    endloop
                endif
            endmethod
            method traverseForwards takes nothing returns nothing
                call this.p_traverse(true)
            endmethod
            method traverseBackwards takes nothing returns nothing
                call this.p_traverse(false)
            endmethod
        endif
    endmodule
    private module LinkedListBase
        implement LinkedListLiteBase
        static method isLinked takes thistype node returns boolean
            return node.next.prev == node or node.prev.next == node
        endmethod
    endmodule

    module LinkedListFields
        readonly thistype prev
        readonly thistype next
    endmodule
    module LinkedListLite
        implement LinkedListLiteBase
        implement optional LinkedListLiteModuleCompatibility // For API compatibility with Dirac's 'LinkedListModule' library
    endmodule
    module LinkedList
        implement LinkedListBase
        implement optional LinkedListModuleCompatibility // For API compatibility with Dirac's 'LinkedListModule' library
    endmodule
    module LinkedListEx
        implement LinkedListBase
        static method p_move takes thistype prev, thistype node returns nothing
            call p_remove(node)
            call p_insert(prev, node)
        endmethod
        static method move takes thistype prev, thistype node returns nothing
            debug call AssertError(not isLinked(node), "move()", "thistype", 0, "Cannot use unlinked node [" + I2S(node) + "]")
            call p_move(prev, node)
        endmethod
        static method swap takes thistype this, thistype node returns nothing
            local thistype thisPrev = this.prev
            local thistype thisNext = this.next
            debug call AssertError(this == 0, "swap()", "thistype", 0, "Cannot swap null node")
            debug call AssertError(node == 0, "swap()", "thistype", 0, "Cannot swap null node")
            debug call AssertError(not isLinked(this), "swap()", "thistype", 0, "Cannot use unlinked node [" + I2S(this) + "]")
            debug call AssertError(not isLinked(node), "swap()", "thistype", 0, "Cannot use unlinked node [" + I2S(node) + "]")
            call p_move(node, this)
            if thisNext != node then
                call p_move(thisPrev, node)
            endif
        endmethod
    endmodule

    module StaticListFields
        implement LinkedListFields
        static constant method operator head takes nothing returns thistype
            return 0
        endmethod
        static method operator back takes nothing returns thistype
            return head.prev
        endmethod
        static method operator front takes nothing returns thistype
            return head.next
        endmethod
        static method operator empty takes nothing returns boolean
            return front == head
        endmethod
    endmodule
    module StaticListLite
        implement StaticListFields
        implement LinkedListLiteBase
        static method pushFront takes thistype node returns nothing
            debug call AssertError(node == 0, "pushFront()", "thistype", 0, "Cannot use null node")
            debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushFront()", "thistype", 0, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
            call insert(head, node)
        endmethod
        static method popFront takes nothing returns thistype
            local thistype node = front
            debug call AssertError(node.prev != head, "popFront()", "thistype", 0, "Invalid list")
            call remove(node)
            return node
        endmethod
        static method pushBack takes thistype node returns nothing
            debug call AssertError(node == 0, "pushBack()", "thistype", 0, "Cannot use null node")
            debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushBack()", "thistype", 0, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
            call insert(back, node)
        endmethod
        static method popBack takes nothing returns thistype
            local thistype node = back
            debug call AssertError(node.next != head, "popBack()", "thistype", 0, "Invalid list")
            call remove(node)
            return node
        endmethod
    endmodule
    module StaticList
        implement StaticListLite
        implement LinkedListBase
        implement LinkedListUtils
        static method clear takes nothing returns nothing
            call head.p_clear()
        endmethod
        static method flush takes nothing returns nothing
            call head.p_flush()
        endmethod
    endmodule
    module StaticListEx
        implement StaticList
        implement LinkedListEx
        implement LinkedListUtilsEx
        static method contains takes thistype node returns boolean
            return head.p_contains(node)
        endmethod
        static method getSize takes nothing returns integer
            return head.p_getSize()
        endmethod
        static method rotateLeft takes nothing returns nothing
            call p_move(back, front)
        endmethod
        static method rotateRight takes nothing returns nothing
            call p_move(head, back)
        endmethod
    endmodule

    module ListFields
        implement LinkedListFields
        method operator back takes nothing returns thistype
            return this.prev
        endmethod
        method operator front takes nothing returns thistype
            return this.next
        endmethod
        method operator empty takes nothing returns boolean
            return this.next == this
        endmethod
    endmodule
    module ListLite
        implement ListFields
        implement LinkedListLiteBase
        method pushFront takes thistype node returns nothing
            debug call AssertError(this == 0, "pushFront()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "pushFront()", "thistype", this, "Invalid list")
            debug call AssertError(node == 0, "pushFront()", "thistype", this, "Cannot insert null node")
            debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushFront()", "thistype", this, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
            call insert(this, node)
        endmethod
        method popFront takes nothing returns thistype
            local thistype node = this.next
            debug call AssertError(this == 0, "popFront()", "thistype", 0, "Null list")
            debug call AssertError(node.prev != this, "popFront()", "thistype", this, "Invalid list")
            call remove(node)
            return node
        endmethod
        method pushBack takes thistype node returns nothing
            debug call AssertError(this == 0, "pushBack()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "pushBack()", "thistype", this, "Invalid list")
            debug call AssertError(node == 0, "pushBack()", "thistype", this, "Cannot insert null node")
            debug call AssertError(not node.p_isEmptyHead() and (node.next.prev == node or node.prev.next == node), "pushBack()", "thistype", this, "Already linked node [" + I2S(node) + "]: prev = " + I2S(node.prev) + " ; next = " + I2S(node.next))
            call insert(this.prev, node)
        endmethod
        method popBack takes nothing returns thistype
            local thistype node = this.prev
            debug call AssertError(this == 0, "popBack()", "thistype", 0, "Null list")
            debug call AssertError(node.next != this, "pushFront()", "thistype", this, "Invalid list")
            call remove(node)
            return node
        endmethod
    endmodule
    module List
        implement ListLite
        implement LinkedListBase
        implement LinkedListUtils
        static method makeHead takes thistype node returns nothing
            set node.prev = node
            set node.next = node
        endmethod
        method clear takes nothing returns nothing
            debug call AssertError(this == 0, "clear()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "clear()", "thistype", this, "Invalid list")
            call this.p_clear()
        endmethod
        method flush takes nothing returns nothing
            debug call AssertError(this == 0, "flush()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "flush()", "thistype", this, "Invalid list")
            call this.p_flush()
        endmethod
    endmodule
    module ListEx
        implement List
        implement LinkedListEx
        implement LinkedListUtilsEx
        method contains takes thistype node returns boolean
            debug call AssertError(this == 0, "contains()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "contains()", "thistype", this, "Invalid list")
            return this.p_contains(node)
        endmethod
        method getSize takes nothing returns integer
            debug call AssertError(this == 0, "getSize()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "getSize()", "thistype", this, "Invalid list")
            return this.p_getSize()
        endmethod
        method rotateLeft takes nothing returns nothing
            debug call AssertError(this == 0, "rotateLeft()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "rotateLeft()", "thistype", this, "Invalid list")
            call p_move(this.back, this.front)
        endmethod
        method rotateRight takes nothing returns nothing
            debug call AssertError(this == 0, "rotateRight()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev == this, "rotateRight()", "thistype", this, "Invalid list")
            call p_move(this, this.back)
        endmethod
    endmodule

    module InstantiatedListLite
        implement ListLite
        debug private boolean valid
        static method create takes nothing returns thistype
            local thistype node = allocate()
            set node.prev = node
            set node.next = node
            debug set node.valid = true
            static if thistype.onConstruct.exists then
                call node.onConstruct()
            endif
            return node
        endmethod
        method destroy takes nothing returns nothing
            debug call AssertError(this == 0, "destroy()", "thistype", 0, "Null list")
            debug call AssertError(this.next.prev != this, "destroy()", "thistype", this, "Invalid list")
            debug call AssertError(not this.valid, "destroy()", "thistype", this, "Double-free")
            debug set this.valid = false
            static if thistype.flush.exists then
                call this.flush()
            endif
            static if thistype.onDestruct.exists then
                call this.onDestruct()
            endif
            debug set this.prev = 0
            debug set this.next = 0
            call this.deallocate()
        endmethod
    endmodule
    module InstantiatedList
        implement List
        implement InstantiatedListLite
    endmodule
    module InstantiatedListEx
        implement ListEx
        implement InstantiatedList
    endmodule


endlibrary


API Documentation
JASS:
//! novjass
|-------------------|
| API Documentation |
|-------------------|
/*
    v1.3.0
    LinkedList library

        If you are looking for a comprehensive listing of API available for each module, see the header of the
        library itself. What you can find here is the description of ALL avaiable API.

        The library supports 4 different implementations of linked-list:
            - Free lists, where you have freedom in linking and dealing with nodes
              without being restricted by the concept of a 'head node' or a 'container node'
            - Static Lists, which turns a struct into a single list, with the 'head node'
              representing the list itself
            - Non-static Lists, which allows you to have multiple lists within a struct
            - Instantiated Lists, similar to non-static lists but comes with its own methods
              for creating and destroying a list (requires allocate() and deallocate() in the
              implementing struct)

        Note: In all these kind of lists, all nodes should be unique together with the head nodes.
              Lists can also be circular or linear.


    |-----------------|
    | STATIC LIST API |
    |-----------------|

        Interfaces:

          */optional interface static method onInsert takes thistype node returns nothing/*
          */optional interface static method onRemove takes thistype node returns nothing/*
                - onInsert() is called after a node is inserted everytime insert(), pushFront(),
                  or pushBack() is called
                - onRemove() is called before a node is removed everytime remove(), popFront(),
                  or popBack() is called
                - <node> is the node being inserted/removed

          */optional interface method onTraverse takes thistype node returns boolean/*
                - Runs in response to traverseForwards()/traverseBackwards() calls
                - <this> is the head node
                - <node> is the currently traversed node
                - Returning <true> removes <node> from the list <this>


        Fields:

          */readonly thistype prev/*
          */readonly thistype next/*
          */readonly static thistype front/*
          */readonly static thistype back/*
          */static constant thistype head/*
          */readonly static boolean empty/*
                - <front>, <back>, <head>, and <empty> are method operators


        Methods:

          */static method isLinked takes thistype node returns boolean/*
                - Checks if a node is currently belongs to a list

          */static method move takes thistype prev, thistype node returns nothing/*
                - Moves <node> next to <prev>
                - Only works if <node> is already linked
          */static method swap takes thistype nodeA, thistype nodeB returns nothing/*
                - Swaps the placement of two nodes

          */static method contains takes thistype node returns boolean/*
                - Checks if <head> contains the given node
          */static method getSize takes nothing returns integer/*
                - Gets the size of the list <head>
                - Time complexity: O(n)

          */static method traverseForwards takes nothing returns nothing/*
          */static method traverseBackwards takes nothing returns nothing/*
                - Traverses a list forwards/backwards and calls onTraverse() for
                  each node in the list

          */static method rotateLeft takes nothing returns nothing/*
          */static method rotateRight takes nothing returns nothing/*

          */static method insert takes thistype prev, thistype node returns nothing/*
          */static method remove takes thistype node returns nothing/*

          */static method pushFront takes thistype node returns nothing/*
                - Inlines to insert() if not on DEBUG_MODE
          */static method popFront takes nothing returns thistype/*

          */static method pushBack takes thistype node returns nothing/*
                - Inlines to insert() if not on DEBUG_MODE
          */static method popBack takes nothing returns thistype/*

          */static method clear takes nothing returns nothing/*
                - Does not call remove() for any node, but only unlinks them from the list
          */static method flush takes nothing returns nothing/*
                - Calls remove() for each node on the list starting from the front to the back node


    |---------------------|
    | NON-STATIC LIST API |
    |---------------------|

          */optional interface static method onInsert takes thistype node returns nothing/*
          */optional interface static method onRemove takes thistype node returns nothing/*
                - onInsert() is called after a node is inserted everytime insert(), pushFront(),
                  or pushBack() is called
                - onRemove() is called before a node is removed everytime remove(), popFront(),
                  or popBack() is called
                - <node> is the node being inserted/removed

          */optional interface method onConstruct takes nothing returns nothing/*
          */optional interface method onDestruct takes nothing returns nothing/*
                - This methods will be called when calling create()/destroy()
                - <this> refers to the list to be created/destroyed

          */interface static method allocate takes nothing returns thistype/*
                - The value returned by this method will be the value returned by create()
          */interface method deallocate takes nothing returns nothing/*
                - This method will be called when calling destroy()

          */optional interface method onTraverse takes thistype node returns boolean/*
                - Runs in response to traverseForwards()/traverseBackwards() calls
                - <this> is the head node
                - <node> is the currently traversed node
                - Returning <true> removes <node> from the list <this>


        Fields:

          */readonly thistype prev/*
          */readonly thistype next/*
          */readonly thistype front/*
          */readonly thistype back/*
          */readonly boolean empty/*
                - <front>, <back>, and <empty> are method operators


        Methods:

          */static method isLinked takes thistype node returns boolean/*
                - Checks if a node is currently belongs to a list

          */static method move takes thistype prev, thistype node returns nothing/*
                - Moves <node> next to <prev>
                - Only works if <node> is already linked
          */static method swap takes thistype nodeA, thistype nodeB returns nothing/*
                - Swaps the placement of two nodes

          */method contains takes thistype node returns boolean/*
                - Checks if <head> contains the given node
          */method getSize takes nothing returns integer/*
                - Gets the size of the list <head>
                - Time complexity: O(n)

          */method traverseForwards takes nothing returns nothing/*
          */method traverseBackwards takes nothing returns nothing/*
                - traverses a list forwards/backwards and calls onTraverse() for
                  each node in the list

          */method rotateLeft takes nothing returns nothing/*
          */method rotateRight takes nothing returns nothing/*

          */static method makeHead takes thistype node returns nothing/*
                - Turns a node into a list

          */static method insert takes thistype prev, thistype node returns nothing/*
          */static method remove takes thistype node returns nothing/*

          */method pushFront takes thistype node returns nothing/*
                - Inlines to insert() if not on DEBUG_MODE
          */method popFront takes nothing returns thistype/*

          */method pushBack takes thistype node returns nothing/*
                - Inlines to insert() if not on DEBUG_MODE
          */method popBack takes nothing returns thistype/*

          */method clear takes nothing returns nothing/*
                - Does not call remove() for any node, but only unlinks them from the list
          */method flush takes nothing returns nothing/*
                - Calls remove() for each node on the list starting from the front to the back node

          */static method create takes nothing returns thistype/*
                - Creates a new list
          */method destroy takes nothing returns nothing/*
                - Destroys the list (Also calls flush internally for InstantiatedList and InstantiatedListEx)


*///! endnovjass


Important:

If you are using Dirac's LinkedListModule in your map and don't want to change your old code, you need to replace the contents of his LinkedListModule library with this one

JASS:
/*
*   For compatibility with Dirac's LinkedListModule
*
*   Note:
*       No changes needed to be made in your old code that's using Dirac's 'LinkedListModule'.
*       Just replace his 'LinkedListModule' library in your map with this one.
*/
library LinkedListModule
    module LinkedListModuleCompatibility
        implement LinkedListLiteModuleCompatibility
        static method createNode takes nothing returns thistype
            local thistype node = allocate()
            //! runtextmacro LINKED_LIST_HEAD("node")
            return node
        endmethod
        method insertNode takes thistype node returns nothing
            call insert(this, node)
        endmethod
        method removeNode takes nothing returns nothing
            call remove(this)
        endmethod
        method clearNode takes nothing returns nothing
            //! runtextmacro LINKED_LIST_CLEAR("this")
        endmethod
        method flushNode takes nothing returns nothing
            //! runtextmacro LINKED_LIST_FLUSH("this")
        endmethod
    endmodule

    module LinkedListLiteModuleCompatibility
        private static integer instanceCount = 0
        private static thistype _base = JASS_MAX_ARRAY_SIZE - 3
        boolean head
        static method operator base takes nothing returns thistype
            if not _base.head then
                set _base.prev = _base
                set _base.next = _base
                set _base.head = true
            endif
            return _base
        endmethod
        static method allocate takes nothing returns thistype
            local thistype this = thistype(base + 1).prev
            if this == 0 then
                debug if instanceCount == base then
                    debug call BJDebugMsg("[Linked List]: Overflow")
                    debug return 0
                debug endif
                set instanceCount = instanceCount + 1
                return instanceCount
            endif
            set thistype(base + 1).prev = this.prev
            return this
        endmethod
        method deallocate takes nothing returns nothing
            set this.prev = thistype(base + 1).prev
            set thistype(base + 1).prev = this
            set this.head = false
        endmethod
    endmodule

    //! textmacro LINKED_LIST_HEAD takes node
        set $node$.next = $node$
        set $node$.prev = $node$
        set $node$.head = true
    //! endtextmacro

    //! textmacro LINKED_LIST_CLEAR takes node
        if $node$!=$node$.next then
            set $node$.next.prev = thistype(base + 1).prev
            set thistype(base + 1).prev = $node$.prev
            set $node$.next = $node$
            set $node$.prev = $node$
        endif
    //! endtextmacro

    //! textmacro LINKED_LIST_FLUSH takes node
        set $node$.next.prev = thistype(base + 1).prev
        set thistype(base + 1).prev = $node$
        set $node$.head = false
    //! endtextmacro

    //! textmacro LINKED_LIST_INSERT takes node, toInsert
        set $node$.prev.next = $toInsert$
        set $toInsert$.prev = $node$.prev
        set $node$.prev = $toInsert$
        set $toInsert$.next = $node$
    //! endtextmacro

    //! textmacro LINKED_LIST_REMOVE takes node
        set $node$.prev.next = $node$.next
        set $node$.next.prev = $node$.prev
    //! endtextmacro

    //! textmacro LINKED_LIST_MERGE takes nodeA, nodeB
        set $nodeA$.next.prev = $nodeB$.prev
        set $nodeB$.prev.next = $nodeA$.next
        set $nodeA$.next = $nodeB$
        set $nodeB$.prev = $nodeA$
    //! endtextmacro
endlibrary

v1.3.0
- Updated debug messages condition in some methods
- Also added a new method for extended lists (move()).
- rotateLeft() and rotateRight() no longer calls the onRemove() and onInsert() interface methods

v1.2.1
- Better error situation handling for the traversal methods

v1.2.0
- Added useful debug messages inside traverseForwards() and traverseBackwards()
- optional interface method onTraverse() must now return a boolean value
> return true - remove traversed node
- Now uses internal debug messages when ErrorMessage is absent
- Added static method makeHead() to the 'List' module

v1.1.0
- Initial release
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
The reason Nestharus always tried to avoid vJass inheritance (outside of delegates) and stub methods/interfaces was due to performance issues and of course all the generated code that comes with it. With "extends array" you know the only generated code is going to be the static constant integer to represent the struct's typeid.

As far as using evaluators for on-add stuff, this seems like it will limit the usefulness to non-time sensitive tasks that don't need to rely on the fastest performance. Each call to an interface method or stub method is one trigger evaluation.

The way to work around this is to use events-within modules, kind of like what Jesus4Lyf did with Timer32 (a module that calls the method, wherein the module itself would not need to be evaluated by a "parent struct" but rather handles the mechanics itself).
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
The interface methods provided by the modules such as optional interface method onRemove takes thistype node returns nothing is actually implemented as
JASS:
module LinkedListLite
static method remove takes thistype node returns nothing
    static if thistype.onInsert.exists then
        call onInsert(node) // <--
    endif
    set node.next.prev = node.prev
    set node.prev.next = node.next
endmethod
...
endmodule
not with real vjass interfaces, so no extra generated codes/triggers.

and the module LinkedList extends LinkedListLite in the docs is just my way of saying that:
JASS:
module LinkedListLite
    // members
endmodule

module LinkedList
    implement LinkedListLite

    // members
endmodule

All operations are still as fast as Dirac's LL, except for flush(), which iterates all nodes and calls remove() on each one.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Sorry yes that would make sense, and now that I have looked at the code rather than documentation I can see there isn't even a struct in the library at all.

I have only one suggestion after reviewing the code:

JASS:
    static if DEBUG_MODE then
        private function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
            static if LIBRARY_ErrorMessage then
                static if THROW_ERRORS then
                    call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
                else
                    call ThrowWarning(condition, SCOPE_PREFIX, methodName, structName, node, message)
                endif
            endif
        endfunction
    endif

The above DEBUG_MODE checker needs to be either within the AssertError function or change the function to a static method. Functions will get written to the map script whether the static if containing them evaluates true or false.
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Hmm, just tried it now with non-DEBUG mode
JASS:
    static if DEBUG_MODE then
        private function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
            s // <-- to see if it throws compile error
            static if LIBRARY_ErrorMessage then
                static if THROW_ERRORS then
                    call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
                else
                    call ThrowWarning(condition, SCOPE_PREFIX, methodName, structName, node, message)
                endif
            endif
        endfunction
    endif
and it doesn't throw a syntax error. I also tried to look for the compiled AssertError function but cant find any.
Maybe you're remembering globals instead of functions, or the 'debug' keyword which doesn't work with functions?
 
If you are using Dirac's LinkedListModule in your map and don't want to change your old code, you need to replace the contents of his LinkedListModule library with this one
JASS:
        method clearNode takes nothing returns nothing
            call this.clear()
        endmethod
.clear() is not a member of the module you implemented above.

Removing the clearNode method did not throw me an error, so I suspect this one isn't actually used and can be deleted without issues.
 
Ah yes, that's right, but the clearNode() still needs to be there if someone uses them from Dirac's LL.
Updated the compat scipt above.
Great.

I have to say I'm not a fan of your API either. Because you've implemented LinkedList as modules, methods should never have generic names such as "remove".
Remember that modules are basically just an advanced textmacros and that having a method remove prevents the user from defining a remove function on the modules' parent.

So whatever struct you implement a linked list to, you can never declare a remove method.

Linked lists imho should have less generic naming conventions so they are non-intrusive.


It's just a matter of style though. You can leave it as it is. I don't have a problem with it, just saying that these kind of things can happen.
 
  • Like
Reactions: AGD

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Updated to v1.2.0
- Added useful debug messages inside traverseForwards() and traverseBackwards()
- optional interface method onTraverse() must now return a boolean value
> return true - remove traversed node
- Now uses internal debug messages when ErrorMessage is absent
- Added static method makeHead() to the 'List' module

v1.2.1
- Better error situation handling for the traversal methods

v1.3.0
- Updated debug messages condition in some methods
- Also added a new method for extended lists - move().
- rotateLeft() and rotateRight() no longer calls the onRemove() and onInsert() interface methods
 
Last edited:
Level 1
Joined
Apr 8, 2020
Messages
110
@AGD How would you loop through a basic list?

I did the following:
JASS:
local thistype node = thistype(0).next

loop
     exitwhen node == 0
     //Do something with data
     set node = node.next
endloop
 
Level 1
Joined
Apr 8, 2020
Messages
110
Well, here you go. The Utilities library is just for the print function.
JASS:
library TimerGroup requires MultidimensionalArray, LinkedList, Utilities

    globals
        private Integer1D stack
        private trigger trig = CreateTrigger()
    endglobals

    private function Eval takes nothing returns nothing
        local TimerStack this = stack[GetHandleId(GetExpiredTimer())]
        call TriggerClearConditions(trig)
        call TriggerAddCondition(trig, this.filter)
        call TriggerEvaluate(trig)
        call TimerStart(this.timer, this.timeout, true, function Eval)
    endfunction

    struct TimerStack extends array
        readonly real remainingTime
        readonly timer timer
        readonly real timeout
        readonly boolexpr filter
      
        implement Alloc
        implement List
      
        method destroy takes nothing returns nothing
            call remove(this)
            set this.timer = null
            set this.filter = null
            if stack.has(GetHandleId(this.timer)) then
                call stack.remove(GetHandleId(this.timer))
            endif
            call this.deallocate()
        endmethod
      
        static method resume takes nothing returns nothing
            local thistype node = thistype(0).next
            loop
                exitwhen node == 0
                call ResumeTimer(node.timer)
                set node = node.next
            endloop
        endmethod
      
        static method pause takes nothing returns nothing
            local thistype node = thistype(0).next
            call print("First node: " + I2S(node))
            loop
                exitwhen node == 0
                call PauseTimer(node.timer)
                call print("Timer #" + I2S(node) + ":" + I2S(GetHandleId(node.timer)))
                set node.remainingTime = TimerGetRemaining(node.timer)
                set node = node.next
            endloop
        endmethod
      
        static method start takes timer t, real timeout, code c returns thistype
            local thistype this = allocate()
            call pushFront(this)
            set this.timer = t
            set this.filter = Condition(c)
            set this.timeout = timeout
            set stack[GetHandleId(t)] = this
            call TimerStart(t, timeout, true, function Eval)
            return this
        endmethod
      
        static method add takes timer t returns thistype
            local thistype this = allocate()
            call pushFront(this)
            set this.timer = t
            set this.remainingTime = 0
            return this
        endmethod
      
        private static method init takes nothing returns nothing
            set stack = Integer1D.create()
        endmethod
      
        implement OnInit
    endstruct

endlibrary
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
From what I can see, you intended to have a single list in the struct so you should implement StaticList instead of implement List. This caused the problem since in the List module, pushFront() is not static so in the call above, it is actually being translated as call this.pushFront(this).
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
@Eikonium , you need to prepare the head node manually for ListLite (and also its derivatives - List, ListEx). List and ListEx has a built-in method for this that you can call: makeHead(this), but ListLite doesn't have this so you need to manually do:
JASS:
set this.prev = this
set this.next = this
before you can use this as a head node.
 
Level 20
Joined
Jul 10, 2009
Messages
477
Thank you, that's working!

I find that method a bit unintuitive, though, because it makes the list circular and one can't formulate the exitloop condition loopNode == 0 anymore (instead must compare for the head node).

Why exactly isn't the .makeHead() method part of ListLite, when it's actually mandatory to use? The absence of the method made me think that I wouldn't need it (and .pushFront() as well as looping from .front towards .next with exit condition loopNode == 0 was already working fine without .makeHead()!).

Speaking of it, I'd really like to encourage you putting all of these important details into the documentation of the API and provide coding examples on how to use the different modules. I had to look at the actual implementation of the list quite a few times, which optimally shouldn't be necessary after having read the API ;). Honestly, I invested so much time in fixing bugs related to me not understanding how your Lists work that it might have been more time-efficient to write my own specific Linked List for my needs (which is unfortunate, as your code seems well written).
So I think an improved API would really do it justice!

Some issues and questions I had during implementation (just ideas of what info you might like to include in the API):
  • I wasn't sure what type of list you actually provide with your API (there are many implementations out there that differ more or less in how to use them). A simple link to a wikipedia article or similiar stuff about the type of list you made would help a ton!
  • It wasn't clear to me that the internals of .makeHead() had to be used in order to use .back and .pushBack().
  • I wasn't sure of what struct-ure I needed to use ListLite. If I wanted a struct A to contain a list of structs of type B, would I have to implement ListLite into A or B? I clearly haven't worked alot with linked lists in the past, so the answer might be clear for more experienced programmers.
  • I wanted to insert new Nodes into the list in a way that the resulting list would be sorted, but I didn't know, if the insert(existingNode, newNode) method was inserting newNode before or after existingNode (actually very important to know, if you want to sort).
  • I wasn't sure if the head node was actually placed at the beginning or the end of the list (well now I know it's both after you told me it's meant to be circular). Especially important to know, when wondering, where exactly insert(headNode, newNode) places newNode.
  • I wasn't sure what exitwhen condition to use in loops (loopNode == null or loopNode == 0 or loopNode == headNode).
    Before knowing about the circularity, I even thought that the exitwhen condition would depend on the direction of the loop (front -> next or back -> prev), but couldn't even test that properly, because I didn't know, if the head node was at the beginning or end of the list.
  • I didn't know the difference of Static Lists and Non-static lists, as I had never dealt with static lists before (well, thats's maybe my own fault :D).
  • I wasn't sure, whether I could use onRemove() to destroy the struct instance of the removed node. Clearly, if onRemove executes before remove, destroying the node in onRemove would cause the remove to fail (node.next and prev not accessible anymore) and thereby corrupt the remaining list. Other way around, if removes executes before onRemove, I wouldn't be able to access node.next in the onRemove function.
  • I found it hard to figure out the methods and attributes available, when implementing a particular list type, because Static and Non-static types do mix in the API and the extension definitions of modules are not linear (so you can't just read from top to bottom to get all your options).

Many of those confusions of mine might come from my inexperience, but I can imagine that a well explained API would help more people that just inexperienced users ;)

So, wall of text, looking forward to updates and wish you best regards!
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Thanks, I very much appreciate your detailed thoughts.

I find that method a bit unintuitive, though, because it makes the list circular and one can't formulate the exitloop condition loopNode == 0 anymore (instead must compare for the head node).
My original intention for the pushFront, pushBack, popFront, & popBack is to be used only for circular lists, which I apparently forgot to mention in the docs. This is to shorten the codes for push and pop operations, basically making them only a wrapper for insert & remove, since to allow them to handle non-circular lists, I need to add a bunch of if-statements to handle the corner cases. If you need to deal with non-circular lists, you have to use the basic insert()/remove() instead.
The reason I excluded makeHead() for ListLite even tho it is designed to fit well for circular lists, is to make it as lite as possible as makeHead only contains two lines. Tho again I forgot to mention this in my docs.

I wasn't sure of what struct-ure I needed to use ListLite. If I wanted a struct A to contain a list of structs of type B, would I have to implement ListLite into A or B? I clearly haven't worked alot with linked lists in the past, so the answer might be clear for more experienced programmers.
You should implement it in A. In my case, this is how I usually do it, I share a common allocator for structs A & B:
JASS:
// Shared allocator for A & B for all nodes to be unique, since internally there
// is no distinction between a head and ordinary node being inserted
struct Node
endstruct

struct B extends array
    static method create takes ... returns thistype
        local thistype node = Node.create()
        set node.data = ...
        return node
    endmethod
endstruct

struct A extends array// a list of B
    implement ListLite
    static method create takes nothing returns thistype
        local thistype node = Node.create()
        set node.prev = node
        set node.next = node
        return node
    endmethod
endstruct

//From outside
local integer count = 10
local A a = A.create()

loop
    exitwhen count == 0
    call a.pushBack(B.create(...))
    set count = count - 1
endloop

I wanted to insert new Nodes into the list in a way that the resulting list would be sorted, but I didn't know, if the insert(existingNode, newNode) method was inserting newNode before or after existingNode (actually very important to know, if you want to sort).
It's actually documented thru the naming of the parameters in the docs
static method insert takes thistype prev, thistype node returns nothing
It inserts <node> after <prev/previous>

I didn't know the difference of Static Lists and Non-static lists, as I had never dealt with static lists before (well, thats's maybe my own fault :D).
Understandable since I also haven't seen the terms static lists and instantiated (or non-static) lists being used outside of vjass. Lists in 'real' languages are commonly objects, which by default means they have to be instantiated before use.
In vjass, static lists are called as such because you refer them using static class syntax and cant be instantiated. Basically, implementing StaticList in class A for example allows you to refer to class A (not an instance of A, but the class itself) as a single List so you can do call A.pushBack(this). Internally, it uses 0 as the head node, and you can't instantiate other head node.

I wasn't sure, whether I could use onRemove() to destroy the struct instance of the removed node. Clearly, if onRemove executes before remove, destroying the node in onRemove would cause the remove to fail (node.next and prev not accessible anymore) and thereby corrupt the remaining list. Other way around, if removes executes before onRemove, I wouldn't be able to access node.next in the onRemove function.
onRemove is called before. Actually destroying a node inside onRemove() is permitted and is perhaps the most significant use for it. By extending my previous example above,
JASS:
struct Node
endstruct

struct B extends array
    static method create takes ... returns thistype
        local thistype node = Node.create()
        set node.data = ...
        return node
    endmethod
    method destroy takes nothing returns nothing
        set this.data = null
        call Node(this).destroy()
    endmethod
endstruct

struct A extends array// a list of B
    method operator b takes nothing returns B
        return this
    endmethod

    private static method onRemove takes thistype node returns thistype
        call node.b.destroy()
    endmethod

    implement ListLite

    static method create takes nothing returns thistype
        local thistype node = Node.create()
        set node.prev = node
        set node.next = node
        return node
    endmethod
    method destroy takes nothing returns nothing
        // Remove all Bs in the list
        local thistype node = this.next // or this.front
        loop
            exitwhen node == this
            call remove(node)
            set node = node.next
        endloop
        /*
        *   Or better yet, use 'List' instead of 'ListLite' so that you can
        *   simply do 'call this.flush()' instead of the 6 lines above
        */

        call Node(this).destroy()
    endmethod
endstruct

//From outside
local integer count = 10
local A a = A.create()

loop
    exitwhen count == 0
    call a.pushBack(B.create(...)) // Populate A with Bs
    set count = count - 1
endloop

call a.destroy() // destroys A including all the Bs it contains

I found it hard to figure out the methods and attributes available, when implementing a particular list type, because Static and Non-static types do mix in the API and the extension definitions of modules are not linear (so you can't just read from top to bottom to get all your options).
I see, I'll consider it and think again which is the better arrangement for the API.

Many of those confusions of mine might come from my inexperience, but I can imagine that a well explained API would help more people that just inexperienced users ;)

So, wall of text, looking forward to updates and wish you best regards!
I admit a lot of these is due to my laziness in the documentation. Thanks for your input, I hope I can improve it in some of my update.

Btw. if you want more examples for this library especially in the usage of the interface methods, you can check the SpellFramework under my signature, and click under the 'Spell Templates' tab. The templates there utilize the difference kinds of list modules, though not all of them. You can use those examples even in different cases not related to spells.
 
Last edited:
Level 20
Joined
Jul 10, 2009
Messages
477
Thank you for your well-explained answer!

It's actually documented thru the naming of the parameters in the docs
static method insert takes thistype prev, thistype node returns nothing
It inserts <node> after <prev/previous>
Right. I did totally overlook that, maybe because I started with .pushFront() and switched to insert once I was already looking at your code, where insert takes this :) Still would be great to also include some text about it.

You should implement it in A.
That's actually interesting and I definitely got a new perspective by looking at your example code. Intuitively I implemented it in B and did not use a dedicated Node struct, which makes everything different and maybe partly explains why I was troubling with so many questions. Example code of mine:
JASS:
struct B
    ... data ...

    implement List

    static method create takes nothing returns B
        local B this = B.allocate()
        call B.makeHead(this)
        return this
    endmethod

    //this would cause problems, because onRemove is executed before remove
    method onRemove takes nothing returns nothing
        call this.destroy()
    endmethod
endstruct

struct A
    public B listHead //A knows the head element of B
    ... other data ...

    static method create takes nothing returns A
        local A this = A.allocate()
        set this.listHead = B.create()
        return this
    endmethod

    method addB takes nothing returns nothing
        local B newNode = B.create() //is affected by makeHead(), but next and prev get overwritten next line anyway
        call this.listHead.pushFront(newNode)
        set newNode.data = ...
    endmethod

    //the root of my question. I thought, flushing and thereby destroying would be nice and easy to do.
    method destroy takes nothing returns nothing
        call this.listHead.flush()
        call this.deallocate()
    endmethod
endstruct
onRemove is called before. Actually destroying a node inside onRemove() is permitted and is perhaps the most significant use for it.

I guess you can do that, because you use onRemove from A to destroy an instance of B?
I actually wanted to call this.destroy() inside onRemove in B (see code above), which would also delete the .next and .prev attributes and cause the following remove to corrupt the list, right?
this.destroy() should be safe to call after remove, though, which is why I wondered.

Thanks for your input, I hope I can improve it in some of my update.
Happy to hear that. Cheers!

Best regards!
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
@Eikonium
Regarding your example of destroying B inside onRemove(), it will still work. this.deskroy() does not change the .prev and .next attributes (unless you specifically defined the destroy() method to do so) so this
JASS:
struct B
    implement List
    private method destroy takes nothing returns nothing
        set this.xxx = null
    endmethod
    private static method onRemove takes B this returns nothing
        call this.destroy()
    endmethod
endstruct
is perfectly fine
 
Level 20
Joined
Jul 10, 2009
Messages
477
Regarding your example of destroying B inside onRemove(), it will still work. this.deskroy() does not change the .prev and .next attributes (unless you specifically defined the destroy() method to do so)
Oh right! vJass always has some suprises to offer.

Still, the remove method would be using the objects attributes after deallocation, which I consider to be evil programming style. People could potentially screw it up by adding other nodes to the list in the same .onRemove(), which allocate into the old nodes struct space. The issue could potentially be fixed by differentiating between beforeRemove and afterRemove.

The current way might be totally fine in the context of vJass, though. It's probably sufficient to leave some info text in the docs about onRemove being beforeRemove to avoid confusion :)

Cheers!
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
Hey, some basic examples of using the modules would be nice. I have the following struct:
JASS:
struct ChangeEventImpl extends ChangeEvent

    public stub method onChange takes nothing returns nothing
    endmethod

    public stub method restore takes nothing returns nothing
    endmethod

    public method onTraverse takes thistype node returns boolean
        call PrintMsg("onTraverse node " + I2S(node))
        call node.restore()

        return false
    endmethod

    implement ListEx

endstruct

from my understanding I have to call makeHead on one single instance to make it its own list?! And then I can add elements?

JASS:
private ChangeEventImpl changeEventsHead = 0

    public stub method getChangeEventsSize takes nothing returns integer
        if (this.changeEventsHead == 0) then
            return 0
        endif

        return this.changeEventsHead.getSize()
    endmethod

    public stub method addChangeEvent takes ChangeEvent changeEvent returns integer
        if (this.changeEventsHead == 0) then
            set this.changeEventsHead = changeEvent
            call this.changeEventsHead.makeHead(changeEvent)
        else
            call this.changeEventsHead.pushBack(changeEvent)
        endif

        return this.getChangeEventsSize() - 1
    endmethod

However, when I traverse the elements backwards only the last one will be traversed not the head. Is the head element not part of the list anymore?

JASS:
public stub method restore takes nothing returns nothing
        if (this.changeEventsHead != 0) then
            call PrintMsg("Restore events with size: " + I2S(this.changeEventsHead.getSize()))
            call this.changeEventsHead.traverseBackwards()
        else
            call PrintMsg("Restore events with size: 0")
        endif
    endmethod
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
@Barade
from my understanding I have to call makeHead on one single instance to make it its own list?! And then I can add elements?
Yup, correct.
However, when I traverse the elements backwards only the last one will be traversed not the head. Is the head element not part of the list anymore?
Traversing a head node calls onTraverse() for each of its element, but excludes the head node.
So for example:
JASS:
struct L
    private static method onTraverse takes thistype node returns boolean
        call BJDebugMsg(I2S(node))
        return false
    endmethod
    implement List
    private static method onTimerInit takes nothing returns nothing
        call L.makeHead(3)
        call L(3).pushBack(1)
        call L(3).pushBack(2)
        call L(3).pushBack(4)

        call BJDebugMsg("Traversing L(3)")
        call L(3).traverseBackwards()
    endmethod
    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(), 0, false, function thistype.onTimerInit)
    endmethod
endstruct
It would display:
Code:
Traversing L(3)
4
2
1

Examples for common module usage will probably be included in the next update.

@Eikonium , thanks. Good points and I have considered your suggestion regarding the before/after in response to the insertion and removal for the next update.
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Okay but why does it exclude the head node? Isn't the head part of the list as well? Otherwise, I will always have to create one useless head element of the list.
I want the traversal to reflect the usual method of iterating the nodes in the list, which excludes the head node.
JASS:
local thistype node = list.next // or list.front or list.first, etc
loop
    exitwhen node == list // or node == 0 or node == sentinelNode, etc
    ...
    set node = node.next
endloop
The head is part of the list, yes, but it only serves as a dummy node, a node for representing the list itself.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Can someone help me with how to use it in simple terms? I want to add hook links to the list and then add new ones if the caster/target goes above a certain distance between the link and the caster/target. I just don't get how to use the list at all.
 
Level 25
Joined
Feb 2, 2006
Messages
1,689
Hi,
I am using Knockback by KRICZ which requires ListModule by grim001 instead of this (see Knockback v1.01b).
Additionally, I use this now as requirement for my own system.
Should both systems work together in one map?

I get this error when saving my map now that I use this system as requirement:
syntax1.jpg
syntax2.jpg

I fixed it now by making the module List public in ListModule, so I had to use ListModule_List in the Knockback system.
Just wanted to let people know that it can cause issues and how to resolve this.

edit:
Is there some way to make this work as some kind of array list as well?
Like implements ArrayList which then allows using the index operators?
 
Last edited:
Top