• 🏆 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] [Containers] Vector<T>

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Implementation of dynamic contiguous array. Adds lot of additional functionality when working with arrays. Compared to Array<T> comsumes more memory to manage storage, thus container can grow dynamically.

Designed to give even more options and control to <type> array. Data is stored within underlying Table object.

Requires copy ctor in form of static method operator [] to provide correct results when working with objects. This element is subject that can be changed.

Requires newer version of Table. (Does not work with versions prior to 3.1.1.1)
Huge applause goes to Bribe for updating his amazing Table to better accommodate this resource.
JASS:
/*****************************************************************************
*
*    Vector<T> v1.1.8.4
*       by Bannar
*
*    Dynamic contiguous array.
*
******************************************************************************
*
*    Requirements:
*
*       Table by Bribe
*          hiveworkshop.com/threads/snippet-new-table.188084/
*
*       Alloc - choose whatever you like
*          e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
*    Implementation:
*
*       macro DEFINE_VECTOR takes ACCESS, NAME, TYPE
*
*       macro DEFINE_STRUCT_VECTOR takes ACCESS, NAME, TYPE
*
*          ACCESS - encapsulation, choose restriction access
*            NAME - name of vector type
*            TYPE - type of values stored
*
*     Implementation notes:
*
*       - DEFINE_STRUCT_VECTOR macro purpose is to provide natural typecasting syntax for struct types.
*       - Vectors defined with DEFINE_STRUCT_VECTOR are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
*    struct API:
*
*       General:
*
*        | static method create takes nothing returns thistype
*        |    Default ctor.
*        |
*        | static method operator [] takes thistype other returns thistype
*        |    Copy ctor.
*        |
*        | method destroy takes nothing returns nothing
*        |    Default dctor.
*        |
*        | method empty takes nothing returns boolean
*        |    Checks whether the vector is empty.
*        |
*        | method size takes nothing returns integer
*        |    Returns size of a vector.
*
*
*       Access:
*
*        | method operator [] takes integer index returns $TYPE$
*        |    Returns item at position index.
*        |
*        | method operator []= takes integer index, $TYPE$ value returns nothing
*        |    Sets item at index to value.
*        |
*        | method front takes nothing returns $TYPE$
*        |    Retrieves first element.
*        |
*        | method back takes nothing returns $TYPE$
*        |    Retrieves last element.
*        |
*        | method data takes nothing returns Table
*        |    Returns the underlying Table object.
*
*
*       Modifiers:
*
*        | method clear takes nothing returns nothing
*        |    Performs a flush operation on data table.
*        |
*        | method push takes $TYPE$ value returns thistype
*        |    Adds elements to the end.
*        |
*        | method pop takes nothing returns thistype
*        |    Removes the last element.
*        |
*        | method assign takes integer count, $TYPE$ value returns thistype
*        |    Assigns count elements replacing current data.
*        |
*        | method insert takes integer pos, integer count, $TYPE$ value returns thistype
*        |    Inserts count elements before position pos.
*        |
*        | method erase takes integer pos, integer count returns thistype
*        |    Erases count elements starting at position pos.
*
*
*****************************************************************************/
library VectorT requires Table, Alloc

//! runtextmacro DEFINE_VECTOR("", "IntegerVector", "integer")
// Run here any global vector types you want to be defined.

