• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Structs unusable in hashtables?

Status
Not open for further replies.

Rui

Rui

Level 41
Joined
Jan 7, 2005
Messages
7,550
So I was attempting to save a struct in a hashtable but JASSHelper kept scolding me for passing an integer instead of a handle. I had no clue why, but then I took a look at the script and realized that in vJASS the local struct x is actually replaced by «local integer x».
The script below explains better:
JASS:
    static method create takes unit u returns Territory
        local Territory NT=Territory.allocate()
        call SaveInteger(udg_HtH,GetHandleId(NT),0,GetHandleId(u))
        call SaveInteger(udg_HtH,GetHandleId(u),40,GetHandleId(NT))
        call SaveReal(udg_HtH,GetHandleId(u),30,size)
        return NT
    endmethod
Code:
function s__Territory_create unit u returns integer
        local [B][U]integer[/U][/B] NT=s__Territory__allocate()
        call SaveInteger(udg_HtH, GetHandleId(NT), 0, GetHandleId(u))
        call SaveInteger(udg_HtH, GetHandleId(u), 40, GetHandleId(NT))
        call SaveReal(udg_HtH, GetHandleId(u), 30, size)
        return NT
    endfunction
Notice it's been replaced by an integer. Perhaps if I change GetHandleId(NT) for simply «NT» it'll work?
Tertiary options / thoughts?

P.S. — I'm asking because I don't know how structs are actually converted to regular JASS and I'm afraid of collisions.
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
Structs are not handles, they're integers. So you have to replace GetHandleId(NT) with NT.

JASS:
static method create takes unit u returns Territory
    local Territory NT=Territory.allocate()
    call SaveInteger(udg_HtH,NT,0,GetHandleId(u))
    call SaveInteger(udg_HtH,u,40,NT)
    call SaveReal(udg_HtH,GetHandleId(u),30,size)
    return NT
endmethod

Structs are basically arrays, and when you use a local struct variable, it is converted to integer, and its index is what gets passed.
 
Yes it works but you should be carefull :
2 different variables of type Territory won't "have the same ID" (meaning that, in the converted jass code, it won't be the same integer).
But 2 different variables of 2 different structs can totally have the same "ID" and it may cause conflicts if you use something like this :
JASS:
function createTerritory takes unit u returns Territory
    local Territory NT=Territory.allocate()
    call SaveInteger(udg_HtH,NT,0,GetHandleId(u))
    return NT
endfunction

function createStruct2 takes unit u returns Struct2
    local Struct2 S2=Struct2.allocate()
    call SaveInteger(udg_HtH,S2,0,GetHandleId(u))
    return S2
endfunction

When you create one struct, it may overwrite the parent key used by a struct of the other kind.

I don't think of a good solution for solving this problem right now...
Maybe you can global indexes like this :
JASS:
globals
    constant integer HASH_TERRITORY_INDEX = 8193
    constant integer HASH_STRUCT2_INDEX      = 8193 * 2
endglobals

function createTerritory takes unit u returns Territory
    local Territory NT=Territory.allocate()
    call SaveInteger(udg_HtH,HASH_TERRITORY_INDEX+NT,0,GetHandleId(u))
    call SaveInteger(udg_HtH,u,40,NT)
    return NT
endfunction
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
I don't know about any as I've never read anything about structs (learning everything all alone is much more fun). I can explain it a bit:
JASS:
struct foo
    integer unitType
    unit effects[3]
    static boolean allowAdd
    
    method doSomething takes nothing returns nothing
    endmethod

    static method doSomethingStatic takes nothing returns nothing
    endmethod
endstruct
function doFunction takes nothing returns nothing
    local foo instance=foo.create()
    set instance.unitType='hfoo'
    set instance.effects[2]=CreateUnit(...)
    set foo.allowAdd=false
endfunction

So we've got something like this. Not that much, but contains enough to give you a start.

