How to call methods from the result of another interface?

Just starting out? Need help? Post your questions and find answers here.
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: How to call methods from the result of another interface

Post by Mijikai »

mk-soft wrote: P.S.
The call you wish for would have to be implemented by compiler.
r1 = {IsObject *VT-stack1 = }\getA\{IsObject *VT-stack2 = }\getB\{IsObject *VT-stack3 = }\getC() {*VT-stack3\Release()} {*VT-stack2\Release()} {*VT-stack1\Release()}
Its not a wish what i showed works.
Unless this is not aimed at me but the original request.
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: How to call methods from the result of another interface

Post by mk-soft »

You can provide functions in a structure. However, this has nothing to do with interfaces.
For this you have to define the function with ProtoType.
However, it is a problem to pass the pointer to your own data.
But there's one trick I've got to figure out...

Link: https://www.purebasic.fr/german/viewtop ... =8&t=30347
Last edited by mk-soft on Sun Jul 15, 2018 1:06 pm, edited 1 time in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: How to call methods from the result of another interface

Post by Mijikai »

mk-soft wrote:You can provide functions in a structure. However, this has nothing to do with interfaces.
For this you have to define the function with ProtoType.
However, it is a problem to pass the pointer to your own data.
But there's one trick I've got to figure out...
Well this works just fine for me...

Code: Select all

Structure PUBLIC_VTABLE_STRUCT
;...
  *Object.iObject;<- Interface !!!
EndStructure
And you always get the pointer to it, access it and or call any function...

Code: Select all

Procedure.i Something(*VT.PUBLIC_VTABLE_STRUCT);<- function of the public interface
;...
EndProcedure
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: How to call methods from the result of another interface

Post by Mistrel »

I looked at the suggestions provided by both mk-soft and Mijikai. The problem still comes down to functions being unable to return anything other than primitives.

Consider how I want to chain methods:

Code: Select all

obj\getBox()\getWidth()
The problem lies squarely with the return value for getBox(). Because its return value cannot be defined as an interface or as a pointer to a specific structure, then the compiler cannot complete the chain.

Can't do any of these:

Code: Select all

Declare.SomeStruct getBox()
Declare.SomeStruct* getBox()
Declare.SomeInterface getBox()
Only this for pointer results:

Code: Select all

Declare.i getBox()
Therefore the chain cannot be completed:

Code: Select all

getBox()\getWidth() <- how can it call getWidth()?
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: How to call methods from the result of another interface

Post by #NULL »

I guess return values are simply not lvalues

Code: Select all

Procedure.s f(s.s)
  ProcedureReturn s
EndProcedure

Debug PeekS(@f("abc")) ; doesn't peek into a string temporary because it's just the same
Debug PeekS(@f())      ; as the function pointer

; output:
;   ㅈ僀䡐䠨襈⑄䠨䒋䀤襈뀅☒䠀璋⠤赈⑼䠨뇕荈Ⴤ襈䣦쟇
;   ㅈ僀䡐䠨襈⑄䠨䒋䀤襈뀅☒䠀璋⠤赈⑼䠨뇕荈Ⴤ襈䣦쟇
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: How to call methods from the result of another interface

Post by Mijikai »

Mistrel wrote:I looked at the suggestions provided by both mk-soft and Mijikai. The problem still comes down to functions being unable to return anything other than primitives.

Consider how I want to chain methods:

Code: Select all

obj\getBox()\getWidth()
The problem lies squarely with the return value for getBox(). Because its return value cannot be defined as an interface or as a pointer to a specific structure, then the compiler cannot complete the chain.

Can't do any of these:

Code: Select all

Declare.SomeStruct getBox()
Declare.SomeStruct* getBox()
Declare.SomeInterface getBox()
Only this for pointer results:

Code: Select all

Declare.i getBox()
Therefore the chain cannot be completed:

Code: Select all

getBox()\getWidth() <- how can it call getWidth()?

You can assign any return value to the vtable - structure and use it directly or
through any of the interface functions.

Code: Select all

Interface box
  GetWidth.i()
EndInterface

Interface obj
  GetBox.i()
  GetBoxWidth.i()