//! textmacro_once DEFINE_VECTOR takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$ extends array
    private Table table
    private integer length

    implement Alloc

    private method seT takes integer index, $TYPE$ value returns nothing
        set table.$TYPE$[index] = value
    endmethod

    private method get takes integer index returns $TYPE$
        return table.$TYPE$[index]
    endmethod

    private method assert_pos takes integer pos, string f returns boolean
        debug if pos < 0 or pos >= length then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::assert_pos failed at "+f+" for instance "+I2S(this)+". Index "+I2S(pos)+" is out of range.")
        debug endif

        return pos >= 0 and pos < length
    endmethod

    private method assert_range takes integer pos, string f returns boolean
        debug if pos < 0 or pos > length then
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::assert_range failed at "+f+" for instance "+I2S(this)+". Iterator "+I2S(pos)+" is out of range.")
        debug endif

        return pos >= 0 and pos <= length
    endmethod

    method operator [] takes integer index returns $TYPE$
        debug if not assert_pos(index, "operator []") then
            debug return get(-1)
        debug endif

        return get(index)
    endmethod

    method operator []= takes integer index, $TYPE$ value returns nothing
        debug if not assert_pos(index, "operator []=") then
            debug return
        debug endif

        call seT(index, value)
    endmethod

    static method create takes nothing returns thistype
        local thistype this = allocate()
        set table = Table.create()
        set length = 0
        return this
    endmethod

    method clear takes nothing returns nothing
        set length = 0
        call table.flush()
    endmethod

    method destroy takes nothing returns nothing
        call clear()
        call table.destroy()
        set table = 0

        call deallocate()
    endmethod

    method front takes nothing returns $TYPE$
        return this[0]
    endmethod

    method back takes nothing returns $TYPE$
        return this[length-1]
    endmethod

    method data takes nothing returns Table
        return this.table
    endmethod

    method empty takes nothing returns boolean
        return length == 0
    endmethod

    method size takes nothing returns integer
        return length
    endmethod

    method push takes $TYPE$ value returns thistype
        call seT(length, value)
        set length = length + 1

        return this
    endmethod

    method pop takes nothing returns thistype
        if length > 0 then
            set length = length - 1
            call table.$TYPE$.remove(length)
        endif

        return this
    endmethod

    static method operator [] takes thistype other returns thistype
        local thistype instance = create()
        loop
            exitwhen instance.size() >= other.size()
            call instance.push(other[instance.size()])
        endloop

        return instance
    endmethod

    method assign takes integer count, $TYPE$ value returns thistype
        if count > 0 then
            call clear()
            loop
                exitwhen length >= count
                call push(value)
            endloop
        endif

        return this
    endmethod

    method insert takes integer pos, integer count, $TYPE$ value returns thistype
        local integer i

        if assert_range(pos, "insert") and count > 0 then
            set length = length + count
            set i = length - 1
            loop
                exitwhen i < (pos + count)
                call seT(i, get(i-count))
                set i = i - 1
            endloop

            set i = 0
            loop
                exitwhen i >= count
                call seT(pos+i, value)
                set i = i + 1
            endloop
        endif

        return this
    endmethod

    method erase takes integer pos, integer count returns thistype
        if assert_pos(pos, "erase") and count > 0 then
            if ( pos + count > length ) then
                set count = length - pos
            endif

            set pos = pos + count
            loop
                exitwhen pos >= length
                call seT(pos-count, get(pos))
                set pos = pos + 1
            endloop

            loop
                exitwhen count <= 0
                call pop()
                set count = count - 1
            endloop
        endif

        return this
    endmethod
endstruct
//! endtextmacro

//! textmacro_once DEFINE_STRUCT_VECTOR takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$ extends array
    private delegate IntegerVector parent

    method operator[] takes integer index returns $TYPE$
        return parent[index]
    endmethod

    static method create takes nothing returns thistype
        local thistype this = IntegerVector.create()
        set parent = this
        return this
    endmethod

    method front takes nothing returns $TYPE$
        return parent.front()
    endmethod

    method back takes nothing returns $TYPE$
        return parent.back()
    endmethod
endstruct
//! endtextmacro

endlibrary
Demo:
JASS:
//! runtextmacro DEFINE_STRUCT_VECTOR("", "VectorOfVec", "IntegerVector")

