• 🏆 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] HandlerCode

Level 9
Joined
Jun 21, 2012
Messages
432
JASS:
library HandlerCode/*
***************************************************************************************
*
*   HandlerCode
*   ¯¯¯¯¯¯¯¯¯¯¯
*   v2.0.0.0
*   by Thelordmarshall
*   
*   Allow to set/run "codes" in array vars; this version no longer uses hashtable, 
*   unsafest but more fastest. HOWEVER you can change the max code size.
*       
*       local HandlerCode h = 0
*       set h.code = function MyFunction
*                   - or -
*       set h = HandlerCode.getHandle(function MyFunction)
*       call h.run() or call h.debugRun()
*
*   API:
*   ¯¯¯
*   struct HandlerCode extends array
*       
*       - readonly static integer size
*
*       - method operator code= takes code c returns HandlerCode
*       - static method getHandle takes code c returns HandlerCode
*       - method run takes nothing returns boolean
*       - debug method debugRun takes nothing returns boolean
*
**************************************************************************************/
    globals
        //if you need more arrays, change this value
        private constant integer MAX_CODE_SIZE = 0x4000
    endglobals
    
    struct HandlerCode extends array
        readonly static integer size=0
        private static integer array handleId[MAX_CODE_SIZE]
        private static trigger array trigger[MAX_CODE_SIZE]

        private static method id takes conditionfunc c returns thistype
            local integer i=GetHandleId(c)-0x100000
            local integer id=handleId[i]
            if(0!=id)then
                return id
            endif
            set size=size+1
            set handleId[i]=size
            set trigger[size]=CreateTrigger()
            call TriggerAddCondition(trigger[size],c)
            return size
        endmethod
        
        method run takes nothing returns boolean
            return TriggerEvaluate(trigger[this])
        endmethod
        
        debug method debugRun takes nothing returns boolean
            debug if(null==trigger[this])then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"[HandlerCode] error: attempted to run null code")
            debug endif
            debug return run()
        debug endmethod

        static method getHandle takes code c returns HandlerCode
            return id(Condition(c))
        endmethod
        
        method operator code= takes code c returns HandlerCode
            return id(Condition(c))
        endmethod
    endstruct
endlibrary
 
Last edited:
Also , we strictly use "thistype" against the struct name. there are some instances that two libraries both have the same struct names.


Also, we can do this in vJass:

JASS:
function A takes nothing returns nothing
    call B()
endfunction

function B takes nothing returns nothing
    call BJDebugMsg("Hello")
endfunction

And:

JASS:
function A takes nothing returns nothing
    call B.execute()
endfunction

function B takes nothing returns nothing
    call BJDebugMsg("Hello")
endfunction

Let's not also forget:

JASS:
function A takes nothing returns nothing
    call ExecuteFunc("B")
endfunction

function B takes nothing returns nothing
    call BJDebugMsg("Hello")
endfunction
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I don't see any advantage of using this. This is not any faster and has even worse memory usage. In general, systems which require handler will only use one trigger for all registered boolexprs, but this system creates another new trigger for every registration which is no good at all.

And I wonder why you use static members?
 
Level 9
Joined
Jun 21, 2012
Messages
432
Well here we go again:

- New update.
- @WaterKnight: Thanks for the help.
- @Dalvengyr: I implemented a small description in this version.
- @edo494: Finally you win xd.
- @Bribe: The problem is that "codes" can not be stored in arrays for use later.

....but this system creates another new trigger for every registration which is no good at all.

You are wrong... the system creates only once the trigger the first time that the code is saved.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Now how can I add another boolexpr in the same handler? The boolexpr should be added into the same trigger, not by creating another one. Handler is meant to contain more than one boolexpr (function).

Anyway, you can avoid the use of hashtable by using non-static struct member. You can add "addHandler" and "removeHandler" methods too.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
why not use function.evaluate() This also allows you to pass arguments without using globals.
JASS:
function A takes real x returns real
    if(GetRandomInt(0,1)==0) then
        return B.evaluate(x*0.02)
    endif
    return x
