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

[vJASS] LinkToStruct

Status
Not open for further replies.
Level 26
Joined
Aug 18, 2009
Messages
4,097
Greetings

I am using a system called Structfollowers and am trying to improve it at the moment by shortening things and making it more secure, which is where I ran into some problem. But first a few explanations of the system:

Thereby, a link shall be created from one struct to another of this kind:

JASS:
scope FolderA
    public struct B
        method test takes nothing returns nothing
        endmethod
    endstruct

    public struct C
    endstruct

    public struct D
    endstruct
endscope

struct A
    FolderA_B B
    FolderA_C C
    FolderA_D D
endstruct

From struct A I can access its "inner" structs FolderA_B/FolderA_C/FolderA_D by going over the variables B/C/D. These variables are not static since every struct instance shall also transfer its own index to the inner struct.

For

JASS:
call A(3).B.test()

"this" should be assigned to thistype(3) in the test method. So the variable B of A(3) has to be set to 3, too. The question for now is how to effectively initialize the variables for each instance.

You cannot assume that every instance of A is fetched by the normal .allocate() method. Additionally, I would like to have done all initializations only once in the beginning. This means there have to be functions that iterate through the whole struct and set the variables.

Either (simplified)

JASS:
static method InitLinks takes nothing returns nothing
    local integer i = STRUCT_MAX

    loop
        set thistype(i).B = i
        set thistype(i).C = i
        set thistype(i).D = i

        exitwhen (i == STRUCT_MIN)
        set i = i - 1
    endloop
endmethod

or

JASS:
static method InitLinkB takes nothing returns nothing
    local integer i = STRUCT_MAX

    loop
        set thistype(i).B = i

        exitwhen (i == STRUCT_MIN)
        set i = i - 1
    endloop
endmethod

JASS:
static method InitLinkC takes nothing returns nothing
    local integer i = STRUCT_MAX

    loop
        set thistype(i).C = i

        exitwhen (i == STRUCT_MIN)
        set i = i - 1
    endloop
endmethod

JASS:
static method InitLinkD takes nothing returns nothing
    local integer i = STRUCT_MAX

    loop
        set thistype(i).D = i

        set i = i - 1
        exitwhen (i == STRUCT_MIN)
    endloop
endmethod

Either everything in one method or separated. Now, it is enwrapped in a textmacro for practical use.

JASS:
struct A
    //! runtextmacro LinkToStruct("A", "B")
    //! runtextmacro LinkToStruct("A", "C")
    //! runtextmacro LinkToStruct("A", "D")
endstruct

And there a problem appears. If I would be to use the first method (everything together), I would write

JASS:
//! textmacro LinkToStruct takes folder, struct
    set thistype(i).$struct$ = i
//! endtextmacro

and insert it into the function hull:

JASS:
static method InitLinks takes nothing returns nothing
    local integer i = STRUCT_MAX

    loop
        //here comes the textmacro calls

        exitwhen (i == STRUCT_MIN)
        set i = i - 1
    endloop
endmethod

This means that I would need an initial and an end mark for this hull. However, the struct instance variable has still to be declared and this cannot be done inside a method. Ergo, one textmacro does not suffice.

That is why I wrote it this way till now:

JASS:
struct A
    //! runtextmacro LinkToStruct("A", "B")  //declaring the variables
    //! runtextmacro LinkToStruct("A", "C")
    //! runtextmacro LinkToStruct("A", "D")

    //! runtextmacro InitLinksToStruct_Start()
    //! runtextmacro InitLinksToStruct_NewMember("B")
    //! runtextmacro InitLinksToStruct_NewMember("C")
    //! runtextmacro InitLinksToStruct_NewMember("D")
    //! runtextmacro InitLinksToStruct_Ending()
endstruct

This is not that nice because of redundance and error-proneness. You could forget to initialize a single member for example. The compiler would not mind.

So I tried it via the second possibility: To have an own init method for each link:

JASS:
//! textmacro LinkToStruct $folder$, $struct$
    Folder$folder$_$struct$

    static method InitLink$struct$ takes nothing returns nothing
        ...
    endmethod
//! endtextmacro

Well, now, these init functions have to be executed and vJass only allows one onInit per struct/module. If I would do every call manually, it would be inconvenient again and also error-prone like above. In vJass, it is nearly impossible to export code to a marker. An exception would be //! inject, which only works one time for main and one time for config, another would be the struct instance initialization on allocate():

JASS:
struct A
    integer B = 4
endstruct

The variable B would be set to 4 on allocation here and you can abuse it and enter non-constant values like integer B = this. But I would like to keep the normal allocate function and generating code via external tool via lua script also has its problems.

One possibility that I had still seen was to work with static ifs. Basically, in the onInit method, I had to know which InitLink methods exist. Since I cannot ask for each combination of characters, it would be nice if the methods were enumerated.

JASS:
static method InitLink1 takes nothing returns nothing
    //initialize B
endmethod

static method InitLink2 takes nothing returns nothing
    //initialize C
endmethod

static method InitLink3 takes nothing returns nothing
    //initialize D
endmethod

static method onInit takes nothing returns nothing
    static if thistype.InitLink1.exists then
        call thistype.InitLink1()
    endif
    static if thistype.InitLink2.exists then
        call thistype.InitLink2()
    endif
    static if thistype.InitLink3.exists then
        call thistype.InitLink3()
    endif
    static if thistype.InitLink4.exists then
        call thistype.InitLink4()
    endif
    static if thistype.InitLink5.exists then
        call thistype.InitLink5()
    endif
    ...//for a fixed maximum amount
endmethod

How could I number them all the way through only using the textmacro inputs and without adding the number as parameter?

JASS:
//! runtextmacro LinkToStruct("A", "B")

This lead me to the following construction:

JASS:
//! textmacro LinkToStruct takes folder, struct
    Folder$folder$_$struct$ $struct$

    static if not thistype.InitLink1.exists then
        static method InitLink1 takes nothing returns nothing
            ...
        endmethod
    else
        static if not thistype.InitLink2.exists then
            static method InitLink2 takes nothing returns nothing
                ...
            endmethod
        else
            static if not thistype.InitLink3.exists then
                static method InitLink3 takes nothing returns nothing
                    ...
                endmethod
            else
                ...
            endif
        endif
    endif
//! textmacro

Unfortunately, static ifs are badly made in vJass. On the first run InitLink1 gets created, nothing after that. This originates from the circumstance that, while checking .exists, it looks whether the searched function is somewhere above and in case this function is declared in a as false evaluated static if block, this counts nonetheless. It would be the same if I used an own constant boolean variable. Therefore I cannot obtain another outcome each textmacro call with static ifs because everything would be falsly declared after the first time.

So, anyone got another idea?

edit: solved, I am allocating all instances now and immediately deallocate them again onInit. Thus, the links are set, the normal allocation method is still usable and the function gets run by a private onInit method in a module that is implemented into the textmacro. Implementing the same module only works once but does not throw an error.
 
Last edited:
Status
Not open for further replies.
Top