struct test_vector extends array

    static method print takes Table tb, integer size, string prefix returns nothing
        local integer i = size
        local string s = ""
        loop
            exitwhen 0 == i
            set i = i-1
            set s = I2S(tb[i]) + ", " + s
        endloop
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, prefix + s)
        set s = null
    endmethod

    static method test_vector takes nothing returns nothing
        local integer max = 9
        local integer i
        local integer j
        local string s

        local IntegerVector vec = IntegerVector.create()
        local IntegerVector vec2 = IntegerVector.create()
        local VectorOfVec vov = VectorOfVec.create()

        call vec.push(55).push(3).push(88).push(0).push(-10).push(1).push(17).push(-1).push(26)

        call vov.assign(3, vec)

        call vec2.push(4)
        call vec2.push(55)
        call vec2.push(0)
        call vec2.push(13)
        call vec2.push(7)

        call vec.erase(1, 5)
        call vec2.insert(3, 5, 4)

        /* performed without print method to show that vector of objects achieves
           in this case double array syntax [][] similar to one from TableArray */
 
        set i = vov.size()-1
        loop
            exitwhen i < 0

            set s = ""
            set j = vov[i].size()-1

            loop
                exitwhen j < 0
                set s = I2S(vov[i][j]) + ", " + s
                set j = j - 1
            endloop

            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Vector (" + I2S(i) +") holds: "+ s)
            set i = i - 1
        endloop

        call print(vec.data(), vec.size(), "Vec: ")
        call print(vec2.data(), vec2.size(), "Vec2: ")
    endmethod

    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(), 2, false, function thistype.test_vector)
    endmethod

endstruct
 
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated.

Code is a bit cleaner now, even though it was elegant already. Added support for .destroy() method if vector is defined for struct type instead of handles or primitives. Whenever clear or destroy method is called for vector instance, prior to proceeding with vector itself, set of object nodes will be destroy and thus deallocated (do you deallocate your struct instance within destroy method, dont you?).

pushBack -> push
popBack -> pop
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated the same way Array<T> got updated. No alloc; less code; extends Table.

Edit: next update. Follows Array<T> once again. 1 array less, swap and maxSize went out. Insert & InsertCount has been merged as I did with assign in the first place. Insertion is now done with "insert" method which requires additional parameter "count".

Edit2: Bribe gonna be overhyped! "count" integer array has been removed and replaced by operators. This is due to lack of allocator for Vector and fact that Table returns indexes which are out of bondary for jass arrays. Fixed problem with eraseRange.
 
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Update.
JASS:
    method insert takes integer pos, integer count, $TYPE$ value returns integer
        local integer i = 0
        if ( pos < 0 or pos > this.count ) then
            // throw exception
            return -1
        elseif ( count <= 0 ) then
            return pos
        endif

        loop
            exitwhen i >= count
            call push($DEFAULT$)
            set i = i + 1
        endloop

        set i = this.count - 1
        loop
            exitwhen i <= pos
            call move(i, i-count)
            set i = i - 1
        endloop

        set i = 0
        loop
            exitwhen i >= count
            call seT(pos+i, value)
            set i = i + 1
        endloop
        return pos
    endmethod

    method erase takes integer pos returns integer
        local integer i
        if not assert_pos(pos) then
            return -1
        endif

        set i = pos
        loop
            exitwhen i >= count - 1
            call move(i, i+1)
            set i = i + 1
        endloop

        call pop()
        return pos
    endmethod

    method eraseRange takes integer first, integer last returns integer
        local integer i
        local integer j
        if not ( assert_pos(first) and assert_pos(last) ) then
            return -1
        endif

        if ( first > last ) then
            set i = first
            set first = last
            set last = i
        endif

        set i = first
        set j = last + 1
        loop
            exitwhen j >= count
            call move(i, j)
            set i = i + 1
            set j = j + 1
        endloop

        set i = last - first + 1
        loop
            exitwhen i <= 0
            call pop()
            set i = i - 1
        endloop
        return first
    endmethod
Into:
JASS:
    method insert takes integer pos, integer count, $TYPE$ value returns integer
        local integer i = 0
        if ( pos < 0 or pos > this.count ) then
            return -1 // throw exception
        elseif ( count <= 0 ) then
            return pos
        endif

        loop
            exitwhen i >= count
            call push( get(this.count-count) )
            set i = i + 1
        endloop

        set i = pos
        loop
            exitwhen i >= pos+count
            call move(i+count, i)
            call seT(i, value)
            set i = i + 1
        endloop

        return pos
    endmethod

    method erase takes integer pos, integer count returns integer
        local integer i
        if not assert_pos(pos) then
            return -1
        elseif ( count <= 0 ) then
            return pos
        endif

        if ( pos + count > this.count ) then
            set count = this.count - pos
        endif

        set i = pos
        loop
            exitwhen i + count >= this.count
            call move(i, i+count)
            set i = i + 1
        endloop

        loop
            exitwhen count <= 0
            call pop()
            set count = count - 1
        endloop

        return pos
    endmethod