First of all, when you create a new instance, it will return the next available id (integer). Destroyed instances will have their ID recycled, of course. Each time you do something for a non-static member, it'll be converted to this:

JASS:
    set instance.unitType='hfoo'

-->

JASS:
    set s_foo_unitType[this]='hfoo'

Secondly, methods. We've got two kind of methods: statics and non-statics (you may also call them dynaimcs). The difference between them is that when the script is compiled, non-statics get one extra argument: integer this. That way you don't have to bother yourself with storing the instance id - this is ready to use. Of course, you can declare and define this in statics.

Array variables look like this:
JASS:
    set instance.effects[2]=CreateUnit(...)

-->

JASS:
    set s_foo_effects[this*arraySize+2]=CreateUnit(...)

Did I forgot to mention anything? Feel free to ask (I'm not that good at explaining things in english, sadly)
Also, this may be also a cool tutorial.
 
Structs pop an integer ranging from 1-8190. All non-static variables
that you declare will get turned into arrays and the struct integer will
be used as a pointer for the array.

The idea behind structs is because each popped number is unique,
the arrays will not clash allowing you to store data particular to the
individual struct integer you allocated.

I can see why you tried to use GetHandleId. The problem with
structs is that they are vJass fluff created by Vexorian which are
really not as magical and integrated as they seem. Yes they are
very useful obviously as structs are ubiquitous these days, but
it really isolates an otherwise open environment, such as saving
structs into hashtables just by their index itself.

JassHelper also doesn't allow certain things to compile that work
otherwise just fine in world editor, such as certain rawcodes or the
position of certain return statements.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
It's more pjass than jasshelper.

About the return statements if i remember correctly the map script is validated if there is one or more return (talking about the official editor or directly editing the map script with a mopaq editor such as winmpq) :

JASS:
function Test takes nothing returns integer

   if false then
      return 0
   endif

endfunction

This function will be accepted but when it will be called the thread will crash.

So i don't know which is worst, pjass or official way.
Maybe a warning should be displayed but the map still saved ?
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I know, here it's easy to know if the function will work or not, but when the condition is dynamic, there is no way.
Ofc there is one used by the official editor : hope the user know he is doing and check if there is at least one return.

That's why i suggested a warning instead of a compilation error, quite like decent compilators for C (or any other language).

Also pJass source code is available, i've even found the flaw in the grammar file flex/bison, but currently i have not the knowledge to correct it.

I would also increase the limit of code handled by pJass before running a "memory exhausted error", or maybe it's jasshelper fault, that would be harder if it is the second one :p
 

Rui

Rui

Level 41
Joined
Jan 7, 2005
Messages
7,550
I understood how static members function, they're basically a "global" variable of a struct type, for what I understand, but when a method should be static or not still confuses me.

Also, Bribe, it apparently also allows things that should not be allowed. It accepted this line: local Territory y=GetHandleId(udg_HtH,a,40). It should actually be LoadInteger() obviously, but JASSHelper did not notice the excessive amount of arguments.
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
Well, it should always be non-static, if you can retrieve the instance (for example when you attach one to a timer and load it when the timer expires). That way you don't have to declare it in the method.

Example:

JASS:
struct foo
    method doSomething takes nothing returns nothing
        call this.destroy()
        //"this" is already declared, as the function gets one more argument 
        //when compiled: "integer this"
    endmethod

    static method doSomethingStatic takes nothing returns nothing
        call this.destroy()
        //You can't do this, as "this" is not declared - however you 
        //can declare it:
        local thistype this=thistype.allocate()
        local thistype this=LoadInteger(HT,0,0)
        local thistype this=12
        local thistype this=GetTimerData(GetExpiredTimer())
        local thistype this=GetUnitUserData(u)

        //You can also modify "this", no matter if the method is static or not:
        set this=GetUnitUserData(u2)
        set this=thistype.allocate()
        set this=thistype.create()
    endmethod
endstruct
 
Status
Not open for further replies.
Top