Tutorial Introduction to Struct

Discussion in 'Graveyard' started by Ayanami, Nov 17, 2011.

  1. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Table of Contents
    1. Introduction
    2. Concept of Instances
    3. Struct Declaration
    4. Struct Instantiation
    5. Constructor Overloading
    6. Multiple Constructors
    7. Members
    8. Methods
    9. Static vs Instance
    10. thistype
    11. Destroying Instances & onDestroy
    12. onInit Method
    13. Naming Conventions
    14. Closing
    15. FAQ


    1. Introduction

    Structs are very powerful and should be learned as soon as possible if you're planning to use vJASS. This tutorial assumes that you have general knowledge of JASS. If you have ever taken other Programming Languages, a Struct is more commonly known as a Class. You must understand that a Struct generally represents an object. This object can represent anything you want (ex: Car, Book, Person, etc). This object would then be a variable type that you can use (like integers, reals, strings, etc).

    JASS:
    
    // struct declaration, will go through this later on
    struct Person
    endstruct
    
    function Test takes nothing returns nothing
        local real r
        local Person p // you can treat structs as a variable type
    endfunction


    Summary: Structs are objects and they can be treated like variable types.


    2. Concept of Instances

    The next important concept about Struct is the various instances that you can have inside the Struct. Instances are exactly what they sound like. In a struct, you can have many instances created inside of it.

    I'll elaborate this with an example. Let's say you have a Struct called Bicycle. So basically, this would mean that you could have multiple bicycles inside the Struct. Let's say the Struct has 2 instances instantiated. This means that there are currently 2 bicycles that are present under the Struct Bicycle. You can also imagine Struct instances as variable array indices. For example, you can have Bicycle[1] and Bicycle[2].

    There is a limit for number of instances in a single struct. The magical number is 8190, remember this number. Once you pass this limit, your struct will basically go haywire from that point.

    Summary: Instances are basically the number of objects that exist in the struct. They are basically the array indices of an array. The instance limit for a struct is 8190.


    3. Struct Declaration

    To use a struct, it must obviously be declared. It's very simple to declare a struct. The format is: struct <name>. Pretty simple, right? Do note that you need the endstruct to close the struct, much like a function.

    JASS:
    
    struct A
    endstruct


    The above example basically declares a struct called "A".

    Summary: Format for struct declaration is: struct <name> followed by endstruct


    4. Struct Instantiation

    So a struct can have multiple instances. Thus, you need to know how to create an instance. A special function exists to create an instance for you. This is known as a constructor. A constructor's job is to basically create a new instance for the user. The format for using the constructor is <struct name>. create(). This calls the constructor which returns the created instance:

    JASS:
    
    struct Bicycle
    endstruct
    
    private function Test takes nothing returns nothing
        local Bicycle d = Bicycle.create()
    endfunction


    As shown above, .create() is the syntax to create a new instance. Since you're trying to create a instance of struct Bicycle, you need to specify the struct name, this Bicycle.create(). The constructor always returns the struct type. It's quite obvious; you need to store the created instance. What's the point of creating an instance and not storing the instance anywhere, right? So basically, your variable d holds an instance of the Struct type Bicycle.

    Instances are actually integers. If you were to do:

    JASS:
    
    struct Bicycle
    endstruct
    
    private function Test takes nothing returns nothing
        local Bicycle b = Bicycle.create()
        
        call BJDebugMsg(I2S(b)) // this is valid and would print an integer
    endfunction


    The maximum struct instances you can have is 8190, like stated before. Just remember that struct instances are integers.

    Summary: To instantiate (create) an instance, you need to call the constructor, which has the format: <struct name>. create(). Instances are actually integers, just like array indices.


    5. Constructor Overloading

    So now you're able to instantiate instances. However, what if we want to take parameters for our constructor? You can override the default constructor, the .create() method.

    Note that a function inside of a struct is called a method. As for the static part, you can ignore it for now. I'll be covering that in a while.

    JASS:
    
    struct Bicycle
        public static method create takes unit u returns Bicycle
            local Bicycle this = Bicycle.allocate()
         
            call BJDebugMsg(GetUnitName(u))
    
            return this
        endmethod
    endstruct
    
    private function Test takes nothing returns nothing
        local Bicycle b = Bicycle.create(SomeUnit)
    endfunction


    Notice that .create takes a unit now? You've successfully overloaded the constructor. Basically, now I've customized the constructor to take a unit as an argument and print that unit's name. Then, return created instance. The .allocate() is always needed when you want to instantiate an instance. Remember to include that inside your custom constructor.

    A custom constructor only has 2 rules. Firstly, it needs to be a static method. I'll be covering this later on. Secondly, it needs to always return the struct type. If you happen to do things like returns nothing, it'll give you a compilation error. However, doing returns integer would be valid, as instances are actually integers, which I mentioned previously.

    If a custom constructor is not declared, a default constructor will be automatically declared for you, which has no parameter.

    Summary: A custom constructor can be used to take parameters. However, constructors must be static and must returns the struct type (or integer). A default constructor is automatically created if no custom constructor is declared, which has no parameters.


    6. Multiple Constructors

    It is possible to have many constructors.

    JASS:
    
    struct A
        public static method new takes nothing returns A
             local A this = A.allocate()
             
             return this
        endmethod
    endstruct
    
    private function Test takes nothing returns nothing
            local A a = A.create() // works
            local A b = A.new() // would work as well
    endfunction


    So what's the point of having multiple constructors? Well here's the thing: as long as your constructor isn't called .create(), you can choose to returns nothing. However, that would kind of defeat the purpose of a constructor and it wouldn't really be called a constructor anymore.

    The main reason why we might want multiple constructors is that you might need different constructors for different purposes. For example, you might need a constructor with no parameters sometimes and you might need a constructor with parameters another time. Just note that you always need the .allocate() method to instantiate an instance, it should always be inside your constructor.

    Summary: You can have multiple constructors as long as they are static methods. As long as your constructor isn't called .create(), you can actually return any type of variable or return nothing at all. Most of the times, you only need a single constructor.


    7. Members

    If methods are equivalents of functions, members are equivalents of variables. Basically, members are variables inside of a struct.

    JASS:
    
    struct A
        real r
        integer i
        boolean b
        static unit u // yes, there are static members as well, I'll be covering that soon
    endstruct


    Members can also have access modifiers. What do I mean by this? Simply put, they can have the private, public and readonly modifiers.

    private members are basically only, and only accessible inside the struct. public members are accessible by things outside the struct. By default, members are public if you do not add any modifier. readonly members are kind of special. The user can access this variable outside of the struct, however the user is not able to modify it. You're only able to "get" the value. They can only be modified from inside the struct.

    JASS:
    
    struct A
        private static integer a = 0
        public static integer b = 0
        static integer c = 0
        readonly static integer d = 0
    endstruct
    
    private function Test takes nothing returns nothing
        set A.a = 1 // ignore the A. for now. Not legal, as "a" is private
        call BJDebugMsg(I2S(A.a)) // still not legal, as "a" is private
    
        set A.b = 1 // legal, it is public
        call BJDebugMsg(I2S(A.b)) // legal as well, it is public
    
        set A.c = 1 // legal, "c" is automatically defined as public since no modifier was given
        call BJDebugMsg(I2S(A.c)) // legal as well, it is public
    
        set A.d = 1 // not legal, as "d" is readonly
        call BJDebugMsg(I2S(A.d)) // legal, since readonly allows you to "get" the value
    endfunction


    Summary: Members are variables inside a struct. There are three types of access modifiers for members: private, public and readonly. A member with no modifier is automatically a public member.


    8. Methods

    I've already mentioned that methods are basically functions in struct.

    JASS:
    
    struct A
        function Catastrophic takes nothing returns nothing // compiler screams at you for doing this, you must use methods
        endfunction
    endstruct


    Methods are exactly same as functions. You have parameters and a return type. All you need to change is the function keyword to method. Methods can be static as well. I'll be covering that the next section (promise).

    Methods, can have access modifiers as well. There are only 2 modifiers for methods though, private and public. private methods can be accessed only inside the struct. public methods can be accessed by things outside of the struct. A method without a specified modifier becomes automatically public. It's the exactly same like member modifiers.

    Summary: Methods are functions inside a struct. There are two types of access modifiers for members: private and public. A member with no modifier is automatically a public member.


    9. Static vs Instance

    So you've been seeing the keyword static all over the place. I'm going to explain the different between static and instance.

    In a struct, you can have static and instance (non-static) methods and members. Basically, instance members and methods are associated with an instance. Let's look at instance members first:

    JASS:
    
    struct Bicycle
        real speed // this is public as no access modifier is specified
    endstruct
    
    private function Test takes nothing returns nothing
        local Bicycle a = Bicycle.create() // creating an instance of Bicycle
        local Bicycle b = Bicycle.create() // creating another instance of Bicycle
    
        set a.speed = 10.0
        set b.speed = 30.0
    endfunction


    The above demonstrates instance members. Basically, the member speed can be associated with instances and each instanced speed can have different values. You can imagine them as arrays:

    JASS:
    
    struct Bicycle
        real speed
    endstruct
    
    globals
        private real array SpeedArray
    endglobals
    
    private function Test takes nothing returns nothing
        local Bicycle a = Bicycle.create() // creating an instance of Bicycle
        local Bicycle b = Bicycle.create() // creating another instance of Bicycle
    
        set a.speed = 10.0
        set b.speed = 30.0
    
        set SpeedArray[0] = 10.0 // notice the similarities?
        set SpeedArray[1] = 30.0
    endfunction


    So basically, you can imagine instances a and b to be the array indices 0 and 1. This is how an instance member works.

    Instance methods are similar to instance members, they are associated with an instance.

    JASS:
    
    struct Person
        string name // instance member
    
        public method displayName takes nothing returns nothing
            call BJDebugMsg("This person's name is: " + this.name)
        endmethod
    endstruct
    
    private function Test takes nothing returns nothing
        local Person a = Person.create()
        local Person b = Person.create()
    
        set a.name = "John" // setting Person a's name as John
        set b.name = "Mary" // setting Person b's name as Mary
    
        call a.displayName() // almost like a function call, except you associate an instance with it by using a.
        call b.displayName()
    
        // the result would be:
        //
        // This person's name is John
        // This person's name is Mary
    endfunction


    So basically, the instance is passed into the method. You might be wondering what is the this keyword for. Basically, this inside a instance method refers to the instance that you passed in. So you call a.displayName(), the instance a would be passed into the method displayName. Now you need a way to refer to the instance a inside of the method. Thus, we use the this keyword to refer to that instance inside of an instance method. You can imagine the above as such:

    JASS:
    
    globals
        private string array Name
    endglobals
    
    private function DisplayName takes integer this returns nothing
        call BJDebugMsg("The person's name is: " + Name[this])
    endfunction
    
    private function Test takes nothing returns nothing
        set Name[0] = "John"
        set Name[1] = "Mary"
    
        call DisplayName(0)
        call DisplayName(1)
    endfunction


    The above code is basically to illustrate what instance methods do.

    The this keyword can be omitted and simply just put as .

    JASS:
    
    struct Person
        string name
    
        public method displayName takes nothing returns nothing
            call BJDebugMsg("The person's name is: " + .name) // you can do .name instead of this.name
            call BJDebugMsg("The person's name is: " + name) // you can also simply refer it as name instead of .name or this.name
        endmethod
    endstruct


    The static keyword is the opposite of instance keyword. A static member and method does not associate any instances. Let's touch on static members first. Static members are simply global variables inside of a struct. They are not associated with an instance.

    JASS:
    
    struct Person
        static integer population
    endstruct
    
    private function Test takes nothing returns nothing
        set Person.population = 10 // setting a static integer to 10, just like a global
    
        // notice the Person. in front. This basically is saying that you're trying to access a static member in struct Person
        // whenever you want to use a static member outside of the struct, you need to include the struct name in front followed by a .
        // this can be omitted inside of the struct itself, but it's good practice to always include it, even inside of the struct
    endfunction


    Essentially, static members are global variables inside of a struct. They have no instance associated, thus they have a single value for one struct, unlike instance members.

    Static methods are simply your normal functions inside of a struct.

    JASS:
    
    struct Person
        public static method displayHello takes nothing returns nothing
            call BJDebugMsg("Hello!")
        endmethod
    endstruct
    
    private function Test takes nothing returns nothing
        call Person.displayHello() // notice the Person. again, this is the rule to call static methods
    endfunction


    This summarizes the difference between static and instance (non-static).

    Summary: You can have Static and Instance methods and members. Instance methods and members are associated by an instance. Inside an instance method, you use the this keyword to refer to the instance that was passed in to the method. Static methods and members are not associated with an instance, and act like global variables and normal functions inside of a struct.


    10. thistype

    The thistype keyword is only used inside of a struct. It's basically a keyword to refer to the struct name.

    JASS:
    
    struct A
        public static method create takes nothing returns thistype // instead of saying "return A", we can just say "return thistype"
             local thistype this = thistype.create() // you can do this instead of doing local A this = A.create()
    
            return this // notice I used "this". Basically, in a static method, you can use "this" as a variable name.
        endmethod
    endstruct


    Thus, when you use static methods from within a struct, you can do thistype.<static method name instead of <struct name><static method name. This is also same for static members. But remember, you can only use thistype inside of a struct.

    Summary: Instead of using the struct name, you can use thistype to refer to the struct name. This only applies inside of a struct.


    11. Destroying Instances & onDestroy

    I mentioned that struct have a limit of 8190 instances. This would mean we would hit the limit eventually if we continuously create instances. Thus, we need to destroy instances when we don't need them anymore.

    JASS:
    
    struct Bicycle
    endstruct
    
    private function Test takes nothing returns nothing
        local Bicycle d = Bicycle.create() // creating an instance
    
        // other actions...
    
       call d.destroy() // destroying the instance
    endfunction


    You would have noticed that you don't do Bicycle.destroy(). This is because Bicycle is the struct itself. When you destroy something, you want to destroy the instance of Bicycle, not the struct itself. Thus, it's d.destroy(). The variable d holds the instance, which is the reason why you do d.destroy(). That's the syntax to destroy.

    Similar to .create(), you can overload the .destroy() method. However, remember that the destructor ( .destroy()) is an instance method, not a static method. However, you can also have a static destructor as well. Destructor doesn't have any parameter or return specifications. You need to remember to include a line though, if you happen to override the destructor or define a custom destructor; this.deallocate().

    JASS:
    
    struct Bicycle
        public method destroy takes nothing returns nothing
            call this.deallocate() // the line of code that you NEED for any deconstructor
        endmethod
    endstruct


    There's also something known as the onDestroy. This is a special method that is ran right before an instance is destroyed:

    JASS:
    
    struct Bicycle
        string name
    
        private method onDestroy takes nothing returns nothing
            call BJDebugMsg(name + " is being removed!")
        endmethod
    endstruct


    The above method is ran every time when an instance is about to be destroyed. However, it's better off overriding the destructor directly rather than using onDestroy for most situations. onDestroy has to take no parameters and return nothing. It also has to be an instance method.

    Remember to always destroy your instances after you're done with them, or else it will be catastrophic once you hit the instance limit.

    Summary: You can destroy instances by using the .destroy() method. If onDestroy is declared, that method will run right before an instance is destroyed. It has to be an instance method as well as take no parameter and no return.


    12. onInit Method

    There's also another special method called the onInit method. This is an initialization method. Simply put, this method runs right when the game starts. The onInit method must be static and cannot take parameters.

    JASS:
    
    struct A
        private static method onInit takes nothing returns nothing
            call BJDebugMsg("Hello!") // would print "Hello!" right after the loading screen; when game starts
        endmethod
    endstruct


    Another thing to take note about onInit is that it is ran before scope/library initializers.

    JASS:
    
    struct MyStruct
        private static method onInit takes nothing returns nothing
            call BJDebugMsg("Struct says hello!")
        endmethod
    endstruct
    
    library MyLib initializer OnInit
        private function OnInit takes nothing returns nothing
            call BJDebugMsg("Library says hello!")
        endfunction
    endlibrary
    
    // the above would print:
    //
    // Struct says hello!
    // Library says hello!


    From the above, you can see that struct onInit is always ran before any library/scope initializers.


    13. Naming Conventions

    There are naming conventions for members, methods and struct name. These conventions are generally followed by most people. For more information, you can take a look at this.

    JASS:
    
    struct ThisIsMyName // struct names in ProperCase
    
    method thisIsMyMethod // method names in camelCase
    
    unit thisIsMyUnit // member names in camelCase



    14. Closing

    I hope this helped all of you who are struggling to grasp the concept of struct. I hope you will be appreciate struct after reading this tutorial. If you have any questions, feel free to ask, and I'll add the important ones in the FAQ section.


    15. FAQ
     
    • Like Like x 2
  2. Sgqvur

    Sgqvur FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi

    Ratings:
    +62 / 0 / -0
    I have a few notes:


    I think the section: 5. Constructor Overriding, should be called Constructor Overloading, because
    Overriding is an inheritance/specialisation term, and overloading is simply making a function take different number and type of arguments, i.e what you are describing.

    In section 7. Members, I think that the term 'members' is used for a struct'/class's fields/variables/properties and for it's methods/functions/subroutines.
    So I guess that you can merge section 7 and 8.

    In section 9: call BJDebugMsg("The person's name is: " + .name) // you can do .name instead of this.name; you can also omit .name in struct methods, i.e simply use name

    In section 11: I read about a "deconstructor", but I can't remember reading about it before, did you mean destructor?

    Also you don't seem to mention that the destroy method can be used as a static method:
    JASS:
    struct S
    endstruct
    
    ...
    local S s = S.create()
    call s.destroy()  // valid
    call S.destroy(s) // also valid
    ...


    The explanation for the onDestroy method is very "shallow", i.e for which situations the onDestroy is useful?

    In section 12:
    JASS:
    struct ThisIsMyName // struct names in ProperCase; did you mean PascalCase?
    
    method thisIsMyMethod // method names in camelCase
    
    unit thisIsMyUnit // member names in camelCase


    Who's naming conventions are those? Your's? Java's? vJass's? Why should someone bother with them?
     
  3. Dirac

    Dirac 22710180

    Ratings:
    +147 / 0 / -0
    The word "Array" is written all over the place!
    This tutorial is great and i recommend it.

    IMO the onDestroy part should be removed since it shouldn't be used.
    You should cover the allocation/deallocation methods a bit further, the user should know how do they work. (And use Alloc as a reference)
    Also mention why should a struct extend array and what's the difference.
     
  4. GFreak45

    GFreak45 I didnt slap you, i high 5'd your face.

    Ratings:
    +132 / 0 / -0
    amazing tutorial, truly gets down to the finest attributes to structs, one thing i would change is the divergent language
    if you could provide a more lamens terms definition or glossary to the top or bottom it would be great, that way those with little or no knowledge on structs could understand all of it regardless
     
  5. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Hmm, I shall consider writing about struct extending array and in-depth of allocation/deallocation methods elsewhere. This is actually meant to be a very basic coverage of struct, so the users actually don't really need to know the actual code behind allocation/deallocation methods. I mean, it would be good to know, but it will probably confuse people who have no knowledge of structs.

    Anyways, changed deconstructor to destructor. And I might add a glossary, that's a good idea.
     
  6. NoobImbaPro

    NoobImbaPro You can change this now in User CP.

    Ratings:
    +60 / 0 / -0
    I'm gonna make a struct tutorial too, but a little more advanced. Well for starters this is one of the best tutorials here.
     
  7. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Last time I heard, overloading is only when you can have multiple methods with the same names but different parameters. So in this case, it's not really overloading. Once you declare your own create method, your default one will be overwritten. That's why I called it Method Overriding. But I do know it's related to Inheritance, so I just changed it to Method Overloading.

    Forgot about that, thanks for mentioning.

    Unfortunately, this is wrong. Destroy method has to be non-static and cannot have any parameters.

    Isn't this almost never? Unless inheritance is involved. I mean using destroy would be better than using onDestroy for every other situation.

    Included a section for onInit method.
     
  8. GFreak45

    GFreak45 I didnt slap you, i high 5'd your face.

    Ratings:
    +132 / 0 / -0
    Hey ayanami, im about to post requesting help on understanding this in the jass zone, but im wondering:
    could you add information on operators? i only see them in structs
    like
    private operator a= blah blah blah
     
  9. Dirac

    Dirac 22710180

    Ratings:
    +147 / 0 / -0
    Operators would be part of the advanced part of this tutorial.

    Anyways operators aren't difficult to understand, they're basically the same as () or [] but instead use =, >, < etc.
    A few examples
    JASS:
    static method operator v= takes integer i returns nothing
        set count=count+i
    endmethod
    
    //so when you do the following
    set v=3
    //the count var is now equal to 3, and if you do this after that..
    set v=5
    //the count var would be equal to 8 now.
    
    static method operator v takes nothing returns integer
        set count = count*2
        return count
    endmethod
    
    //now if you ask what's the value of "v" anywhere it would display...
    BJDebugMsg(I2S(v)) //returns 16
    BJDebugMsg(I2S(v)) //returns 32
    BJDebugMsg(I2S(v)) //returns 64
    
    //There are also other types of operators
    
    static method operator [] takes unit u returns thistype
        return GetUnitUserData(u)
    endmethod
    
    Struct[unit] -> struct instance
    
    static method operator []= takes unit u, integer i returns nothing
        call SetUnitUserData(u,i)
    endmethod
    
    set Struct[unit]=3
    
    //side note: read the JassHelper manual, this info is ALL there
    Sometimes I wonder if you had read the manual already.
     
  10. GFreak45

    GFreak45 I didnt slap you, i high 5'd your face.

    Ratings:
    +132 / 0 / -0
    wow you manage to be a dick and help at the same time, i no longer know what to do with you...

    so something like:

    JASS:
    static method operator ()= takes unit u, integer i returns nothing
        call SetUnitUserData(u, i)
    endmethod
    
    static method operator ()= takes item u, integer i returns nothing
        call SetItemUserData(u, i)
    endmethod


    would make it so that

    JASS:
    set Struct(unitvar)=0
    and
    set Struct(itemvar)=0


    would work, allowing you to basically use 2 different arguement types for the "same" function?
     
  11. Dirac

    Dirac 22710180

    Ratings:
    +147 / 0 / -0
    No, when you're overriding that operator, only one per character is allowed
    JASS:
    static method operator ()= takes unit u, integer i returns nothing
        call SetUnitUserData(u, i)
    endmethod
    
    static method operator []= takes item u, integer i returns nothing
        call SetItemUserData(u, i)
    endmethod
    Please clear up wether you have read the manual or not
     
  12. GFreak45

    GFreak45 I didnt slap you, i high 5'd your face.

    Ratings:
    +132 / 0 / -0
    I have read it but it was before i really learned anything about jass and most of it did not make sense to me, and i no longer have time to read it as i am working 50 hrs a week, attending school, and have an immediate family of over 10 people that i still try to spend time with (brothers and sisters, not including cousins etc)

    so essentially its just a way to shorten the time it takes to write a system...
    does it add anything you cant do easily with out them, except basically turn vJass into baby C++?

    it would be considerably more usefull if it could compile it by expanding the arguements and checking types, allowing you to have more than one argument allowed for a single type operator

    can you do this with functions?
    ie:
    JASS:
    private function operator x++ takes nothing returns nothing
        set x = x + 1
    endfunction
     
  13. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    the onDestroy method runs off of trigger evaluations. When an object is destroyed, it's destroy trigger is evaluated. Now, I don't remember how it constructed that trigger. It's either set to an already generated trigger by vjass or constructed in the allocators. Don't remember what it was, but w/e, that's not important. Essentially, it's one trigger that is evaluated to run all of the onDestroys so that everything gets destroyed properly. Ofc, it would be less overhead and make more sense to just call the destructors in order. This would still require 1 trigger evaluation, but at least it wouldn't be a whole chain of them.


    The onDestroy trigger is always called when inheritance is involved, even if onDestroy was never declared. Because of this, I always highly recommend against inheritance and against using standard vjass structs in general =p.
     

Share This Page