erase change reasoning: case similar to String library. No iterators in jass, thus its more intuitive to implement "count" version instead. It better fits jass environtment. Code gets thinner.

Any suggestions appreciated.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated.
Fixed insert method, and made it quicker by using similar approach found in resize method - instead of looping through push(), size is immidiately increased.
On top of that, push() method now returns struct type, thus, as demonstrated in example, you can add multiple values without breaking into new line.

Tell me if access specifier should be added into textmacro. I've made sure amount of arguments is reduced to maximum, yet telling my struct (vector) to be declared as private might be usefull feature.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Made this even lighten by removing unnecessary "valueType" variable, resize() method and at() - operator[] now always checks the boundary.

Question: would you prefer vector to destroy/remove whats stored in it when clear/destroy method is called or let it leave users stuff alone?
I've removed "auto-destroy" for struct type vectors for now, untill I know whats the right approach.

E.g: you create vector of trigger handles and somewhere in your code, when you don't need your vector anymore, you call clear/destroy method. If you had stored those via push(CreateTrigger()) or smthing, such call generated multiple leaks within your code.

Code:
#include <vector>
#include <iostream>
 
int main()
{
    std::vector<int> numbers;
    std::vector<std::vector<int>*> vectors;
    vectors.push_back(&numbers);
    std::vector<int>* p_vec = vectors[0];
 
    vectors.clear();
    numbers.push_back(7);
    numbers.push_back(159);
 
    for (int i : *p_vec) {
        std::cout << i << '\n';
    }
 
    return 0;
}
Still outputs: 7, 159.

So my guess is to not touch thier stuff.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I'm altered code a bit, since new idea for "global" collection for struct-type type of e.g vector is to create child struct extending parent: IntegerVector. This would eliminate irritating conversions and still keeping the idea of IntegerVector being the parent for all "struct-type" vectors.

Delegate is basically the only convenient way for implementing inheritance. Overriden members are those, which returns $TYPE$ -> vec[2].create() instead of MyType(vec[2]).create().

JASS:
library VectorT requires Table

    // Create here any global vector types you want to be defined.
    //! runtextmacro DEFINE_VECTOR("", "IntegerVector", "integer")

//! textmacro DEFINE_STRUCT_VECTOR takes ACCESS, NAME, TYPE

    $ACCESS$ struct $NAME$ extends array
		private delegate IntegerVector deleg

		static method create takes nothing returns thistype
			local thistype this = IntegerVector.create()
			set this.deleg = this
			return this
		endmethod

		method destroy takes nothing returns nothing
			set deleg = 0
			call IntegerVector(this).destroy()
		endmethod

		method operator[] takes integer index returns $TYPE$
			return IntegerVector(this)[index]
		endmethod

		method front takes nothing returns $TYPE$
			return IntegerVector(this).front()
		endmethod

		method back takes nothing returns $TYPE$
			return IntegerVector(this).back()
		endmethod
	endstruct

//! endtextmacro

