System HandleGroups

quraji

zap
Reaction score
144
HandleGroup

What is it?
This is a library which contains a textmacro. This textmacro allows you to create a new 'group' with any handle you like. Units thought they were special because they were the only ones that got groups. No more! If you want a lightninggroup, so be it. Want a triggergroup? Be my guest.

How do I use it?
Run the text macro, and use the resulting struct type as if it were a new group type (with the supplied functions).

The Code:
JASS:
library HandleGroup
/* by quraji v1.03

    This library enables you to create your own group of any handle!
    The implemention is very simple. Just run the below textmacro where
    you want your new struct.
    Ex:
        //! runtextmacro CreateHandleGroup("lightning", "Lightning", "private", "8190")
        "lightning" is the handle type. This can be any handle type.
        "Lightning" is the desired name of that handle (for the methods/functions)
        "private" is the scope. This can be "private", "public" or "" for no scope
        "8190" is the number of possible struct instances (8190 is the default for vJass)
               -You can increase this to any number, at the cost of some performance.
    
    Method Definitions (x is the handle type, and X is the handle name):
    
        method forGroup takes code e returns nothing 
            This is the equivalent of the ForGroup for units. This uses a function interface instead of code. 
            e is the function to call for each handle in the group. Pass the name of any function that takes nothing and returns nothing
            You may use EnumHandleX or GetEnumHandleX() in e to refer to the enum handle
            Ex:
                call myhandlegroup.forGroup(myfunc)
        method clear takes nothing returns nothing
            This clears the handle group, but does not destroy it.
            
        method refresh takes nothing returns nothing
            Removes null handles from the group
            
        method getRandomX takes nothing returns x
            Returns a random handle from the group
            
        method firstOfGroup takes nothing returns x
            This returns the first handle of the group.
        
        method isXInGroup takes x h returns boolean
            Returns true if handle h is in the group, else false.
            
        method addX takes x h returns nothing
            Add a handle to the group.

        method removeX takes x h returns nothing
            Remove a handle from the group.
            
    There are also WC3 style function wrappers at the bottom, but I wont list them. (just look..this comment block is big enough already..)
    
*/
//! textmacro CreateHandleGroup takes handle, name, scope, size

globals
    $scope$ $handle$ EnumHandle$name$
endglobals

$scope$ struct $handle$group [$size$]
    
    private $handle$group last = 0
    private $handle$group prev = 0
    private $handle$group next = 0
    
    private $handle$ h
    private integer count = 0
    
    private method unlink takes nothing returns nothing
        set this.prev.next = this.next
        set this.next.prev = this.prev
        set this.last = this
        call this.destroy()
    endmethod
     
        /* for every handle in the group, call e
           use EnumHandleX or GetEnumHandleX to get the handle in the callback function, where X is the handle name
           Ex: set l = GetEnumHandleLightning()
               set l = EnumHandleLightning
          
           remember that function e must take nothing and return nothing
        */
    method forGroup takes enumfunc e returns nothing
        local $handle$group hg
        if (this.last!=this) then
            set hg = this.last
            loop
                exitwhen (hg==this)
                set EnumHandle$name$ = hg.h
                call e.execute()
                set hg = hg.prev
            endloop
        endif
    endmethod
        
        // clear the handle group
    method clear takes nothing returns nothing
        local $handle$group hg
        local $handle$group temp
        if (this.last!=this) then
            set hg = this.last
            loop
                exitwhen (hg==this)
                set temp = hg.prev
                set hg.last = hg
                call hg.destroy()
                set hg = temp
            endloop
        endif
    endmethod
    
    method refresh takes nothing returns nothing
        local $handle$group hg = this.last
        loop
            exitwhen (hg==this)
            if (hg.h==null) then
                call hg.unlink()
                set this.count = this.count-1
            endif
            set hg = hg.prev
        endloop
    endmethod
    
        // return the first handle of the group
    method firstOfGroup takes nothing returns $handle$
        return this.next.h
    endmethod
    
    method getRandom takes nothing returns $handle$
        local integer i = 1
        local integer rand = GetRandomInt(1, .count)
        local $handle$group hg = this.next
        loop
            exitwhen (i==rand)
            set hg = hg.next
            set i = i+1
        endloop
        return hg.h
    endmethod
        
        // return true if the handle exists in the handle group
    method isInGroup takes $handle$ h returns boolean
        local $handle$group hg
        if (h!=null) then
            set hg = this.last
            loop
                exitwhen (hg==this)
                if (hg.h==h) then
                    return true
                endif
                set hg = hg.prev
            endloop 
        endif
        return false
    endmethod
    
    method getCount takes nothing returns integer
        return this.count
    endmethod
    
        // add a handle to the handle group
    method add takes $handle$ h returns nothing
        local $handle$group hg
        if (h!=null and .isInGroup(h)==false) then
            set hg = $handle$group.allocate()
            set hg.prev = this.last
            set hg.prev.next = hg
            set this.last = hg
            set hg.h = h
            set this.count = this.count+1
        endif
    endmethod
        
        // remove a handle from the group
    method remove takes $handle$ h returns nothing
        local $handle$group hg
        if (h!=null) then
            set hg = this.next
            loop
                exitwhen (hg==this.last)
                if (hg.h==h) then
                    call hg.unlink()
                    set this.count = this.count-1
                    exitwhen (true)
                endif
                set hg = hg.next
            endloop
        endif
    endmethod
    
    static method create takes nothing returns $handle$group
        local $handle$group hg = $handle$group.allocate()
        set hg.last = hg
        return hg
    endmethod
    
    private method onDestroy takes nothing returns nothing
        local $handle$group hg
        local $handle$group temp
        if (this.last!=this) then // if the destroyed handle group is a list, depopulate it
            set hg = this.last
            loop
                exitwhen (hg==this)
                set temp = hg.prev
                call hg.destroy()
                set hg = temp
            endloop
        endif
        set this.h = null
    endmethod