endfunction

function B takes real x returns real
    if(GetRandomInt(0,1)==1) then
        return A(x*1000.)
    endif
    return x
endfunction
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
because it generates trigger evaluation, which is very, very bad, because it generates 2 copies of your function + requires initialization(one trigger per .evaluate), requires additional globals generated, slows code down extremly and bloats it with unnecessary bytes.

Both evaluate and execute are not really useful anyways, if you have circular dependencies, you are doing it wrong already
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
For myself, I never use execute() nor evaluate().

I also jumped into the discussion in the trigger&script section about those two.

I just try to find a good reason, why we would need such a snippet.
I can't see any. Maybe the author can post some example good, of what he thinks is useful.

With little skill and time effort, you will eradicate most recursive function calls by placing them into a correct order.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you are putting apples into oranges. Circular dependencies are bad, dynamic trigger invocation isnt. It shouldnt even compile, but Vexorian decided TriggerEval > compiler error.

But I dont care how it compiles down, if you have circular dependency, you are doing it wrong(there is absolutly always way to break circularity)
 
Level 9
Joined
Jun 21, 2012
Messages
432
Could you give an example of this being useful?

Wc3 not support arrays for code:
JASS:
    globals
        HandlerCode array c
    endglobals
   
    function Func1 takes nothing returns nothing
        call BJDebugMsg("test 1")
    endfunction

    function Func2 takes nothing returns nothing
        call BJDebugMsg("test 2")
    endfunction

    function Init takes nothing returns nothing
        local integer i=1

        set c[0].code=function Func1
        set c[1].code=function Func2

        loop
            call c[i].run()
            exitwhen(0==i)
            set i=i-1
        endloop
    endfunction
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
I see a lot of problems with this resource:

  • Why does this use two arrays? One array should be sufficient, right?
  • You shouldn't try to map handle ids to array indices.
  • How do you expect this to clean things up? You just have a static integer counter that keeps getting increased, so your array will leak and never be cleaned up (and therefore eventually overflow).
  • Why does this even has a public field size? The array which is used to manage different code instances is an implementation detail and should therefore not be accessible from outside.
  • What if someone wants to have two different objects with the same function registered? Your system doesn't account for that.
  • Why does getHandle does exactly the same thing as operator code=? Thats not only redundant but also wrong because a getter shouldn't register a new callback?? Why would I ever use this function?
  • The registered callback should be taken in the constructor.
  • The debugRun method is superfluous... this checks should happen in the normal run method or even better in the constructor (of course only in debug mode). Otherwise you can't just switch to debug mode and have additional error checking, you would have to refactor all your code from run to debugRun - thats not desirable.

The library can be rewritten as short as this (whithout all the problems mentioned above):

JASS:
library CodeType
	struct Code
		private trigger trg = CreateTrigger()
	
		static method create takes code c returns thistype
			local thistype this = thistype.allocate()
			call TriggerAddCondition(trg, Condition(c))
			return this
		endmethod
		
		method destroy takes nothing returns nothing
			call DestroyTrigger(trg)
			call deallocate()
		endmethod
		
		method run takes nothing returns nothing
			call TriggerEvaluate(trg)
		endmethod
	endstruct
endlibrary

And then be used like (works of course also with arrays):

JASS:
function Func takes nothing returns nothing
	call BJDebugMsg("Hello from Func")
endfunction

function Test takes nothing returns nothing
	local Code c = Code.create(function Func) // Create a code object
	call c.run() // Execute it
	call c.destroy() // Clean up
endfunction

And thats it. However I doubt that this is enough for an own system...
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I'm completely with looking_for_help on this one. It literally can be as simple as he made it to be. What is most important to note is that there are good ideas, but few practical applications for many of them. ExecuteFunc is good for reducing handles and completely breaking precedence rules. LFH's code is better if you prefer speed at the cost of more handles.
 
Top