//! textmacro_once DEFINE_VECTOR takes ACCESS, NAME, TYPE

	$ACCESS$ struct $NAME$ extends array
		private Table table
		private integer count

		implement Alloc

		private method seT takes integer index, $TYPE$ value returns nothing
			set table.$TYPE$[index] = value
		endmethod

		private method move takes integer to, integer from returns nothing
			set table.$TYPE$[to] = table.$TYPE$[from]
		endmethod

		private method get takes integer index returns $TYPE$
			return table.$TYPE$[index]
		endmethod

		private method assert_pos takes integer pos returns boolean
			if not ( pos >= 0 and pos < count ) then
				debug call DisplayTimedTextFromPlayer(GetLocalPlayer(), 0, 0, 60, "DEBUG: $NAME$::assert_pos Invalid index at position pos.")
				return false
			endif
			return true
		endmethod

		method operator[] takes integer index returns $TYPE$
			if not assert_pos(index) then
				return get(-1)
			endif
			return get(index)
		endmethod

		method operator[]= takes integer index, $TYPE$ value returns nothing
			if not assert_pos(index) then
				return
			endif
			call seT(index, value)
		endmethod

		static method create takes nothing returns thistype
			local thistype this = allocate()

			set this.table = Table.create()
			set this.count = 0

			return this
		endmethod

		static method operator[] takes thistype vec returns thistype
			local thistype this = create()
			local integer i = 0
			set this.count = vec.count

			loop
				exitwhen i >= this.count
				call this.seT(i, vec[i])
				set i = i + 1
			endloop

			return this
		endmethod

		method clear takes nothing returns nothing
			set count = 0
			call table.flush()
		endmethod

		method destroy takes nothing returns nothing
			call clear()
			call table.destroy()
			set table = 0

			call deallocate()
		endmethod

		method front takes nothing returns $TYPE$
			return get(0)
		endmethod

		method back takes nothing returns $TYPE$
			return get(count-1)
		endmethod

		method data takes nothing returns Table
			return table
		endmethod

		method empty takes nothing returns boolean
			return count == 0
		endmethod

		method size takes nothing returns integer
			return count
		endmethod

		method push takes $TYPE$ value returns thistype
			call seT(count, value)
			set count = count + 1
			return this
		endmethod

		method pop takes nothing returns nothing
			set count = count - 1
			call table.$TYPE$.remove(count)
		endmethod

		method assign takes integer count, $TYPE$ value returns nothing
			local integer i
			if ( count >= 0 ) then
				call clear()

				set i = 0
				loop
					exitwhen i >= count
					call seT(i, value)
					set i = i + 1
				endloop
				set this.count = count
			endif
		endmethod

		method insert takes integer pos, integer count, $TYPE$ value returns integer
			local integer i
			if ( pos < 0 or pos > this.count ) then
				debug call DisplayTimedTextFromPlayer(GetLocalPlayer(), 0, 0, 60, "DEBUG: $NAME$::insert Invalid iterator at position pos.")
				return -1
			elseif ( count <= 0 ) then
				return pos
			endif

			set this.count = this.count + count
			set i = this.count - 1
			loop
				exitwhen i < (pos + count)
				call move(i, i-count)
				set i = i - 1
			endloop

			set i = 0
			loop
				exitwhen i >= count
				call seT(pos+i, value)
				set i = i + 1
			endloop

			return pos
		endmethod

		method erase takes integer pos, integer count returns integer
			local integer i
			if not assert_pos(pos) then
				return -1
			elseif ( count <= 0 ) then
				return pos
			endif

			if ( pos + count > this.count ) then
				set count = this.count - pos
			endif

			set i = pos
			loop
				exitwhen i + count >= this.count
				call move(i, i+count)
				set i = i + 1
			endloop

			loop
				exitwhen count <= 0
				call pop()
				set count = count - 1
			endloop

			return pos
		endmethod

	endstruct

//! endtextmacro

endlibrary
However, I got a problem with ListT.

JASS:
//! textmacro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE

    $ACCESS$ struct $NAME$Node extends array
        method operator next takes nothing returns thistype
            return IntegerListNode(this).next
        endmethod

        method operator next= takes thistype value returns nothing
            set IntegerListNode(this).next = value
        endmethod

        method operator prev takes nothing returns thistype
            return IntegerListNode(this).prev
        endmethod

        method operator prev= takes thistype value returns nothing
            set IntegerListNode(this).prev = value
        endmethod

        method operator data takes nothing returns $TYPE$
            return IntegerListNode(this).data
        endmethod

        method operator data= takes $TYPE$ value returns nothing
            set IntegerListNode(this).data = value
        endmethod

        static method create takes $TYPE$ value returns thistype
            return IntegerListNode.create(value)
        endmethod

        method destroy takes nothing returns nothing
            call IntegerListNode(this).destroy()
        endmethod
    endstruct

    $ACCESS$ struct $NAME$ extends array
		private delegate IntegerList deleg

		static method create takes nothing returns thistype
			local thistype this = IntegerList.create()
			set this.deleg = this
			return this
		endmethod

		method destroy takes nothing returns nothing
			set deleg = 0
			call IntegerList(this).destroy()
		endmethod

        method front takes nothing returns $TYPE$
            return IntegerList(this).front()
        endmethod

        method back takes nothing returns $TYPE$
            return IntegerList(this).back()
        endmethod
    endstruct