endstruct

    // WC3 style functions
$scope$ function Create$name$Group takes nothing returns $handle$group
    return $handle$group.create()
endfunction
$scope$ function Destroy$name$Group takes $handle$group hg returns nothing
    call hg.destroy()
endfunction
$scope$ function Refresh$name$Group takes $handle$group hg returns nothing
    call hg.refresh()
endfunction
$scope$ function Clear$name$Group takes $handle$group hg returns nothing
    call hg.clear()
endfunction
$scope$ function $name$GroupAdd$name$ takes $handle$group hg, $handle$ h returns nothing
    call hg.add(h)
endfunction
$scope$ function $name$GroupRemove$name$ takes $handle$group hg, $handle$ h returns nothing
    call hg.remove(h)
endfunction
$scope$ function Is$name$In$name$Group takes $handle$group hg, $handle$ h returns boolean
    return hg.isInGroup(h)
endfunction
$scope$ function Count$name$sIn$name$Group takes $handle$group hg returns integer
    return hg.getCount()
endfunction
$scope$ function FirstOf$name$Group takes $handle$group hg returns $handle$
    return hg.firstOfGroup()
endfunction
$scope$ function For$name$Group takes $handle$group hg, enumfunc e returns nothing
    call hg.forGroup(e)
endfunction
//! endtextmacro

function interface enumfunc takes nothing returns nothing

endlibrary

You could have done X better.
Please, if something could be better, tell me :thup:

Change Log
v1.03
Changed some method names to make them smaller (easier to type). They're no longer explicit of what they do..but you'll manage.
Also removed an unneeded variable, thanks to J4L for pointing that out.

v1.02
Fixed a few typos. Added missing WC3 style functions. Changed the method naming convention to start with lower case. All these thanks to kenny!
Switched back to function interfaces after I realized they're simpler.

v1.01
I changed .ForGroup to use code instead of a function interface. I'm not sure why it did in the first place anyways.
 

Kenny

Back for now.
Reaction score
202
Awesome. *Gives simple praise*. It will just have to do, I lost the photos. :p

There are however some things that bug me a little (just person preference really).

1. You should just use thistype instead of all this $handle$group crap. It just looks so much 'neater', and makes it easy to change.

2. Your naming convention... Usually you see methods with the first-letter-of-the-first-word-not-capitalised-while-the-first-letter-of-the-next-word-is kind of thing. :p When working with methods, it is a strong habit of mine, and others, to start off with a small letter. But hey it's up to you.

3. You never seem to put 'this' in front of .count. Just seems a little odd (seems like it is not standardised), but you probably have your reasons.

4. You don't have a wc3 style function for Get$name$Count. (I am assuming that is the CountUnitsInGroup() function for the handlegroups.

Anywho, as I said, most of those things are just odd to me and don't really change anything to do with this system.

Great system, and it is definately useful. :thup:
 

quraji

zap
Reaction score
144
Awesome. *Gives simple praise*. It will just have to do, I lost the photos. :p

There are however some things that bug me a little (just person preference really).

1. You should just use thistype instead of all this $handle$group crap. It just looks so much 'neater', and makes it easy to change.

2. Your naming convention... Usually you see methods with the first-letter-of-the-first-word-not-capitalised-while-the-first-letter-of-the-next-word-is kind of thing. :p When working with methods, it is a strong habit of mine, and others, to start off with a small letter. But hey it's up to you.

3. You never seem to put 'this' in front of .count. Just seems a little odd (seems like it is not standardised), but you probably have your reasons.

4. You don't have a wc3 style function for Get$name$Count. (I am assuming that is the CountUnitsInGroup() function for the handlegroups.

Anywho, as I said, most of those things are just odd to me and don't really change anything to do with this system.

Great system, and it is definately useful. :thup:

Thanks for the comment.

1. Meh, I guess. I just wrote this to say "handlegroup" then search+replaced it, so it wasn't an issue for me.

2. I usually do that, but I didn't originally plan on adding the WC3 style functions so I thought I should be friendly to people who like that naming convention. I'll change it back since the WC3 functions are there.

3. Nope, you're right. I added .count in later on, so I must have forgot that my style at the time was this.member :p

4. Neither do I have one for .Refresh. Thanks for pointing that out.

Once again thanks, I always like hearing from you kenny! :thup:
 

Azlier

Old World Ghost
Reaction score
461
Two things:

1. Either make the enumfuncs be called by .evaluate (it's faster) or make them into code arguments instead, with a little magic.

2. Refresh will not work for all handle types (mainly triggers). I don't know of a good way around that.
 

Jesus4Lyf

Good Idea™
Reaction score
397
This uses a textmacro interface for unknown reasons.

It should provide simple functions for the user to deal with instead.

And I'd suggest that universally, the limit be 8190 or whatever instead of requiring the user to enter it (who needs >8190 instances of something like this). But that much is up to you.
 

quraji

zap
Reaction score
144
No real interest was shown in this (understandable), so I haven't bothered updating it with improvements/changes.

>> This uses a textmacro interface for unknown reasons.

I don't understand.

>> It should provide simple functions for the user to deal with instead.

Are you saying I should make the groups follow an interface so they can use one set of WC3 style functions?

>> And I'd suggest that universally, the limit be 8190 or whatever instead of requiring the user to enter it (who needs >8190 instances of something like this). But that much is up to you.

It probably won't be needed, but remember that these groups are actually linked lists so the usage will climb faster than if they were actual groups.
Hm, I wonder if this would be better purely using hashtables (one hashtable for each type)?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top