1. Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.
    Dismiss Notice
  2. A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!
    Dismiss Notice
  3. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Structfollowers

Discussion in '"Graveyard"' started by WaterKnight, Dec 28, 2010.

  1. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,035
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    Exposition
    I have searched for new ways to write my vJass codes. Particularly, the semi-optimal encapsulation always annoyed me. You cannot nest libraries, you may do so with scopes but equal-labelled identifiers of parents are in the way nor will be overshadowed, same when extending structs. Private implementing of modules was not introduced, so modules are actually only nestable textmacros but without parameters, quasi like import. Well, now it does not seem like vJass is still going on with development, so I do something on my own.

    Structs are fairly good since they allow a short notation on calling through their instances. When you extend structs, the members of the parent will simply get transfered, resulting in you being unable to redeclare its identifiers. Even if you could overshadow them, you would not be able to access them anymore. A precise indicator is missing, folders in structs. It's a simple strategy: You jump from one struct into another by using a local identifier in the origin struct that redirects to the target struct by returning an instance of this target struct.

    Read "Realization" to see single steps of how to acquire this target and which problems may appear. It finalizes in my current solution.


    Realization
    That would be my basic approach how I wanted it:
    Code (vJASS):
    scope SurpriseBag
        public struct B
            static method Goal takes nothing returns nothing
            endmethod
        endstruct
    endscope

    struct A
        static SurpriseBag_B B
    endstruct


    Now you could write
    Code (vJASS):
    call A.B.Goal()


    This works already for the static call. But it's harder to go via an instance of A because you would need to pass the id of this instance to struct B and cast again. This is because "B" shall be a folder of A, store information for the instance of A you enter B with and therefore need to identify to which A instance it belongs.

    This means it's necessary to assign each B instance to its parallel A instance.

    Code (vJASS):
    scope SurpriseBag
        public struct B
            method Goal takes nothing returns nothing
            endmethod

            static method StaticGoal takes nothing returns nothing
            endmethod
        endstruct
    endscope

    struct A
        SurpriseBag_B B

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

            set this.B = this

            return this
        endmethod
    endstruct


    Call:
    Code (vJASS):
    call <InstanceOfA>.B.Goal()


    But it ain't that easy. It would only work this way if you allocated the instance, which isn't a must since you would want to have deeper nestings without necessarily allocating.

    Unit.Position.X.Get() for example, you would create a new Unit but not a new Position, this should just work as a folder that contains the inner part X. You also would not want to write this create-method with modifyings everytime, which would be error-prone anyway.

    So my idea: Allocate each instance onInit but immediately destroy them again. Their members will not be reset. I also use some textmacros here to finalize the shape and BaseStruct/Folder/Struct are meant for avoiding name conflicts as you might have a inner struct Position for both Units and Items for example.

    Current version
    Code (vJASS):
    //! textmacro Folder takes name
        scope Folder$name$
    //! endtextmacro

    //! textmacro LinkToStruct takes folder, name
        Folder$folder$_Struct$name$ $name$
    //! endtextmacro

    //! textmacro Struct takes name
        public struct Struct$name$
    //! endtextmacro

    //! textmacro BaseStruct takes name
        struct $name$
            static thistype THIS = NULL
    //! endtextmacro

    globals
        trigger InitLinks_DUMMY_TRIGGER = CreateTrigger()
        integer InitLinks_ITERATION
        integer InitLinks_THREAD_BREAK_COUNTER
        constant integer InitLinks_THREAD_BREAK_LIMIT = 1500
    endglobals

    globals
        constant boolean DEBUG = true
        constant integer STRUCT_EMPTY = 0
        constant integer STRUCT_MAX = 8190
        constant integer STRUCT_MIN = 1
    endglobals

    module InitStructLinks
        private static method InitStructLinks2 takes nothing returns nothing
            local integer iteration = InitLinks_ITERATION

            loop
                call thistype(iteration).deallocate()

                set iteration = iteration - 1

                exitwhen (iteration < STRUCT_MIN)

                set InitLinks_THREAD_BREAK_COUNTER = InitLinks_THREAD_BREAK_COUNTER + 1

                exitwhen (InitLinks_THREAD_BREAK_COUNTER > InitLinks_THREAD_BREAK_LIMIT)
            endloop

            if (iteration > STRUCT_EMPTY) then
                set InitLinks_ITERATION = iteration
                set InitLinks_THREAD_BREAK_COUNTER = 0

                call TriggerEvaluate(InitLinks_DUMMY_TRIGGER)
            else
                static if (DEBUG) then
                    set InitLinks_ITERATION = STRUCT_EMPTY
                endif
            endif
        endmethod

        private static method InitStructLinks takes nothing returns nothing
            local integer iteration = InitLinks_ITERATION

            loop
                call thistype.allocate()

                set iteration = iteration - 1

                exitwhen (iteration < STRUCT_MIN)

                set InitLinks_THREAD_BREAK_COUNTER = InitLinks_THREAD_BREAK_COUNTER + 1

                exitwhen (InitLinks_THREAD_BREAK_COUNTER > InitLinks_THREAD_BREAK_LIMIT)
            endloop

            if (iteration > STRUCT_EMPTY) then
                set InitLinks_ITERATION = iteration
                set InitLinks_THREAD_BREAK_COUNTER = 0

                call TriggerEvaluate(InitLinks_DUMMY_TRIGGER)
            else
                static if (DEBUG) then
                    set InitLinks_ITERATION = STRUCT_EMPTY
                endif
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            local boolexpr condition = Condition(function thistype.InitStructLinks)

            set InitLinks_ITERATION = STRUCT_MAX
            set InitLinks_THREAD_BREAK_COUNTER = 0

            call TriggerClearConditions(InitLinks_DUMMY_TRIGGER)

            call TriggerAddCondition(InitLinks_DUMMY_TRIGGER, condition)

            call TriggerEvaluate(InitLinks_DUMMY_TRIGGER)

            call DestroyBoolExpr(condition)

            set condition = null

            static if (DEBUG) then
                if (InitLinks_ITERATION > STRUCT_EMPTY) then
                    call BJDebugMsg("InitLinks: thread break in " + InitStructLinks.name + " with " + I2S(InitLinks_ITERATION))
                endif
            endif

            set condition = Condition(function thistype.InitStructLinks2)
            set InitLinks_ITERATION = STRUCT_MAX
            set InitLinks_THREAD_BREAK_COUNTER = 0

            call TriggerClearConditions(InitLinks_DUMMY_TRIGGER)

            call TriggerAddCondition(InitLinks_DUMMY_TRIGGER, condition)

            call TriggerEvaluate(InitLinks_DUMMY_TRIGGER)

            call DestroyBoolExpr(condition)

            set condition = null
        endmethod
    endmodule

    //! textmacro LinkToStruct takes folder, struct
        Folder$folder$_Struct$struct$ $struct$ = this

        implement InitStructLinks
    //! endtextmacro

    Code (vJASS):
    //Example

    //! runtextmacro Folder("Apple")
        //! runtextmacro Struct("Worm")
        endstruct
    endscope

    //! runtextmacro BaseStruct("Apple", "Apple")
        //! runtextmacro LinkToStruct("Apple", "Worm")
    endstruct

    //Worm is an inner part of Apple
     
    Last edited: Apr 15, 2011
  2. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    rewrite
    Ok, now that I read through this I see what you are trying to do: folders in structs.

    ...

    Code (vJASS):

    struct A extends array
        private static integer instanceCount = 0
        private static integer recycleCount = 0
        private static integer array recycle

        public static method create takes nothing returns thistype
            local thistype this

            if (recycleCount == 0) then
                set instanceCount = instanceCount + 1
                set this = instanceCount
            else
                set recycleCount = recycleCount - 1
                set this = recycle[recycleCount]
            endif

            return this
        endmethod

        public method destroy takes nothing returns nothing
            set recycle[recycleCount] = this
            set recycleCount = recycleCount + 1
        endmethod
    endstruct

    struct B extends array
        private static integer instanceCount = 0
        private static integer recycleCount = 0
        private static integer array recycle

        public static method create takes nothing returns thistype
            local thistype this

            if (recycleCount == 0) then
                set instanceCount = instanceCount + 1
                set this = instanceCount
            else
                set recycleCount = recycleCount - 1
                set this = recycle[recycleCount]
            endif

            return this
        endmethod

        public method destroy takes nothing returns nothing
            set recycle[recycleCount] = this
            set recycleCount = recycleCount + 1
        endmethod
    endstruct

    struct C extends array
        readonly delegate B B
        readonly delegate A A

        public static method create takes nothing returns thistype
            local thistype this = A.create()
            set A = this
            set B = B.create()

            return this
        endmethod

        public method destroy takes nothing returns nothing
            call A.destroy()
            call B.destroy()
        endmethod
    endstruct
     


    gg

    Not only is it easier to read/understand/implement, but it has less overhead and has more functionality! Woohoo.

    If you want more complex implementations with scopes, protected fields, internal fields, and etc, check http://www.hiveworkshop.com/forums/jass-functions-413/stacked-fields-169977/.

    And yes, this allows you to override fields as well as access super fields as well as have multi inheritance or have that folder syntax you were talking about.

    This is how I make structs extend other structs.
     
    Last edited: Dec 29, 2010
  3. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,035
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    Code (vJASS):
    delegate
    pastes its own methods and variables in the parent struct if there are not already methods/variables of the same name.

    Code (vJASS):
    struct B
        method ChildMethod takes nothing returns nothing
        endmethod
    endstruct

    struct A
        readonly delegate B B

        method ParentMethod takes nothing returns nothing
            call this.ChildMethod()
        endmethod
    endstruct


    This does not throw a syntax error but I'd want it to.

    Other than that, you do nearly the same as I do.

    The links are initialized here, too. Delegates have the same problem that they do not automatically return the casted version of the input struct instance.

    I, however, wanted to not allocate instances of the child structs.

    It should look this way

    Now, to not manually obfuscate my code through my syntax problems, I split it off and used textmacros. The link preload at least has the advantage that it does not have to be done multiple times as structs would have when reallocated.
     
  4. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    You can't do this..

    Code (vJASS):

            local thistype this = thistype.allocate()

            set A = this
            set B = this
     


    Unless you are only using B and A for that one struct... what you are trying to do is intense coupling, which should never be done.

    And the fact is that your textmacros obsfucate the code and I doubt anyone will want to use them. The scope and struct macros are extreme examples, but examples nonetheless ; P.
     
  5. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,401
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    If it's vJass syntax improvements you're looking for, I am planning on doing a series of unnofficial JassHelper updates. Maybe you and I should talk about what you would like to see from a JassHelper update?
     
  6. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,035
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    Update: I am abusing the allocation macros now like in
    Code (vJASS):
    struct A
        integer B = 4
    endstruct


    When an instance of A is allocated, it's B member will be initialized to 4. To init the links to the inner structs, every instance will be allocated onInit and deallocated again, so you do not need another allocation method. Since the number of struct links may vary, therefore said macro will too and the normal allocation method is of undetermined length, which may crash the thread. Fetching ~8k instances and laying them back already takes some operations, so I have integrated that from time to time a new thread will be started. But this time/execution count may be adjusted according to the amount of max links in a struct. The debug message will give information if the limit is hit.

    The advantage of this new version is that the InitLinksToStruct-macros are no longer required and I was told that you can have onInit multiple times in a struct when putting them in modules and making them private, so I might as well use onInit and you do not need an extra call.