//! endtextmacro

//! textmacro_once DEFINE_LIST takes ACCESS, NAME, TYPE

	private module $NAME$NodeInit
		private static method onInit takes nothing returns nothing
			set Next = Table.create()
			set Prev = Table.create()
			set Data = Table.create()
		endmethod
	endmodule

    $ACCESS$ struct $NAME$Node extends array
		private static Table Next
		private static Table Prev
		private static Table Data

        implement AllocT

        method operator next takes nothing returns thistype
            return Next[this]
        endmethod

        method operator next= takes thistype value returns nothing
            set Next[this] = value
        endmethod

        method operator prev takes nothing returns thistype
            return Prev[this]
        endmethod

        method operator prev= takes thistype value returns nothing
            set Prev[this] = value
        endmethod

        method operator data takes nothing returns $TYPE$
            return Data.$TYPE$[this]
        endmethod

        method operator data= takes $TYPE$ value returns nothing
            set Data.$TYPE$[this] = value
        endmethod

        static method create takes $TYPE$ value returns thistype
            local thistype this = allocate()
            set this.data = value
            return this
        endmethod

        method destroy takes nothing returns nothing
            call Next.remove(this)
			call Prev.remove(this)
			call Data.$TYPE$.remove(this)

            call deallocate()
        endmethod

		implement $NAME$NodeInit
    endstruct

    $ACCESS$ struct $NAME$ extends array
		readonly $NAME$Node first
		readonly $NAME$Node last
		private integer count

		implement Alloc

        static method create takes nothing returns thistype
            local thistype this = allocate()
            set this.count = 0
            set this.first = 0
            set this.last = 0
            return this
        endmethod

        method clear takes nothing returns nothing
            local $NAME$Node node = first
            local $NAME$Node temp

            loop
                exitwhen 0 == node
                set temp = node.next
                call node.destroy()
                set node = temp
            endloop

			set first = 0
			set last = 0
			set count = 0
        endmethod

        method destroy takes nothing returns nothing
            call clear()
            call deallocate()
        endmethod

        method front takes nothing returns $TYPE$
            return first.data
        endmethod

        method back takes nothing returns $TYPE$
            return last.data
        endmethod

        method empty takes nothing returns boolean
            return 0 == count
        endmethod

        method size takes nothing returns integer
            return count
        endmethod

        method push takes $TYPE$ value returns nothing
            local $NAME$Node node = $NAME$Node.create(value)

            if ( not empty() ) then
                set last.next = node
                set node.prev = last
                set last = node
            else
                set first = node
                set last = node
                set node.prev = 0
            endif

            set node.next = 0
            set count = count + 1
        endmethod

        method unshift takes $TYPE$ value returns nothing
            local $NAME$Node node = $NAME$Node.create(value)

            if ( not empty() ) then
                set first.prev = node
                set node.next = first
                set first = node
            else
                set first = node
                set last = node
                set node.next = 0
            endif

            set node.prev = 0
            set count = count + 1
        endmethod

        method pop takes nothing returns nothing
            local $NAME$Node node

            if ( not empty() ) then
                set node = last
                set last = last.prev

                if ( last == 0 ) then
                    set first = 0
                else
                    set last.next = 0
                endif

                call node.destroy()
                set count = count - 1
            else
                // pop on empty list
            endif
        endmethod

        method shift takes nothing returns nothing
            local $NAME$Node node

            if ( not empty() ) then
                set node = first
                set first = first.next

                if ( first == 0 ) then
                    set last = 0
                else
                    set first.prev = 0
                endif

                call node.destroy()
                set count = count - 1
            else
                // shift on empty list
            endif
        endmethod

        static method operator[] takes thistype list returns thistype
            local thistype this = create()
            local $NAME$Node node = list.first

            loop
                exitwhen node == 0
                call push(node.data)
                set node = node.next
            endloop

            return this
        endmethod

        method find takes $TYPE$ value returns $NAME$Node
            local $NAME$Node node = first
            loop
                exitwhen node == 0 or node.data == value
                set node = node.next
            endloop
            return node
        endmethod

        method remove takes $NAME$Node node returns nothing
            if ( node != 0 ) then
                if ( node == first ) then
                    call shift()
                elseif ( node == last ) then
                    call pop()
                else
                    set node.prev.next = node.next
                    set node.next.prev = node.prev
                    call node.destroy()
                    set count = count - 1
                endif
            else
                // removing invalid node
            endif
        endmethod

    endstruct

