V4 - OOP

Share your advanced PureBasic knowledge/code with the community.
El_Choni
TailBite Expert
TailBite Expert
Posts: 1007
Joined: Fri Apr 25, 2003 6:09 pm
Location: Spain

Post by El_Choni »

Something like this, perhaps?:

Code: Select all

Prototype PROCTYPE(a, b, c)

Structure OBJECT
  a.l
  b.w
  proc.PROCTYPE
EndStructure

Procedure MyObjectProcedure(*this.OBJECT, a, b)
  Debug "*this\a: "+Str(*this\a)
  Debug "TEST a+b ="+Str(a+b)
EndProcedure

MyObject.OBJECT
MyObject\proc = @MyObjectProcedure()
MyObject\a = 16

MyObject\proc(MyObject, 1, 2) 
El_Choni
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

In fact, i see it is possible to forget Interfaces and do the same stuff (but even faster) using Prototypes instead!
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Here is a "Prototyped" version of the first fsw idea.
This way no needs for Datasection neither Interfaces, but Prototypes.
Even i don't know if it is so elegant OOP code, but it is probably faster :)

Code: Select all

;example with Properties - now we have Methods and Properties
;Original (Interface version) with "Labels/address in datasection" by fsw
;Prototyped modified version by Psychophanta.

;define Methods
Prototype PROCTYPE1(a,b)
Prototype PROCTYPE2(a)

;define Properties
Structure MyObject
   VTable.l ;this is needed to store the address of the Methods vtable
   ValueOfThat.l ;start with the properties
   ;add more properties if needed
   ;Methods:
   StoreThis.PROCTYPE1;  <-  StoreThis(par.l)
   GetThat.PROCTYPE2;  <-  GetThat()
   ;add more if needed
EndStructure

;this method/procedure can have a different name than in the interface
;the only thing that is important is the position in the vtable
Procedure This(*Self.MyObject, par.l)
   *Self\ValueOfThat = par
   MessageRequester("MyObject", "This: " + Str(par))
EndProcedure

;this method/procedure can have a different name than in the interface
;the only thing that is important is the position in the vtable
Procedure That(*Self.MyObject)
   MessageRequester("MyObject", "That: " + Str(*Self\ValueOfThat))
EndProcedure

;Create a class list
;this way there can be more than one object of a class
Global NewList MyObject_List.MyObject()

Procedure.l CreateThisClass()
   AddElement(MyObject_List())
   MyObject_List()\StoreThis = @This()
   MyObject_List()\GetThat = @That()
   ProcedureReturn @MyObject_List()
EndProcedure

;- start main:

CreateThisClass()

MyObject_List()\StoreThis(@MyObject_List(),347)
MyObject_List()\GetThat(@MyObject_List())
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post by fsw »

Now I have to give my humble opinion.

Eddy's first example is not oop at all because he never uses the variables inside the structure (Eddy recognizes itself later on that's it's not oop...).

----------------------------------------------

El_Choni's example is good, but is not scalable. With scalable I mean all Procedures need to be set AFTER the OBJECT is initialized.

In real life situation this would result in:

Code: Select all

;- start main
;user creates his object named MyObject
MyObject.OBJECT

;now the user HAS TO SET all procs to get it working
MyObject\proc1 = @MyObjectProcedure1()
MyObject\proc2 = @MyObjectProcedure2()
MyObject\proc3 = @MyObjectProcedure3()
MyObject\proc4 = @MyObjectProcedure4()
MyObject\proc5 = @MyObjectProcedure5()
MyObject\proc6 = @MyObjectProcedure6()
;and even more procs

;the user can set the properties of object
MyObject\a = 16

;the user can call his methods of object
MyObject\proc(MyObject, 1, 2) 
If the setting of all the Procedures/Methods could be done before the user assigns a class to his object (MyObject.OBJECT) this solution would be another good way to do it.

----------------------------------------------

Phantas example is a also good but MAIN actually starts with GLOBAL NEWLIST.
So users code would look like:

Code: Select all

;- start main
;user has to create a list
Global NewList MyObject_List.MyObject()

;user has to set the procedures/ methods
Procedure.l CreateThisClass()
   AddElement(MyObject_List())
   MyObject_List()\StoreThis = @This()
   MyObject_List()\GetThat = @That()
   ProcedureReturn @MyObject_List()
EndProcedure

;user initializes his class
;List name is "hard wired" and can't be changed on the fly
CreateThisClass()

;user is calling his methods
;code seems to be pretty long
MyObject_List()\StoreThis(@MyObject_List(),347)
MyObject_List()\GetThat(@MyObject_List()) 
If Phantas code can be changed to:

Code: Select all

;- start main
;user has to create a list
Global NewList MyObject_List.MyObject()

;user initializes his class
;here all procedures/methods are initialized automatically
CreateThisClass(@MyObject_List())

;user is calling his methods
MyObject_List()\StoreThis(347)
MyObject_List()\GetThat()
this solution would be another good way to do it.

----------------------------------------------

But now look at the main code of my first post:

Code: Select all

;- start main:
;user creates his object named "m" as "MyObject" class
;user initializes his class with "CreateThisClass()"
;all Procedures/Methods are set automatically
m.MyObject = CreateThisClass()

;user uses his methods
m\StoreThis(347)
m\GetThat()

End 
For the user the last code is far more simpler to use.
The Properties can only be accessed through Methods, which is actually not a bad thing.

Maybe the same can be achieved with prototypes...

Hope I didn't upset anybody, it wasn't my intention.

In fact if there is a simpler oop way using prototypes instead of interfaces this would be great.

Please keep the ideas coming... :D
Kale
PureBasic Expert
PureBasic Expert
Posts: 3000
Joined: Fri Apr 25, 2003 6:03 pm
Location: Lincoln, UK
Contact:

Post by Kale »

El_Choni wrote:Something like this, perhaps?:

Code: Select all

Prototype PROCTYPE(a, b, c)

Structure OBJECT
  a.l
  b.w
  proc.PROCTYPE
EndStructure

Procedure MyObjectProcedure(*this.OBJECT, a, b)
  Debug "*this\a: "+Str(*this\a)
  Debug "TEST a+b ="+Str(a+b)
EndProcedure

MyObject.OBJECT
MyObject\proc = @MyObjectProcedure()
MyObject\a = 16

MyObject\proc(MyObject, 1, 2) 
Thats Great!!! :D
--Kale

Image
Kale
PureBasic Expert
PureBasic Expert
Posts: 3000
Joined: Fri Apr 25, 2003 6:03 pm
Location: Lincoln, UK
Contact:

Post by Kale »

Ok, El Choni, WTF have i done wrong here? :x

EDIT: finished code below.
Last edited by Kale on Thu Feb 16, 2006 11:51 pm, edited 1 time in total.
--Kale

Image
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

You didn't pass the object to the method as parameter. When using the prototype approach, this is necessary.
Last edited by netmaestro on Tue Feb 21, 2006 12:44 pm, edited 3 times in total.
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post by fsw »

netmaestro wrote:Kale, your prototype for InitialiseSkull is missing a parameter for the object to be passed.

Code: Select all

Prototype INITIALISESKULL(object, SpriteNumber.l, ScreenWidth.l, ScreenHeight.l) 

and pass the variable of type SKULL_CLASS:

Skulls\InitialiseSkull(skulls, 1, ScreenWidth, ScreenHeight) 

and it should work. (it does here)
This is correct.

@Kale, you set the procs before it's even defined.

this can't work:

Code: Select all

Skulls\EchoValues = @EchoValues()
Skulls\MoveSkull = @MoveSkull()
Skulls\InitialiseSkull = @InitialiseSkull()
Skulls\DisplaySkull = @DisplaySkull()

;Object Instance
Global Skulls.SKULL_CLASS

Skulls\InitialiseSkull(1, ScreenWidth, ScreenHeight)
Skulls\EchoValues()

End 
this does:

Code: Select all

;Object Instance
Global Skulls.SKULL_CLASS

Skulls\EchoValues = @EchoValues()
Skulls\MoveSkull = @MoveSkull()
Skulls\InitialiseSkull = @InitialiseSkull()
Skulls\DisplaySkull = @DisplaySkull()

Skulls\InitialiseSkull(Skulls, 1, ScreenWidth, ScreenHeight)
Skulls\EchoValues()

End 
As you can see with prototypes (as it is now) you have to use your object as 1st parameter:

Code: Select all

Skulls\InitialiseSkull(Skulls, 1, ScreenWidth, ScreenHeight) 
which is actually oop-like but not oop.

With interfaces it would look like:

Code: Select all

Skulls\InitialiseSkull(1, ScreenWidth, ScreenHeight) 
which is oop.
Bonne_den_kule
Addict
Addict
Posts: 841
Joined: Mon Jun 07, 2004 7:10 pm

Post by Bonne_den_kule »

Maybe someone could make a new OOP tutorial for PB 4.00 :P
Truth_Seeker
Enthusiast
Enthusiast
Posts: 145
Joined: Tue Mar 01, 2005 8:41 pm
Location: Near a Computer

Post by Truth_Seeker »

