• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Cannot find struct to extend from

Status
Not open for further replies.

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
That's because you prefixed your struct as public.
Your struct name is A_Base.
So either drop public as it's exported anyways or extend from A_Base.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Instead of extending struct I'd rather use delegate or textmacros/module interface.

In jass, extending struct forces create member of parent struct to be non-private, whatmore that struct can not extend array. You end up with generated code, which could be replaced with more gentle, custom allocating/deallocating enhancing your control over script.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
I am using stub methods, i see no other way around them.
But feel free to point a better way if possible while being user friendly.

JASS:
library A

struct Base
 public stub method doSomething takes nothing returns nothing
 endmethod
endstruct

endlibrary

JASS:
library B requires A

struct NotBase extends Base
 method doSomething takes nothing returns nothing
    // something
 endmethod
endstruct

endlibrary

And then something like:
JASS:
local NotBase b = NotBase.create()
call someFunc(b)

...

function someFunc takes Base b returns nothing
    call b.doSomething()
endfunction
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Before, let me know what are you trying to achieve. Is it class polymorphism feature takes from object programming resulting in:

Let define class base as an abstract parent class:

class base
{
// base interface
// in form of virtual methods
};

Now, we have child class, which user is supposed to work with, with enhanced api:

class child : public base
{
// enhances api; wrapper
};

This way, we can create hierarchy of object like: ListView -> ListCtrl -> Control -> Window -> Window_Base
where Window_base stores most important data and is not to be used by default.

However, jass object programming features are kinda limited, whatmore, ppl bash your head when they see "stub/super/interface/function interface" and such. Those generate trash code, and especially interfaces can alter rest of the code within map.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
Base struct defines everything i need for my system to work.
The user extends the base struct, redefines the methods he wants to implement differently and then he passes object of a user defined struct to my system which operates with Base objects.

So it would be like this
JASS:
library System

struct Object
 stub method perform takes nothing returns nothing
 endmethod
 // more stub methods here...
endstruct

struct System

 public static method doActions takes Object o returns nothing
  call o.perform()
 endmethod
 // more methods which call other stub methods when a certain event happens or when explicitly called

endstruct

endlibrary

library SomethingElse requires System

struct A extends Object

 method perform takes nothing returns nothing
  // something user defined
 endmethod

 // can redefine other stub methods below but doesn't have to

endstruct

endlibrary


// somewhere called:
System.doActions(A.create())
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
In that case, you could either combine macros/static ifs and modules to create best solution, or use simpler one, one you could actually be using without even noticing (e. g. if you have been working with AIDS):
JASS:
// Jesus defines DEFAULT struct which is kind of base struct. it's
// methods are called if one does not exist within child struct:

    struct DEFAULT extends array
        method AIDS_onCreate takes nothing returns nothing
        endmethod
        method AIDS_onDestroy takes nothing returns nothing
        endmethod
        
        static method AIDS_filter takes unit u returns boolean
            return true // UnitIndexingFilter(u)
        endmethod
        
        static method AIDS_onInit takes nothing returns nothing
        endmethod
    endstruct
    
    //! textmacro AIDS
        /* base struct delegate, connecting future struct with it's parent */
        private static delegate DEFAULT deleg = 0

        //  some unimportant stuff for you

        private static method AIDS_onEnter takes nothing returns boolean
            if thistype.AIDS_filter(?) then
                call thistype(?).AIDS_onCreate()
            endif
            
            return false
        endmethod
        
        private static method AIDS_onDeallocate takes nothing returns boolean
            call thistype(?).AIDS_onDestroy()
            return false
        endmethod

        private static method onInit takes nothing returns nothing
            call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
            call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
            
            // Because I robbed you of your struct's onInit method.
            call thistype.AIDS_onInit()
        endmethod
    //! endtextmacro


// Now, within demo:
    struct FootmanGuardians extends array
        //! runtextmacro AIDS()
    
        private static method AIDS_filter takes unit u returns boolean
            call BJDebugMsg("checking")
            return true
        endmethod

        private method AIDS_onCreate takes nothing returns nothing
            call BJDebugMsg("indexing: " + GetUnitName(this.unit))
        endmethod

        private method AIDS_onDestroy takes nothing returns nothing
            call BJDebugMsg("deindexing: " + GetUnitName(this.unit))
        endmethod
    endstruct
Since all of defined templates exist, those will be executed and results will be shown in game. However, if one of those wouldn't exist, then compiler moves up via delegate and continues searching for method existence. If found, apropriate method will be called. Compiler moves up through struct's delegates untill method is found, returns an error if neither sturct or it's delegates provide given method.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
Sec, how would i pass FootmanGuardians as DEFAULT in a function in that case?

JASS:
// public static method doActions takes DEFAULT d returns nothing

local FootmanGuardians fg = FootmanGuardians.create()
call System.doActions(fg) // <- DEFAULT type expected