//! endtextmacro

endlibrary
List nodes are based on AllocT (table for "infinite" instances). Declaring non static delegate of type IntegerListNode defines an array -> restricted to 8191 members. Because of that, as you can see in hidden script, all of node's members had to be redeclared just to typecast instance index safely.
Question: is there a better / more convenient way for providing inheritance for struct "extending" parent which instances are not limited to MAX_ARRAY_SIZE?

Thanks in advance.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated.

operators [] & []= will now inline when in release mode.
Asserts now inform user where and for which instance assertion has failed. They will also inline when not in debug mode.
erase & insert method has been slightly improved in regard of efficiency.
front & back methods are now under asserts security.
pop, push, insert, erase and assign will now return caller instance what allows for cascade operations. Previously, thier behaviour was similar to c-equivalents, but for jass environment that might not be as good.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Is this still under development? Because the example code does not compile ("private outside library/scope definition").

Also the code form the examples //! runtextmacro DEFINE_VECTOR("VectorOfVec", "IntegerVector", "true") doesn't match the textmacro declaration: // macro DEFINE_VECTOR takes ACCESS, NAME, TYPE
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Hello there.

I've checked out the Vector once again. Seems like everything works fine, however, the demo code was taken from old version of Vector. This has been fixed.

Bribe, when I've been writing this, there was no "<T>" aka template based implementation. Most of similar resources are written around "struct" i.e "integer" type, since most things can actually be wrapped around it.

However, sometimes there is a need for <type> specific implementation - this is especially handly if you are not a speed maniac and prefer to work with convenient library.

It's rather small and is written in such a way, that can be used both for standard types and for structs. As said previously, is adds additional features to Table. Dialogs can be a perfect example for usage of such scripts, but there are plenty more - multiboard with a vector 2D defining rows and columns. Within the Dialog, vector is used for convenient Button handling - each Dialog stores single vector of Buttons.
 
Level 13
Joined
Nov 7, 2014
Messages
571
There seems to be a small bug that could only affect "a real vector"
(//! runtextmacro DEFINE_VECTOR("", "RealVector", "real"))

JASS:
method pop takes nothing returns thistype
    if ( length > 0 ) then
        set length = length - 1
        call table.$TYPE$.remove(length)
        call table.real.remove(0) // <-- forgotten?
    endif

    return this
endmethod

The data method is maybe missing something?
JASS:
/*
*        | method data takes nothing returns Table
*        |    returns the underlying Table object
*/

method data takes nothing returns Table
    return this // should be this.table?
endmethod

Also in the erase method after shifting the elements the length could simply be adjusted instead of calling the pop method repeatedly?

PS: I though vectors are a "physicsy" concept?

JASS:
Vector<T>
...
Dynamic contiguous array.
...
Requirements:
...
[Hash]Table

Yeah... kind of reminds of Php =).
Perhaps calling the library "sparse array" would've been more accurate?
 
Please change what Aniki said.
I had bugs and didn't know what the issue was until I checked VectorT's core code.

Furthemore I had a bug, that is nativly based on a hashtable issue. (which I came along pretty late haha)
When I try to null elements of my handle vector, it doesn't work, due to overwrting handled with "null" does not work with hashtables.
One need to use the RemoveHandle function to get rid off it.
So I tried to erase my element, and then to add an empty element again, though, it internaly also uses the "seT" function solely.
What fixed my problem:

JASS:
    private method seT takes integer index, $TYPE$ value returns nothing
        set table.$TYPE$[index] = value
    endmethod
->
JASS:
    private method seT takes integer index, $TYPE$ value returns nothing
        call table.$TYPE$.remove(index)
        set table.$TYPE$[index] = value
    endmethod