EndInterface

Structure OBJ_VTABLE
  ;...
  *Box.box;< - interface!!!
EndStructure

Structure OBJ_STRUCT
  StructureUnion
    *Interface.obj
    *VT.OBJ_VTABLE
  EndStructure
EndStructure

Global Object.OBJ_STRUCT

Procedure.i CreateObject()
  ;will create and return the object interface (with custom vtable)
EndProcedure

Procedure.i GetBoxInterface()
  ;will create/get a box interface!
EndProcedure

Procedure.i GetBox(*VT.OBJ_VTABLE);function of the object interface
  *VT\Box = GetBoxInterface();<- get the box interface and assign it to the vtable 
EndProcedure

Procedure.i GetBoxWidth(*VT.OBJ_VTABLE);function of the obj interface
  ProcedureReturn *VT\Box\GetWidth()
EndProcedure

Object\Interface = CreateObject();<- create the object interface
Object\Interface\GetBox();<- get the box interface
Object\VT\Box\GetWidth();<- call GetWidth() directly through the vtable
Object\Interface\GetBoxWidth();<- call GetWidth() through a object interface function
I mean you could even just do this:

Code: Select all

;...
Private *Box.box
*Box = Object\Interface\GetBox():*Box\GetWidth()
;...
If no access through the obj interface is required.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: How to call methods from the result of another interface

Post by Mistrel »

Mijikai, I think you misunderstand. SomeObject is returning a BoxObject which has its own interface. In this example it only has one box. What if it's a collection of boxes?

Here is a more complete example if the current limitations and where the call chain gets stuck:

Code: Select all

;/ -- [[ BoxObject ]]- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

DeclareModule BoxObject
  Interface Public
    getWidth()
    getHeight()
  EndInterface
  
  Structure VTable
    *getWidth
    *getHeight
  EndStructure
  
  Structure Class
    *vTable
    width.i
    height.i
  EndStructure
  
  ;/ Static
  Declare setVTable(*vTable.Class)
  Declare new()
EndDeclareModule

Module BoxObject
  ;/ Static
  Global vTable.VTable
  setVTable(@vTable)
  
  Procedure new()
    Protected *instance.Class
    
    *instance=AllocateStructure(Class)
    *instance\vTable=@vTable
    
    *instance\width=123
    *instance\height=456
    
    ProcedureReturn *instance
  EndProcedure
  
  Procedure getWidth(*this.Class)
    ProcedureReturn *this\width
  EndProcedure
  
  Procedure getHeight(*this.Class)
    ProcedureReturn *this\height
  EndProcedure
  
  Procedure setVTable(*vTable.VTable)
    *vTable\getWidth=@getWidth()
    *vTable\getHeight=@getHeight()
  EndProcedure
EndModule

Interface BoxObject Extends BoxObject::Public
EndInterface

;/ -- [[ SomeObject ]] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

DeclareModule SomeObject
  Interface Public
    getBox()
  EndInterface
  
  Structure VTable
    *getBox.getBox
  EndStructure
  
  Structure Class
    *vTable
    *box.BoxObject
  EndStructure
  
  ;/ Static
  Declare setVTable(*vTable.Class)
  Declare new()
EndDeclareModule

Module SomeObject
  ;/ Static
  Global vTable.VTable
  setVTable(@vTable)
  
  Procedure new()
    Protected *instance.Class
    
    *instance=AllocateStructure(Class)
    *instance\vTable=@vTable
    
    ;/ SomeObject has a BoxObject
    *instance\box=BoxObject::new()
    
    ProcedureReturn *instance
  EndProcedure
  
  Procedure getBox(*this.Class)
    ProcedureReturn *this\box
  EndProcedure
  
  Procedure setVTable(*vTable.VTable)
    *vTable\getBox=@getBox()
  EndProcedure
EndModule

;/ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

Interface SomeObject Extends SomeObject::Public
EndInterface

box.BoxObject=BoxObject::new()

;/ BoxObject has default values
Debug box\getHeight()
Debug box\getWidth()

obj.SomeObject=SomeObject::new()

;/ SomeObject has a BoxObject which has dimensions
Debug obj\getBox()\ ; <- Can't do it
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: How to call methods from the result of another interface

