Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Structs (A rough crash course)

Discussion in '"Graveyard"' started by MyPad, May 10, 2017.

  1. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,544
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9


    • Introduction to Structs
      (Part I)



      (By MyPad)


      Hello reader. By having clicked this link here, I assume that you want to learn structs, right? Yes, you do, and what a wordy tutorial this would be.

      What we'll need to have is (either):
      1. JNGP or
      2. Sharpcraft WEX (for versions 1.24. - 1.29.2)
      3. Vanilla World Editor (For 1.30+ above)​

      What this tutorial will cover:
      Part I - Intro
      - What is a Struct?
      - Declaration of (Independent) Structs
      - Members
      - Methods
      Part II - Encapsulation
      - History
      - Libraries
      - Scopes


      I. What is a Struct?


      A struct is a collection of data fields or data types which acts as a data type itself.
      In other programming languages, a struct is called a class.


      II. Declaration of (Independent) Structs


      Structs as defined above seem ambiguous to you. What does it actually mean? Let's just show a struct, since an example is worth a thousand words..

      Code (vJASS):

              struct someType
                  integer count
              endstruct
         


      What did we declare here? We declared a struct in vJASS. (JASS does not support this, and vJASS parses a struct into JASS) (You'll know why later on) ;)

      Note that to declare a struct (in vJASS), you will have to type it like this:

      Code (vJASS):

              struct <name_of_struct>
              endstruct
         


      ... and you're good to go.


      III. Structs (Members)



      Notice that the example before had
      integer count
      inside the struct. Didn't notice?
      Well, that was a member of the struct.

      The integer count is a member of the struct someType or a data field in the struct. There are two kinds of members: static and normal

      Normal members are those that are present per instance.
      Static members are those that are reserved to the whole struct.

      What's their difference? Let's take a look:

      Normal members
      Code (vJASS):

              struct someType
                  integer count
              endstruct

              ...
         
              local someType that = 3
              set that.count = 5
         

      Static members
      Code (vJASS):

              struct someType
                  static integer count
              endstruct

              ...
       
              set someType.count = 5
         


      As you can see, instances or pointers of a struct can use members but they can't use static members because they're not the struct itself (E.g., they're only associated with the struct), while the struct-type (Let's call this struct-type) can use static members but not normal members. Hence, the syntax for using members and static members is:

      Code (vJASS):

              set <struct_instance>.<member> = <some_value>
              set <struct_type>.<static_member> = <some_value>
         


      Note

      You can also do this:
      Code (vJASS):

                  set <struct_type>(<instance>).<member> = <some_value>
             



      IV. Structs (Methods)


      Methods are the functions associated with the struct. They also come with two types: static and normal.

      The static method follows the logic of a static member while a normal method follows the logic of a member.

      Syntax:
      Code (vJASS):

              struct SomeType
                  method foop takes nothing returns nothing
                      call BJDebugMsg("The enemy of Foop(" + I2S(this) + ") is Poof(" + I2S(this) + ").")
                  endmethod

                  static method foo takes nothing returns nothing
                      call DoNothing()
                      call BJDebugMsg("You've been bamboozled")
                  endmethod
              endstruct

              ...
         
              local SomeType fool = 5
              call SomeType.foo()
              call fool.foop()
         




      V. Conclusion




      I hope that you have learned something here. If not, just leave a reply here asking why you do not understand or you can send a query via PM. Who knows, I might add you to the credits list.



    • Encapsulation
      (Part II)


      It looks like we meet again, fellow reader. Welcome to Part II of the tutorial. By clicking on this tab, I assume that you've already learned everything about Part I.

      So, let us begin!

      History



      vJASS was and still is the go-to scripting language for the majority of the community members. In essence, vJASS is how some compiler engineers or enthusiastic coders would have programmed the language, and is like the first-born child of JASS2 (figuratively speaking).

      Under all fanciful syntax and such, vJASS is an abstraction for JASS2, which then gets compiled to JASS2 (which shall be referred hereafter as JASS) through JassHelper. How JassHelper compiles vJASS to JASS is another topic altogether and shall be linked here:

      Coverage



      What this part will discuss:
      - Libraries
      - Scopes
      - Scopes within Libraries
      - Scopes within Scopes

      Pre-Discussion Questions



      Q: Wait, all this part is talking about isn't even about structs! Where's our structs?

      A: It will be covered, although structs are going to be talked about a little bit here.

      I. Libraries



      Libraries are containers of packets of code. They're like juice cartons, where the juice is the code and the carton is the enclosure, which is representative of a library.

      Libraries are very useful in keeping the code organized, or at least, the thought-process or the logic of the code. Libraries are what enforces modularity in writing code, but we'll deal with it later. To declare a library:

      Code (vJASS):

          library MyLibrary
              public function foo takes nothing returns nothing
              endfunction
          endlibrary
       


      One must declare the keyword "library" and assign a unique name to the library. Due to vJASS being based on JASS, one must also declare "endlibrary" at the end of the script. This becomes second-nature very quickly.

      If you can notice, a function foo was declared within the library. Now, what's with the prefix "public"?
      Let's see the compiled result:

      Code (vJASS):

      // MyLibrary starts
      function MyLibrary_foo takes nothing returns nothing
      endfunction
      // MyLibrary ends
       


      Looks like foo got prefixed with the name of the library. Neat'o, isn't it?

      From the example, we can see that the function that's prefixed with "public" inherits the name of the library as a prefix. Why is it so?

      Code (vJASS):

      library MyLibrary1
          public function foo takes nothing returns nothing
          endfunction
      endlibrary

      library MyLibrary2
          public function foo takes nothing returns nothing
          endfunction
      endlibrary

      // ...
      call foo() // error
       


      Now, let's look at the compiled result: (Order may vary, explanation later)
      Code (vJASS):

      // MyLibrary1 begins

      function MyLibrary1_foo takes nothing returns nothing
      endfunction

      // MyLibrary1 ends

      // MyLibrary2 begins

      function MyLibrary2_foo takes nothing returns nothing
      endfunction

      // MyLibrary2 ends

      // ...
      call foo() // error
       


      As you can see, libraries look like they are giving the packets of code within them their own space. This gives them a sense of organization. Remember that the functions were "public"? Well, it does look like the functions are given unique names, which means that you can keep using the same name of a function, as long as you add some encapsulation prefixes.

      The most commonly used prefixes for encapsulation are "public, private". These prefixes for encapsulation, these keywords, allow functions with the same name in compile-time to be differentiated from each other, as long as the library names are different.

      A simple explanation for "public" and "private" keywords are as follows; they behave just as their meaning implies.

      Now, the code within libraries is moved to the top, which allow functions below to call the library code. However, the compiler will mix the order of compilation of libraries, which could make for some unintended errors if trying to call functions from other libraries. How can we resolve this?

      Code (vJASS):

      library MyLibrary1
      endlibrary

      library MyLibrary2 uses MyLibrary1  // keywords needs, requires are also usable here.
      endlibrary
       


      So, what's with the keyword "uses"? The library doesn't seem to be doing anything, right?
      The keyword "uses" is instructing the compiler to put the desired library before the library itself.
      In another sense, it tells the compiler that that used library should be placed above it, since it may use its' features or functions.
      To make it more clear, an example of it is as follows:

      Code (vJASS):

      // MyLibrary1 begins
      // MyLibrary1 ends

      // MyLibrary2 begins
      // MyLibrary2 ends
       


      So far, nothing has changed from our previous example except we took out some unnecessary functions. However, the main difference between the former and the latter is that the order is deterministic, which means in here that MyLibrary2 will always be below MyLibrary1 no matter what. This further organizes the script into the way that would seem most logical.​

      II. Scopes



      Scopes are functionally similar to libraries, only that they are moved way below the library section. It would only make sense when it is understood in a sense that scopes are just the local functions relative to the map, while the libraries are global, being shared across maps. In another sense, they're only relevant to the map at hand.

      A good example of such scopes are spells. Spells do not need to be placed in the library section, since nothing from below is presumed to call them. They are, in a sense, reserved to themselves, and nothing should change that by calling them.

      While this is all fine and dandy, an example should best explain what happens.

      Input:

      Code (vJASS):

      library MyLibrary1
      endlibrary

      library MyLibrary2
      endlibrary

      scope MyScope
      endscope
       


      Output:

      Code (vJASS):

      // MyLibrary2 begins
      // MyLibrary2 ends

      // MyLibrary1 begins
      // MyLibrary1 ends

      // MyScope begins
      // MyScope ends
       


      Don't let the scrambling of the order of libraries fool you. Look at MyScope. It is at the bottom of the libraries.

      Let's try compiling it again.

      Output:

      Code (vJASS):

      // MyLibrary1 begins
      // MyLibrary1 ends

      // MyLibrary2 begins
      // MyLibrary2 ends

      // MyScope begins
      // MyScope ends
       


      Huh, looks like we encounter this case again. MyScope can't seem to go up. :(
      It is rather bittersweet to know that scopes can never be above libraries, no matter how many times we compile them.

      There is also another feature of scopes, the ability to enclose individual scopes within it. Basically, you can declare a scope inside a scope, and the same inside a library. But, what would happen if we place a library within a scope?

      Code (vJASS):

      scope MyScope
          library MyScopeLib
          endlibrary
      endscope
      // Won't compile ...
       


      Huh? It would not compile, no matter how many times you will save it. So, declaring a scope within a scope or a library is A-Okay but placing a library inside a scope is a no-no? Yes, it is, because of a simple reason, a scope can never be above libraries, and trying to move that library in the scope up would cause a violation of that rule.

      III. Recap



      To recap, libraries are containers that give code their own space (or something like it). They encapsulate the functions so as to add protection from unintentional reading or access. When using the keywords "uses, requires or needs", the library that requires the other library will be moved below it.

      Scopes are similar to libraries in that they give code their own space as well. In basic form, they are libraries that require all libraries before it, but not needing to declare them as requirements.

      Now, without compiling the code, would the following code compile without a trigger-evaluation (e.g. not compiling in the vanilla trigger editor)?

      Code (vJASS):

      library MyLibrary2 uses MyLibrary1
          public struct Struct
              static method doElse takes nothing returns nothing
              endmethod
          endstruct
      endlibrary

      library MyLibrary1
          function doSomething takes nothing returns nothing
              call MyLibrary2_Struct.doElse()
          endfunction
      endlibrary
       


      Or, say we have this. Would this compile?
      Code (vJASS):

      library MyLibrary1
          private function foo takes nothing returns nothing
              call BJDebugMsg("boop")
          endfunction
      endlibrary

      library MyLibrary2 uses MyLibrary1
          private function bar takes nothing returns nothing
              call MyLibrary1_foo()
          endfunction
      endlibrary
       



    • Cuore for asking questions and clarifications about the struct tutorial...
      IcemanBo for providing links to other tutorials...





    Thanks for taking your time to read this tutorial!
     
    Last edited: Jul 7, 2018
  2. Aniki

    Aniki

    Joined:
    Nov 7, 2014
    Messages:
    564
    Resources:
    6
    Tools:
    1
    Maps:
    1
    Spells:
    1
    JASS:
    3
    Resources:
    6
    Code (vJASS):

    struct Data2
        unit whichUnit
        real whichLife
    endstruct

    function SomeInitFunc takes nothing returns nothing
        local Data2 this = 1
        loop
            exitwhen this > 5
            set this.whichUnit = <some_random_unit>
            set this.whichLife = GetWidgetLife(<some_random_unit>)
            set this = this + 1
        endloop
    endfunction
     


    Would've been nice if structs were a bit more type safe, i.e requiring explicit a type casts (integer(foo), or Bar(foo)) though, because that catches mistakes, I think.
    Code (vJASS):

    struct Foo
        string a
    endstruct

    struct Bar
        integer b
    endstruct

    function main takes nothing returns nothing
        local Foo foo = Foo.create()
        local Bar bar = foo // no error
        // =>
        // local integer foo= s__Foo__allocate()
        // local integer bar= foo

    endfunction
     


    Might be worth mentioning that static members can be accessed from a struct instance and the "struct name" itself:
    Code (vJASS):

    struct Foo
        string a
        static string b

        static method bar takes nothing returns nothing
        endmethod
    endstruct

    function main takes nothing returns nothing
        local Foo foo = Foo.create()

        call foo.bar() // ok
        call Foo.bar() // ok
        call BJDebugMsg(foo.a) // ok
        call BJDebugMsg(foo.b) // ok
        // call BJDebugMsg(Foo.a) // error: a not a static member of Foo
        call BJDebugMsg(Foo.b) // ok
        //
        // =>
        //
        // local integer foo= s__Foo__allocate()
        //
        // call s__Foo_bar() // ok
        // call s__Foo_bar() // ok
        //
        // call BJDebugMsg(s__Foo_a[foo]) // ok
        // call BJDebugMsg(s__Foo_b) // ok
        // // call BJDebugMsg(Foo.a) // // error: a not a static member of Foo
        // call BJDebugMsg(s__Foo_b) // ok
        //
        // its funny how vJass preserves comments =)
        //
    endfunction
     


    Oh, you have it at the bottom but it seems a bit convoluted.

    PS: You could also have an explanation of why one would bother to learn about structs in the first place instead of sticking to GUI, "dynamic indexing", hashtables, etc.
    PPS: structs are more readable, efficient, composable (and type unsafe ;P)
     
  3. Chaosy

    Chaosy

    Tutorial Reviewer

    Joined:
    Jun 9, 2011
    Messages:
    11,063
    Resources:
    18
    Icons:
    1
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    18
    Seems like something you'd need prior programming to understand, it does not feel very beginner friendly to me.
    I personally would prefer the existing struct tutorials.
     
  4. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,544
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    Woah, comments. This is the first tutorial I've written. I'll polish it up though.
     
  5. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    I apologize for the delay. I like this tutorial. In fact, when I was first transitioning to vJASS, I used a similar "light" tutorial and learned from there. Sometimes it is easier to just see how structs are used in a basic sense rather than going into the nitty gritty of trying to figure out how they work internally.

    Keep it up. I definitely would delve into creating struct instances in Part II.
     
  6. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Any plan to update? :) @MyPad
     
  7. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,544
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    I've been busy with my entry in the Techtree Contest. I'll see if I come around.
     
  8. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,537
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Would be good to have. A focus would be definitly useful, for not to cover all points from vjass, but maybe seperating it into packs/different tutorials.
    @BlueSaint made a good but also quite big tutorial, but did not fully finish it because it's just too big to overview and to maintain.

    References can also be Structs for Dummies, vJASS OOP Lesson, Introduction to Struct, to be aware of what kind of learn attempt it will be.

    Personally I would also love to have a basic crash course/introduction to have combined with little tasks, like maybe extending the Jass Class with vjass, to have theory and practice together. Tutorial(s) are for sure useful too, but I'm just sceptical if it covers too much from all features in one.
     
  9. MrModify

    MrModify

    Joined:
    Mar 4, 2020
    Messages:
    15
    Resources:
    0
    Resources:
    0
    Great job thanks
     
  10. Ouroel

    Ouroel

    Joined:
    Jul 12, 2020
    Messages:
    12
    Resources:
    0
    Resources:
    0
    Someone conspicuously forgot that you can only have 8192 instances of a struct. Less if your struct contains 2 of another type of struct.
     
  11. Chaosy

    Chaosy

    Tutorial Reviewer

    Joined:
    Jun 9, 2011
    Messages:
    11,063
    Resources:
    18
    Icons:
    1
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    18
    Wasnt that limit changed in a patch a while back?
    (I seem to remember the array limit being increased)
     
  12. Ouroel

    Ouroel

    Joined:
    Jul 12, 2020
    Messages:
    12
    Resources:
    0
    Resources:
    0
    oh yeah it's 32768 now

    still way too little to store the entire navgrid though for physics collision purposes
     
  13. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,544
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    If arrays won't do, then hashtables probably will. As a sacrifice for speed, you get a lot of space.
     
  14. Ouroel

    Ouroel

    Joined:
    Jul 12, 2020
    Messages:
    12
    Resources:
    0
    Resources:
    0
    wasn't there a 20 hashtable limit?
    srsly just use Lua
    (also structs are array-based)
     
  15. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,544
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    The limit to the number of hashtables is actually around 255-256.
     
  16. Ouroel

    Ouroel

    Joined:
    Jul 12, 2020
    Messages:
    12
    Resources:
    0
    Resources:
    0
    not sure on the speed of lua tables vs hashtables though
     
  17. Chaosy

    Chaosy

    Tutorial Reviewer

    Joined:
    Jun 9, 2011
    Messages:
    11,063
    Resources:
    18
    Icons:
    1
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    18
    @MyPad plans to update this?

    And if you do, I figure there would need to be a motivation of what this does better than other options that already exist that cover the same topic.
     
  18. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,544
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    Probably not. Graveyard away