Unless you rely on the fact it will be just converted into an integer?
Which is pulling away from OOP more and more and going towards native jass and just integer manipulations.
 
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
You are implementing this as you would in standard compiled/script language, whereas this is jass ;/

Reason why I've even pulled out this subject comes from generated code via parser.
Let's look at your first example:
JASS:
library A

    struct Base
        public stub method doSomething takes nothing returns nothing
            call BJDebugMsg("called within A")
        endmethod
    endstruct

endlibrary

library B requires A

    struct NotBase extends Base
        method doSomething takes nothing returns nothing
            call BJDebugMsg("called within B")
        endmethod
    endstruct

endlibrary

struct test_me extends array

    static method someFunc takes Base b returns nothing
        call b.doSomething()
    endmethod

    static method onInit takes nothing returns nothing
        local NotBase b = NotBase.create()
        call someFunc(b)
    endmethod

endstruct
Compiles to:
JASS:
//Generated method caller for Base.doSomething
function sc__Base_doSomething takes integer this returns nothing
    set f__arg_this=this
    call TriggerEvaluate(st__Base_doSomething[si__Base_type[this]])
endfunction

//Generated allocator of Base
function s__Base__allocate takes nothing returns integer
 local integer this=si__Base_F
    if (this!=0) then
        set si__Base_F=si__Base_V[this]
    else
        set si__Base_I=si__Base_I+1
        set this=si__Base_I
    endif
    if (this>8190) then
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Unable to allocate id for an object of type: Base")
        return 0
    endif

    set si__Base_type[this]=1
    set si__Base_V[this]=-1
 return this
endfunction

//Generated destructor of Base
function sc__Base_deallocate takes integer this returns nothing
    if this==null then
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Attempt to destroy a null struct of type: Base")
        return
    elseif (si__Base_V[this]!=-1) then
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Double free of type: Base")
        return
    endif
    set f__arg_this=this
    call TriggerEvaluate(st__Base_onDestroy[si__Base_type[this]])
    set si__Base_V[this]=si__Base_F
    set si__Base_F=this
endfunction

//Generated method caller for NotBase.doSomething
function sc__NotBase_doSomething takes integer this returns nothing
            call BJDebugMsg("called within B")
endfunction

//Generated allocator of NotBase
function s__NotBase__allocate takes nothing returns integer
 local integer this=s__Base__allocate()
 local integer kthis
    if(this==0) then
        return 0
    endif
    set si__Base_type[this]=2
    set kthis=this

 return this
endfunction


//library A:

        function s__Base_doSomething takes integer this returns nothing
            call BJDebugMsg("called within A")
        endfunction


//library A ends
//library Alloc:

//library Alloc ends
//library B:

        function s__NotBase_doSomething takes integer this returns nothing
            call BJDebugMsg("called within B")
        endfunction


//library B ends


    function s__test_me_someFunc takes integer b returns nothing
        call sc__Base_doSomething(b)
    endfunction

    function s__test_me_onInit takes nothing returns nothing
        local integer b= s__NotBase__allocate()
        call s__test_me_someFunc(b)
    endfunction
As you can see, instead of single doSomething method per struct, there is an additional method defined as method caller and, what you can quickly realise - this isn't very elegant.

Extending struct from array gives you control over allocate and deallocate which sometimes are not even needed. Additionaly, without usage of stub/super and such, method callers won't be generated.

I won't force you to anything, yet with more handwork and some compromise you can achieve program which performs and looks better.
It's not an accident that modules/macros are commonly used as interface declarations within Jass resources section instead of stuff described above.
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
@Kingz: using stub methods is often a very valid and clean solution. Sadly on the hive it is often considered bad style to use methods which trade performance for cleaner code (stub methods are one of those).
I dont see a reason for this, especially if your code is not performance critical, and i use stub methods myself (for example here).
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
Since when do people who use code resources care about compiled wc3 code? They don't ever look at compiled vJass code.

And i don't think an extra caller method per struct is such a big thing, it won't impact on performance that much, it's not like you are using recursive calls.

I think it's a bit of hypocritical to promote only bits and pieces of vJass.
Anyhow my rant is not directed at you Spinnaker, you are helping and i appreciate it.

But i still think that if you wanted to write nice compiled code you might as well stick to vanilla JASS, butchering perfectly normal OOP style for the sake of background compiled minimal efficency boosts is not something i find rational at all.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
@muzzel: As I've already said I'm not forcing him to do anything, and many times I've also stated that ppl, especialy here, bash your head untill your code is up to the task in the most efficient way.

Whatmore, programing in jass in most efficient way can have bad impact on ppl who want to write scripts in the future as a programist. Inheritence and polymorphism are key features to take advantage of, and such features in jass are represented via extend, stub and such. Unfortunately, thier implementation leaves much to be desired.
 
Status
Not open for further replies.
Top