Post by Mistrel »

This is interesting and you would think that it might work but it does not:

Code: Select all

Interface BoxObject
EndInterface

Structure SomeObject
  getBox.BoxObject
EndStructure

*obj.SomeObject=0

; [Error] Garbage at the end of the line.
Debug *obj\getBox() ; <- Can't do it
It seems like a hard limitation that we can't call functions in this way.
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: How to call methods from the result of another interface

Post by Mijikai »

Mistrel wrote:This is interesting and you would think that it might work but it does not:

Code: Select all

Interface BoxObject
EndInterface

Structure SomeObject
  getBox.BoxObject
EndStructure

*obj.SomeObject=0

; [Error] Garbage at the end of the line.
Debug *obj\getBox() ; <- Can't do it
It seems like a hard limitation that we can't call functions in this way.
Heres a working example:

Code: Select all

Interface BoxObject
  GetBox.i()
EndInterface

Structure SomeObject
  *getBox.BoxObject
EndStructure

Global *obj.SomeObject

Procedure.i GetBox(*VT)
  Debug "Something"
EndProcedure

Procedure.i GetInterface()
  Protected *Interface, *Set.Integer
  *Interface = AllocateMemory(SizeOf(Integer) * 2)
  If *Interface
    *Set = *Interface:*Set\i = *Interface + SizeOf(Integer)
    *Set + SizeOf(Integer):*Set\i = @GetBox();<- the inteface will have one function
  EndIf
  ProcedureReturn *Interface
EndProcedure

*obj = AllocateStructure(SomeObject);<- structure needs to be allocated
*obj\getBox = GetInterface();<- assign a interface
*obj\getBox\GetBox();<- call it
A interface always needs a vtable you cant use it without.
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: How to call methods from the result of another interface

Post by mk-soft »

@Mistrel

You're a little lost now.
I've now rebuilt my example the way you need it.

Unfortunately Purebasic cannot do this:

Code: Select all

r1 = *obj1\GetBox()\GetWidth()
What Mijikai means, I called at the bottom of the test.

Example Update 1.6

Code: Select all

;-TOP

; Example 1.6

; ***************************************************************************************

DeclareModule Public
  
  Interface iDefault
    Addref()
    Release()
  EndInterface
  
  Structure sDefault
    *vTable.iDefault
    Refcounter.i
  EndStructure
  
EndDeclareModule

Module Public
  
EndModule


; ***************************************************************************************

DeclareModule BoxObject
  
  Interface iBoxObject Extends Public::iDefault
    GetWidth()
    GetHeiht()
  EndInterface
  
  Declare New()
  
EndDeclareModule

Module BoxObject
  
  Structure sBoxObject Extends Public::sDefault
    dx.i
    dy.i
  EndStructure
  
  Procedure Addref(*this.sBoxObject)
    With *this
      \Refcounter + 1
      Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Addref - Refcounter: " + \Refcounter
      ProcedureReturn \Refcounter
    EndWith
  EndProcedure
  
  Procedure Release(*this.sBoxObject)
    With *this
      If \Refcounter = 0
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Destroy - Refcounter: " + \Refcounter
        FreeStructure(*this)
      Else
        \Refcounter - 1
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Release - Refcounter: " + \Refcounter
      EndIf
      ProcedureReturn \Refcounter
    EndWith
  EndProcedure
  
  Procedure GetWidth(*this.sBoxObject)
    With *this
      ProcedureReturn \dx
    EndWith
  EndProcedure
  
  Procedure GetHeight(*this.sBoxObject)
    With *this
      ProcedureReturn \dy
    EndWith
  EndProcedure
  
  ; -----------------------------------------------------------------
  
  Procedure New()
    Protected *this.sBoxObject
    With *this
      *this = AllocateStructure(sBoxObject)
      If *this
        \vTable = ?vtBoxObject
        \Refcounter = 0
        \dx = Random(400, 100)
        \dy = Random(300, 10)
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " New - Refcounter: " + \Refcounter
      EndIf
      ProcedureReturn *this
    EndWith
  EndProcedure
  
  ; -----------------------------------------------------------------
  
  DataSection
    vtBoxObject:
    Data.i @Addref()
    Data.i @Release()
    Data.i @GetWidth()
    Data.i @GetHeight()
  EndDataSection
  
