Page 1 of 2

How to call methods from the result of another interface?

Posted: Sat Jul 14, 2018 6:39 am
by Mistrel
I'm just not starting to move to object-oriented programming in PureBasic to make my code more consistent and portable between the languages I use. I've spent a lot of time getting familiar with how to make things work but this one is stumping me.

If I have an interface method which returns an instance of another object, how do I declare things such that I can call them in sequence?

For example:

Code: Select all

DeclareModule SomeObject
  Interface BoxObject
    getWidth()
    getHeight()
  EndInterface
  
  Interface Public
    ;/ [Error] A structure can't be used with ProcedureReturn.
    ;getBox.BoxObject()
    getBox()
  EndInterface
  
  Declare new()
EndDeclareModule

Module SomeObject
  Procedure new()
  EndProcedure
EndModule

Interface SomeObject Extends SomeObject::Public
EndInterface

obj.SomeObject=SomeObject::new()

;/ [Error] Garbage at the end of the line.
Debug obj\getBox()\getWidth()
I'm returning a reference to an interface. Assigning a reference to a variable declared as an interface works but not for methods within an interface?

Why can't I do this?

Code: Select all

;/ Allowed
obj.SomeObject=*instance

;/ Not allowed?
Procedure.SomeObject ProcA()
  ProcedureReturn *instance
EndProcedure

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

Posted: Sat Jul 14, 2018 9:37 am
by NicTheQuick
This ist not possible but it exists a feature request to make it possible.

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

Posted: Sat Jul 14, 2018 9:57 am
by Mistrel
If this cannot be done then interfaces in PureBasic is toothless.

I was really looking forward to porting some object-oriented code but now I'll have to rewrite a large portion of it.

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

Posted: Sat Jul 14, 2018 10:24 am
by mk-soft
Purebasic supports the OOP calling convention.
Not the automatic splitting of calling OOP chains.

In your example, the'GetBoxObject' is missing

The correct sequence is therefore

obj.InterfaceSomeObject = NewSomeObject()
obj2.InterfaceBoxObject =obj\GetBoxObject()
r1 = obj2\GetWidth()
r2 = obj2\GetHeight()
obj2\Release()
obj\Release()

My OOP-BaseClass modules are examples of this.

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

Posted: Sat Jul 14, 2018 11:25 am
by Mijikai
How about:

Code: Select all

Structure INTERFACE_STRUCT
  StructureUnion
    *Interface.iPublic
    *VT.PUBLIC_VTABLE_STRUCT;the vtable gets modified to contain iObject -> *Object.iObject!
  EndStructureUnion
EndStructure
That allows you to assing or return a interface:

Code: Select all

Global Public.INTERFACE_STRUCT
;...
Public\Interface = InterfacePublic();make a iPublic interface with custom vtable
Public\VT\Object = *Object;pointer to any iObject interface
Then use the VTable to call it:

Code: Select all

Public\VT\Object\Message()

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

Posted: Sat Jul 14, 2018 12:23 pm
by Mistrel
I don't think a structure union would work if the public interface does not align with the vtable.

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

Posted: Sat Jul 14, 2018 12:32 pm
by Mijikai
Mistrel wrote:I don't think a structure union would work if the public interface does not align with the vtable.
The structure will expand to the size of the modified (extended) vtable.

This is also possible:

Code: Select all

Interface iPublic
  Something.i()
EndInterface

;...

Procedure.i Something(*VT.PUBLIC_VTABLE_STRUCT)
  ProcedureReturn *VT\Object\Message()
EndProcedure
A interface function (Something()) of the public interface accesses the supplied
Object interface through the expanded vtable.

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

Posted: Sun Jul 15, 2018 4:58 am
by Mistrel
I've looked at this more closely. Wouldn't it result in an additional VT field for every object in the call chain? :|

Code: Select all

Object\VT\getA\VT\getB\VT\getC()
Compared to:

Code: Select all

Object\getA()\getB()\getC()
There's also no way to pass arguments in the call chain except to the last method.

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

Posted: Sun Jul 15, 2018 6:35 am
by Mijikai
Mistrel wrote:I've looked at this more closely. Wouldn't it result in an additional VT field for every object in the call chain? :|

Code: Select all

Object\VT\getA\VT\getB\VT\getC()
Compared to:

Code: Select all

Object\getA()\getB()\getC()
There's also no way to pass arguments in the call chain except to the last method.
I dont see why writing out the calls would be a problem.
Wont it get also messy if you write several calls in one line?

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

Posted: Sun Jul 15, 2018 7:53 am
by Mistrel
Say I have a world object of players and want to teleport one to 0,0,0.

Code: Select all

world\getPlayer("Bugsy22")\teleport(0,0,0)
I can't chain these as per your workaround because:

Code: Select all

world\getPlayer("Bugsy22")\ <- this is an interface and I can't access additional members
It has the same problem as my original post. Using the interface/vtable union is only a partial workaround and brings with it a different problem:

Code: Select all

world\getPlayer\ <- this is pointer to a structure and I can't pass arguments

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