... alternativly I could get the table data and then apply the remove() function by myself, though I think it should be nativly supported, and it would be very neat that "nulling" elements work then.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
@IcemanBo could you elaborate on "due to overwrting handled with "null" does not work with hashtables."?
I.e. idk if calling remove for every case is necessary if an issue you are describing is present only in very specific situation which you already said is connected to flaw of native hashtable itself.
I might be wrong tho.
 
In a map I intuivly nulled things with assign operator =, but it first needs to be removed explicitly.

//! runtextmacro DEFINE_VECTOR("", "UnitVector", "unit")

JASS:
struct test_vector extends array
    private static method onInit takes nothing returns nothing
        local UnitVector uv = UnitVector.create()
        local unit u = CreateUnit(GetLocalPlayer(), 'hfoo', 0, 0, 0)
    
        call uv.push(u)
        set u = uv[0]
        call BJDebugMsg(GetUnitName(u))
        set uv[0] = null
        set u = uv[0]
        call BJDebugMsg(GetUnitName(u))
    endmethod

endstruct

EDIT:

Here, I mean this is hashtable problem:

JASS:
struct test2 extends array
    private static hashtable hash = InitHashtable()
  
    private static method onInit takes nothing returns nothing
        local unit u = CreateUnit(GetLocalPlayer(), 'hfoo', 0, 0, 0)
        call SaveUnitHandle(hash, 0, 0, u)
        set u = LoadUnitHandle(hash, 0, 0)
        call BJDebugMsg(GetUnitName(u))
        call SaveUnitHandle(hash, 0, 0, null)
        set u = LoadUnitHandle(hash, 0, 0)
        call BJDebugMsg(GetUnitName(u))
    endmethod

endstruct

It doesn't work like this, too. And the vector function does just mimic/take usage of it. But yeh, it would be cool maybe if it works, too with nulling "v[0] = null". ;D
 

Attachments

  • VectorTest.w3x
    33.7 KB · Views: 77
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Nice one @IcemanBo. Question is now, should the Table be updated because of this too?
I.e. updating Vector behavior for this particular case means that its behavior now differs from Table - and the idea is be a wrapper over Table to provide "generics" (<T>) syntactic sugar.
I'd need more 1 more moderator / possibly Bribe here so it's not a decision made in haste.
 
Nulling won't work with assign operator overload and [], due to how hashtable works, as shown above. One might intuitivly try something like:
JASS:
set id = GetUnitTypeId(<some unit>)
call KillUnit(UnitTable[id])
set UnitTable[id] = null
But when the overloaded operator removes the handle internaly first, and then re-assigns, then it works as expected.
 
Once user uses remove function, it's null already, or? So if user is aware of this, it makes not much sense anymore setting it to null from user side if he removed it.

It's really only the []-overloading I think, as it mimics array syntax and one might think that "MyUnit[id] = null" works fine.
We use [] because it's faster and more intuitive than expressing functions directly, and with allowing to null would then just strengthen this goal.

Writing
MyUnit[id] = <value>
.. and then having to write
call MyUnit.remove(id) .. over:
MyUnit[id] = null

seems less intuitive, while we would have no loss with supporting nulling on other side. Why write an extra warning or note for user that he maybe needs to think about, if we could solve it with one line that does no damage.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Updated to 1.1.8.0.

Added resize method for VectorT to quickly populate or reduce vector size. This is useful when e.g.: representing unit's inventory with vector, where you can use null (default value) items to stand for empty slots and use non-null values for actually equipped items. (Edited): Use assign or resize instead.
Improved documentation and readability.
Validated to be Wurst friendly.

Edit:

resize method removed once again - I've forgotten about assign method. This was one of the reasons I had removed resize in the past in the first place.

Also in the erase method after shifting the elements the length could simply be adjusted instead of calling the pop method repeatedly?
I could have done that, true, the reason I'm not is assertion safety being removed when not compiling in DEBUG mode. I wanted to prevent user from receiving some "valid" data if he or she accesses index that is out of range for given vector. Calling pop method ensures that no valid handles or data is stored outside of vector size range. Otherwise, I'd have to a) drop such security entirely b) methods get/seT would not be inline-friendly.
 
Last edited:
Top