EndModule

; ***************************************************************************************

DeclareModule CircleObject
  
  Interface iCircleObject Extends Public::iDefault
    GetRadius()
  EndInterface
  
  Declare New()
  
EndDeclareModule

Module CircleObject
  
  Structure sCircleObject Extends Public::sDefault
    r.i
  EndStructure
  
  Procedure Addref(*this.sCircleObject)
    With *this
      \Refcounter + 1
      Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Addref - Refcounter: " + \Refcounter
      ProcedureReturn \Refcounter
    EndWith
  EndProcedure
  
  Procedure Release(*this.sCircleObject)
    With *this
      If \Refcounter = 0
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Destoy - Refcounter: " + \Refcounter
        FreeStructure(*this)
      Else
        \Refcounter - 1
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Release - Refcounter: " + \Refcounter
      EndIf
      ProcedureReturn \Refcounter
    EndWith
  EndProcedure
  
  Procedure GetRadius(*this.sCircleObject)
    With *this
      ProcedureReturn \r
    EndWith
  EndProcedure
  
  ; -----------------------------------------------------------------
  
  Procedure New()
    Protected *this.sCircleObject
    With *this
      *this = AllocateStructure(sCircleObject)
      If *this
        \vTable = ?vtCircleObject
        \Refcounter = 0
        \r = Random(400, 100)
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " New - Refcounter: " + \Refcounter
      EndIf
      ProcedureReturn *this
    EndWith
  EndProcedure
  
  ; -----------------------------------------------------------------
  
  DataSection
    vtCircleObject:
    Data.i @Addref()
    Data.i @Release()
    Data.i @GetRadius()
  EndDataSection
  
EndModule

; ***************************************************************************************


; ***************************************************************************************

DeclareModule MyObject
  
  Interface iMyObject Extends Public::iDefault
    GetBox()
    GetCircle()
  EndInterface
  
  Structure sMyObject Extends Public::sDefault
    *Self.iMyObject
    *Box.BoxObject::iBoxObject
    *Circle.CircleObject::iCircleObject
  EndStructure
  
  Declare New()
  
EndDeclareModule

Module MyObject
  
  Procedure Addref(*this.sMyObject)
    With *this
      \Refcounter + 1
      Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Addref - Refcounter: " + \Refcounter
      ProcedureReturn \Refcounter
    EndWith
  EndProcedure
  
  Procedure Release(*this.sMyObject)
    With *this
      If \Refcounter = 0
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Destoy - Refcounter: " + \Refcounter
        \Box\Release()
        \Circle\Release()
        FreeStructure(*this)
      Else
        \Refcounter - 1
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " Release - Refcounter: " + \Refcounter
      EndIf
      ProcedureReturn \Refcounter
    EndWith
  EndProcedure
  
  Procedure GetBox(*this.sMyObject)
    With *this
      \Box\Addref()
      ProcedureReturn \Box
    EndWith
  EndProcedure
  
  Procedure GetCircle(*this.sMyObject)
    With *this
      \Circle\Addref()
      ProcedureReturn \Circle
    EndWith
  EndProcedure
  
  ; -----------------------------------------------------------------
  
  Procedure New()
    Protected *this.sMyObject
    With *this
      *this = AllocateStructure(sMyObject)
      If *this
        Debug "[" + Hex(*this) + "] " + #PB_Compiler_Module + " New - Refcounter: " + \Refcounter
        \vTable = ?vtMyObject
        \Self = *this
        \Box = BoxObject::New()
        \Circle = CircleObject::New()
      EndIf
      ProcedureReturn *this
    EndWith
  EndProcedure
  
  DataSection
    vtMyObject:
    Data.i @Addref()
    Data.i @Release()
    Data.i @GetBox()
    Data.i @GetCircle()
  EndDataSection
  
EndModule

; ***************************************************************************************

;- Test

Define.MyObject::iMyObject *obj1
Define.BoxObject::iBoxObject *box1
Define.CircleObject::iCircleObject *Circle1