Posted: Sun Jul 15, 2018 9:30 am
by Mijikai
I dont rly see the problem...

Code: Select all

Public\Interface\GetPlayer("Bugs");<- will get the interface and pass it to the vtable
Public\VT\Teleport(0,0,0);<- will call the function of the passed interface
Make the structure available in the passed vtable and you can use
a function to mess with it or just use it directly.

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

Posted: Sun Jul 15, 2018 11:32 am
by mk-soft
You have forgotten something essential with the handling of interfaces and objects.
Calling a function in a subobject is not enough.

Wrote about an example of what the process must be like.

Code: Select all

;-TOP

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

DeclareModule MyObject
  
  Interface iBoxObject
    Release()
    GetWidth()
    GetHeiht()
  EndInterface
  
  Interface iMyObject
    Release()
    GetBox()
  EndInterface
  
  Declare New()
  
EndDeclareModule

Module MyObject
  
  Structure sMyObject
    *vTable
    BoxObject.iBoxObject
  EndStructure
  
  Structure sBoxObject
    *vTable
    Refcounter.i
    dx.i
    dy.i
  EndStructure
  
  Procedure Box_Release(*this.sBoxObject)
    With *this
      If \Refcounter = 0
        Debug "[" + Hex(*this) + "] Box destoy object refcounter: " + \Refcounter
        FreeStructure(*this)
      Else
        Debug "[" + Hex(*this) + "] Box release object refcounter: " + \Refcounter
        \Refcounter - 1
      EndIf
      ProcedureReturn \Refcounter
    EndWith
  EndProcedure
  
  Procedure Box_GetWidth(*this.sBoxObject)
    With *this
      ProcedureReturn \dx
    EndWith
  EndProcedure
  
  Procedure Box_GetHeight(*this.sBoxObject)
    With *this
      ProcedureReturn \dy
    EndWith
  EndProcedure
  
  Procedure Box_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) + "] Box new object refcounter: " + \Refcounter
      EndIf
      ProcedureReturn *this
    EndWith
  EndProcedure
  
  Procedure Box_GetObject(*this.sBoxObject)
    With *this
      \Refcounter + 1
      Debug "[" + Hex(*this) + "] Box get object refcounter: " + \Refcounter
      ProcedureReturn *this
    EndWith
  EndProcedure
  
  ; -----------------------------------------------------------------
  
  Procedure Release(*this.sMyObject)
    With *this
      Box_Release(\BoxObject)
      FreeStructure(*this)
    EndWith
  EndProcedure
  
  Procedure GetBox(*this.sMyObject)
    With *this
      ProcedureReturn Box_GetObject(\BoxObject)
    EndWith
  EndProcedure
  
  ; -----------------------------------------------------------------
  
  Procedure New()
    Protected *this.sMyObject
    With *this
      *this = AllocateStructure(sMyObject)
      If *this
        \vTable = ?vtMyObject
        \BoxObject = Box_New()
      EndIf
      ProcedureReturn *this
    EndWith
  EndProcedure
  
  DataSection
    vtBoxObject:
    Data.i @Box_Release()
    Data.i @Box_GetWidth()
    Data.i @Box_GetHeight()
    
    vtMyObject:
    Data.i @Release()
    Data.i @GetBox()
  EndDataSection
  
EndModule

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

;- Test

Define.MyObject::iMyObject *obj1, *obj2
Define.MyObject::iBoxObject *box1, *box2

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

Debug "Get Object 1"
*box1 = *obj1\GetBox()
Debug *box1\GetWidth()
Debug *box1\GetHeiht()


Debug "Get Object 2"
*box2 = *obj2\GetBox()
Debug *box2\GetWidth()
Debug *box2\GetHeiht()

Debug "Relases box 1"
*box1\Release()
Debug "Relases obj 1"
*obj1\Release()
Debug "Relases box 2"
*box2\Release()
Debug "Relases obj 2"
*obj2\Release()

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

Posted: Sun Jul 15, 2018 11:51 am
by Mijikai
mk-soft wrote:You have forgotten something essential with the handling of interfaces and objects.
Calling a function in a subobject is not enough.
...
I cant follow you - the solution i proposed does not change the interface itself
but extends the underlinig vtable by adding stuff but not by changing it.

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

Posted: Sun Jul 15, 2018 12:20 pm
by mk-soft
Unfortunately, there are also rules in the programming.
A deviation in this range definitely leads to errors and is not supported by the compiler.

The call you're trying doesn't go like this...

Maybe someone who speaks better English can discuss this again.


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()}
Purebasic is not OOP, but supports the Object Rules

P.P.S
If you want to see what happens when objects are called up, check out my project 'OOP-BaseClassDispatch'.
This can write log files...

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

Posted: Sun Jul 15, 2018 12:52 pm
by Mijikai
mk-soft wrote:Unfortunately, there are also rules in the programming.
A deviation in this range definitely leads to errors and is not supported by the compiler.

The call you're trying doesn't go like this...
So i cant call a function provided by a interface in a structure?
And a structure should not hold more than just the vtable?

Im sorry i dont get it mby its a english barrier.