Is there anyway for this to be used to allow us to interface or access libraries made in C++ (that use oop)? Or is this all just fake oop?
Thanks
Truth Seeker
Kale
PureBasic Expert
PureBasic Expert
Posts: 3000
Joined: Fri Apr 25, 2003 6:03 pm
Location: Lincoln, UK
Contact:

Post by Kale »

Thanks NetMaestro and FSW! I must of had too much red wine tonight! :P Anyway heres what i was working on:

Code: Select all

EnableExplicit

;===========================================================================
;-PROCEDURES
;===========================================================================

;Handle an error
Procedure HandleError(Result, Text.s)
	If Result = 0 : MessageRequester("Error", Text, #PB_MessageRequester_Ok) : End : EndIf
EndProcedure

;===========================================================================
;-CONSTANTS
;===========================================================================

#APP_NAME = "Skulls"
#APP_VERSION = "v0.1a"

#NUMBER_OF_SPRITES = 100
#SPRITE_IMAGE = 1
#SPRITE_SIZE = 256

Enumeration
	#WINDOW_ROOT
EndEnumeration

;===========================================================================
;-VARIABLES / FLAGS / VARIABLES / STRUCTURES / ARRAYS
;===========================================================================

HandleError(ExamineDesktops(), "Failed to examine any desktops")
Define ScreenWidth.l : ScreenWidth = DesktopWidth(0)
Define ScreenHeight.l : ScreenHeight = DesktopHeight(0)

Global x.l

;===========================================================================
;-SKULL CLASS
;===========================================================================

Prototype ECHOVALUES(Object.l)
Prototype MOVESKULL(Object.l, ScreenWidth.l, ScreenHeight.l)
Prototype INITIALISESKULL(Object.l, SpriteNumber.l, ScreenWidth.l, ScreenHeight.l)
Prototype DISPLAYSKULL(Object.l)

;Class Definition
Structure SKULL_CLASS
	EchoValues.ECHOVALUES
	MoveSkull.MOVESKULL
	InitialiseSkull.INITIALISESKULL
	DisplaySkull.DISPLAYSKULL
	SpriteNumber.l
	XPos.f
	YPos.f
	XStep.f
	YStep.f
	Alpha.l
EndStructure

;Class Method Definitions
Procedure EchoValues(*this.SKULL_CLASS)
	Debug "Sprite Number: " + Str(*this\SpriteNumber)
	Debug "XPos: " + StrF(*this\XPos)
	Debug "YPos: " + StrF(*this\YPos)
	Debug "XStep: " + StrF(*this\XStep)
	Debug "YStep: " + StrF(*this\YStep)
	Debug "Alpha: " + Str(*this\Alpha)
	Debug ""
EndProcedure

Procedure MoveSkull(*this.SKULL_CLASS, ScreenWidth.l, ScreenHeight.l)
	*this\YPos + *this\YStep
	*this\XPos + *this\XStep
	If *this\YPos > ScreenHeight + #SPRITE_SIZE / 2
		*this\YPos = 0 - #SPRITE_SIZE
		*this\XPos = Random(ScreenWidth) - #SPRITE_SIZE / 2
		*this\Alpha = Random(128)
	EndIf
EndProcedure

Procedure InitialiseSkull(*this.SKULL_CLASS, SpriteNumber.l, ScreenWidth.l, ScreenHeight.l)
	*this\SpriteNumber = SpriteNumber
	*this\XPos = Random(ScreenWidth)
	*this\YPos = Random(ScreenHeight)
	*this\YStep = 1 + Random(5)
	If *this\YStep >= 3
		*this\XStep = 1 + Random(5)
	Else
		*this\XStep = 1 - Random(5)
	EndIf
	*this\Alpha= Random(128)
EndProcedure

Procedure DisplaySkull(*this.SKULL_CLASS)
	DisplaySprite3D(*this\SpriteNumber, *this\XPos, *this\YPos, *this\Alpha)
EndProcedure

;Object Instances
Global Dim Skulls.SKULL_CLASS(#NUMBER_OF_SPRITES)

;Initialise All Objects
For x = 0 To #NUMBER_OF_SPRITES
	Skulls(x)\EchoValues = @EchoValues()
	Skulls(x)\MoveSkull = @MoveSkull()
	Skulls(x)\InitialiseSkull = @InitialiseSkull()
	Skulls(x)\DisplaySkull = @DisplaySkull()
	Skulls(x)\InitialiseSkull(Skulls(x), x, ScreenWidth, ScreenHeight)
	;Skulls(x)\EchoValues(Skulls(x))
Next x

;===========================================================================
;-GEOMETRY
;===========================================================================

HandleError(InitSprite(), "Error Initialising DirectX:"+Chr(10)+"Microsoft DirectX v7.0 or greater must be installed correctly."+Chr(10)+"Offending Command: InitSprite()")
HandleError(InitKeyboard(), "Error Initialising DirectX:"+Chr(10)+"Microsoft DirectX v7.0 or greater must be installed correctly."+Chr(10)+"Offending Command: InitKeyboard()")
HandleError(InitMouse(), "Error Initialising DirectX:"+Chr(10)+"Microsoft DirectX v7.0 or greater must be installed correctly."+Chr(10)+"Offending Command: InitMouse()")
HandleError(InitSprite3D(), "Error Initialising DirectX:"+Chr(10)+"Microsoft DirectX v7.0 or greater must be installed correctly."+Chr(10)+"Offending Command: InitSprite3D()")
Sprite3DQuality(0)

SetRefreshRate(60)
HandleError(OpenScreen(ScreenWidth, ScreenHeight, 32, #APP_NAME + " " + #APP_VERSION), "A " + Str(ScreenWidth) + "x" + Str(ScreenHeight) + " 32bit fullscreen screen could not be opened!"+Chr(10)+"Microsoft DirectX v7.0 Or greater must be installed correctly.")
SetFrameRate(60)

CatchSprite(#SPRITE_IMAGE, ?SpriteImage, #PB_Sprite_Texture)
TransparentSpriteColor(#SPRITE_IMAGE, 0)

For x = 0 To #NUMBER_OF_SPRITES
    CreateSprite3D(x, #SPRITE_IMAGE)
    ZoomSprite3D(x, #SPRITE_SIZE, #SPRITE_SIZE)
Next x

;===========================================================================
;-MAIN LOOP
;===========================================================================

HandleError(Start3D(), "Error Initialising DirectX:"+Chr(10)+"Microsoft DirectX v7.0 or greater must be installed correctly."+Chr(10)+"Offending Command: Start3D()")

Repeat

    ClearScreen(123)
    Sprite3DBlendingMode(5, 7)
    
    For x = 0 To #NUMBER_OF_SPRITES
        Skulls(x)\DisplaySkull(Skulls(x))
        Skulls(x)\MoveSkull(Skulls(x), ScreenWidth, ScreenHeight)
    Next x

    ExamineKeyboard()
    ExamineMouse()

    Delay(1)
    
    FlipBuffers()
    
Until KeyboardPushed(#PB_Key_All) Or MouseDeltaX() <> 0 Or MouseDeltaY() <> 0 Or MouseWheel() <> 0 Or MouseButton(1) <> 0 Or MouseButton(2) <> 0 Or MouseButton(3) <> 0
Stop3D()

End

;===========================================================================
;-BINARY INCLUDES
;===========================================================================

DataSection
    SpriteImage:
        IncludeBinary "Skull.bmp"
EndDataSection

Image:
http://www.garyw.uklinux.net/images/Skull.bmp

I love this approach of simple OOP with using structures and prototypes! I wonder if simple inheritance is possible using the 'Extends' keyword? ;)

My next game project will all be wrote like this! :twisted:
Last edited by Kale on Thu Feb 16, 2006 11:45 pm, edited 1 time in total.
--Kale

Image
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Using interfaces allows to skip the repetition of the first argument, which is a must, as it looks much better IMHO (and is faster).
Kale
PureBasic Expert
PureBasic Expert
Posts: 3000
Joined: Fri Apr 25, 2003 6:03 pm
Location: Lincoln, UK
Contact:

Post by Kale »

Fred wrote:Using interfaces allows to skip the repetition of the first argument, which is a must, as it looks much better IMHO (and is faster).
Aye but surely implementing Virtual Tables can be done more elegantly. I hate the way you have to create vtables using interfaces. And using interfaces, the properties are seperate from methods. leading to an interface declaration and properties structure plus i can't find a way to set property values without an interface method. :?
--Kale

Image
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Yes, the repetition of the argument is not cool.

I think the future of OOP in PureBasic is going to look very much like fsw's first example. The vtable approach is really quite clean and efficient once you try it out.
Last edited by netmaestro on Thu Feb 23, 2006 7:02 am, edited 3 times in total.
BERESHEIT
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Kale wrote:plus i can't find a way to set property values without an interface method. :?
In some object langage like java, it's almost forbidden to access a property without encapsulate it. It makes the things more modular and easier to maintain (for example if you need to add a check later, it's easier to put it in the function, things you can't do when you allow to affect directly a value to a structure element).
Post Reply