Debug "New MyObject..."
*obj1 = MyObject::New()

Debug "Get Box 1"
*box1 = *obj1\GetBox()
Debug "DX: " + *box1\GetWidth()
Debug "DY: " + *box1\GetHeiht()

Debug "Get Circle 1"
*Circle1 = *obj1\GetCircle()
Debug "R : " + *Circle1\GetRadius()

Debug "Release box 1"
*box1\Release()
*Circle1\Release()

Debug "Release obj 1"
*obj1\Release()

Debug "This is not possible because purebasic cannot resolve the object chain."
;r1 = *obj1\GetBox()\GetWidth()

Debug "Trick... but not good"
Define *obj2.MyObject::sMyObject
*obj2 = MyObject::New()
Debug "DX: " + *obj2\Box\GetWidth()
Debug "R : " + *obj2\Circle\GetRadius()

Debug "Release obj "
*obj2\Self\Release()
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
deathmx
User
User
Posts: 27
Joined: Mon Feb 26, 2018 3:14 am

Re: How to call methods from the result of another interface

Post by deathmx »

Hmm would this style of organization help you? Maybe it's a bit much but if i were wanting to make a work around it maybe something close to this. Note: This was quick coding so i didn't add any memory freeing management.
I am trying to show some of the possible flexibility and alternative methods all in one :D .

Code: Select all

DeclareModule world
  Interface _
    getwidth()
    getheight()
    
  EndInterface
  
  
  Interface Main_Methods
    ShowBoxes()
    newbox(Name$,x,y,width,height)
  EndInterface
  Structure box
    *interface
    x.l
    y.l
    width.l
    height.l
  EndStructure
  
  Structure main
    *interface
    Map *Box._()
  EndStructure
  
  
  Structure NewObject
    *Functions.Main_Methods
    *Vars.main
  EndStructure
  
  
  Declare create()
EndDeclareModule



Module world
  
  Procedure create()
    *new.main = AllocateStructure(main)
    *new\interface = ?Main_Methods
    *object.NewObject = AllocateStructure(NewObject)
    *object\Functions = *new
    *object\Vars = *new
    
     ProcedureReturn *object
    
  EndProcedure
  
  
  Procedure NewBox(*self.main,Name$,x,y,width,height)
    With *self
       AddMapElement(\Box(),name$)
       
      *this.box = AllocateStructure(box)
       *this\x = x
      *this\y = y
      *this\width = width
      *this\height = height
      *this\interface = ?Box_Methods
      \Box() = *this
    EndWith
  EndProcedure
  
  Procedure showboxes(*self.main)
    Debug "name: x,y,width,height"
    ForEach *self\Box()
      *this.box = *self\Box()
      Debug MapKey(*self\Box()) + ":" + Str(*this\x) + "," + Str(*this\y) + "," + Str(*this\width) + "," + Str(*this\height)
   Next
  EndProcedure
 
  
  
  Procedure GetWidth(*self.box)
    ProcedureReturn *self\width
  EndProcedure
  
  Procedure getheight(*self.box)
    ProcedureReturn *self\height
  EndProcedure
  
  DataSection
    Main_Methods:
    Data.i @showboxes(),@newbox()
    Box_Methods:
    Data.i @getwidth(),@getheight()
  EndDataSection
  
EndModule


*Galaxy.world::NewObject = world::create()

*Galaxy\Functions\newbox("Small",0,0,10,10)
*Galaxy\Functions\newbox("Medium",0,0,100,100)
*Galaxy\Functions\newbox("Large",0,0,1000,1000)
*Galaxy\Functions\newbox("Random",Random(1000),Random(1000),Random(1000),Random(1000))
*Galaxy\Functions\ShowBoxes()

Debug "Large Width:" + Str(*Galaxy\Vars\Box("Large")\getwidth())


Debug ""
Debug "Lets get all the heights"
ForEach *Galaxy\Vars\Box()
  Debug MapKey(*Galaxy\Vars\Box()) + "::" + "Height = " + Str(*Galaxy\Vars\Box()\getheight())
Next
Post Reply