Code: Select all
;////////////////////////////////////////////////////////////////
;//
;// Title: Object Oriented Programming
;// FileName: OOPBase.pbi
;// Purpose: OOP Base
;// Version: 1.0.0.0
;// Date: 7/13/07
;// Notes: Any segment of code requiring OOP
;//
;////////////////////////////////////////////////////////////////
;- Prototypes
Prototype.l Object_Structor(*This.l, Parameter.l = #Null)
Prototype.l Object_NullPointer(*This.l)
;- Structures
Structure ObjectBase_
;Base Properties
MethodPointer.l ;Vector Table Pointer
;Methods
Constructor.Object_Structor ;On Creation this is called (Shouldn't be called from host unless auto constuct is false)
Destructor.Object_Structor ;On Release this is called (Shouldn't be called by the host, this is called by release to properly destroy the object)
Release.Object_Structor ;Release is what should be called from host
EndStructure
;- Interfaces
Interface ObjectBase
Constructor(Parameter.l) ;On Creation
Destructor(Parameter.l = #Null) ;On Release
Release(Parameter.l = #Null) ;Front End should Call this instead of destructor
EndInterface
;- Macros
Macro SizeOfObject(Object)
SizeOf(Object#_)
EndMacro
Macro OffsetOfObject(Object, Field)
OffsetOf(Object#_\Field)
EndMacro
;- Procedures
Procedure DestroyObject(*This.ObjectBase_, DestructParameter.l = #Null) ;One Call to Free them All
Result = *This\Destructor(*This, DestructParameter) ;should return the pointer to the object for this to free NUll if freed it self
*This\MethodPointer = #Null
*This\Constructor = #Null
*This\Destructor = #Null
*This\Release = #Null
If Result <> #Null
FreeMemory(*This)
EndIf
ProcedureReturn Result
EndProcedure
Procedure CreateObject(Constructor.Object_NullPointer, Destructor.Object_NullPointer, ConstructParameter.l = #Null, AutoConstruct.c = #True) ;Creates a new Object (Returns Object Interface)
If Constructor = #Null Or Destructor = #Null
ProcedureReturn #Null
EndIf
*New.ObjectBase_ = AllocateMemory(SizeOf(ObjectBase_))
*New\MethodPointer = @*New\Constructor
*New\Constructor = Constructor
*New\Destructor = Destructor
*New\Release = @DestroyObject()
If AutoConstruct = #True
Result = *New\Constructor(*New, ConstructParameter)
Else
Result = *New
EndIf
ProcedureReturn Result
EndProcedure
well then here is an example;
lets say you wanted to create a object of a dog
starting out with some simple Properties for the dog such as:
Name
Age
Weight
and some Functions:
Bark
Feed
Play
together they are a object.
now an object is a combination of an interface and a structure (and in fact an interface is very similar to a structure) with some fancy type overloading.
to define a object me must write both it's structured version and it's interface version of the object.
for are dog the structured version of the object looks like this. Note: when I name or refer the structured version of the object I put a underscore after the name and on a interface I do not this is purely my style of coding you can mark them with another method
Code: Select all
Structure Dog_
;Methods
Bark.Object_NullPointer
Feed.Object_NullPointer ;Feed(*This, Amount)
Play.Object_NullPointer ;Play(*This, Time)
;Properties
Name.s
Weight.f
Age.f
EndStructure
you still need to blend the interface and structure together but, before i explain how that happens I'm going to show you how the interface of "dog" should be. now sense an interface is just functions it should be rather simple.
Code: Select all
Interface Dog
Bark()
Feed(Amount.f)
Play(HowLong.f)
EndInterface
what makes interfaces special is that they have a hidden first parameter. "*this" when a function is called from a interface the first parameter is always the pointer to it's self, handy isn't? Now to "blend" the interface version of dog and structured version of dog we must extend both of them with the included OOPBase structure and interface: ObjectBase_ (structure) and ObjectBase (Interface). These contain fields that house the constructor and destructor and the vector table pointer which makes these definitions into objects. now the object definitions now should look like this, comments aside:
Code: Select all
Structure Dog_ Extends ObjectBase_
;ObjectBase_ Resides Here
;{
;Base Properties
;MethodPointer.l ;Vector Table Pointer
;Methods
;Constructor.Object_Structor ;On Creation this is called (Shouldn't be called from host unless auto constuct is false)
;Destructor.Object_Structor ;On Release this is called (Shouldn't be called by the host, this is called by release to properly destroy the object)
;Release.Object_Structor ;Release is what should be called from host
;}
;Methods
Bark.Object_NullPointer
Feed.Dog_FloatPointerFloat ;Feed(*This, Amount)
Play.Dog_FloatPointerFloat ;Play(*This, Time)
;Properties
Name.s
Weight.f
Age.f
EndStructure
;- Interface
Interface Dog Extends ObjectBase
;ObjectBase Resides Here
;{
;Constructor(Parameter.l) ;On Creation
;Destructor(Parameter.l = #Null) ;On Release
;Release(Parameter.l = #Null) ;Front End should Call this instead of destructor
;}
Bark()
Feed(Amount.f)
Play(HowLong.f)
EndInterface
Code: Select all
Methods.l[SizeOf(Dog) / 4]
with are object define now how do we create it?
with it constructors of course
Constructors are relatively easy all you need to do is remember to reallocate the memory, set up your methods, and return the pointer to it's self. to spare you a long explanation of how and why we set are methods up (assuming you know how pointers and prototypes work) are dog constructor will look like this:
Code: Select all
Procedure Dog_Constructor(*This.Dog_, Name.s)
;We must make room for the rest of the object
*This = ReAllocateMemory(*This, SizeOf(Dog_))
;lets set up the Functions
*This\Bark = @Dog_Bark()
*This\Feed = @Dog_Feed()
*This\Play = @Dog_Play()
;Lets Set the starting Properties
*This\Name = Name
*This\Weight = 5
*This\Age = 1
;If Successful we must return the Object Address to the calling procedure (Create Object)
ProcedureReturn *This
EndProcedure
object variable. as a side note object can only be constructed and destructed with one long variable so it's one variable or a structured pointer must be passed this is one of the limits of the this system.
destruction work very similar to construction
Code: Select all
Procedure Dog_Destructor(*This.Dog_, Void = #Null)
;this functions is called from Release it's function is to properly destroy any object specific items
*This\Bark = #Null
*This\Feed = #Null
*This\Play = #Null
;Lets Set the starting Properties
*This\Name = #Null$
*This\Weight = #Null
*This\Age = #Null
;If Successful we must return the Object Address to the calling procedure (Release)
;you can destroy the object by release the memory and return #null but it's ussally safer to let release do it
ProcedureReturn *This
EndProcedure
these are very straight forward, th only special thing is that the first parameter is *this.dog_ (structured version of the object)
Code: Select all
Procedure Dog_Play(*This.Dog_, HowLong.f)
*This\Weight = *This\Weight - (HowLong / 4)
;Example of inter-method call
*This\Bark(*This) ;Must have *This
ProcedureReturn -(HowLong / 4)
EndProcedure
Procedure Dog_Feed(*This.Dog_, Amount.f)
*This\Weight = *This\Weight + (Amount / 4)
ProcedureReturn (Amount / 4)
EndProcedure
Procedure Dog_Bark(*This.Dog_)
MessageRequester(*This\Name, "Woof!")
EndProcedure
so when we want to use the object was call
Code: Select all
*MyDog.Dog = CreateObject(@Dog_Constructor(), @Dog_Destructor(), "Mutt") ;because it's a string we must use the address to pass (PB does this internally but because we are passing through longs we must do this manually)
Code: Select all
Macro CreatePuppy(Name)
CreateObject(@Dog_Constructor(), @Dog_Destructor(), @Name) ;because it's a string we must use the address to pass (PB does this internally but because we are passing through longs we must do thsi manually)
EndMacro
Code: Select all
*MyDog.Dog = CreatePuppy("Mutt")
but you cannot use it's properties unless you make another variable to hold the structured version of the object or create methods to get and set
as a general rule for myself is that I typically use the structured version only with in internal functions to the object and the interface for the external (unless I am defining objects with in object then i defines union with both of them defined in the same location)
now here is that full source of what I was explaining if your more of the Understand-by-source type of person
Code: Select all
;////////////////////////////////////////////////////////////////
;//
;// Title: Dog Object Example
;// FileName: Dog.pbi
;// Purpose: Show how to use OOP
;// Version: 1.0.0.0
;// Date: 7/13/07
;// Notes: Woof
;//
;////////////////////////////////////////////////////////////////
;- Includes
XIncludeFile "OOPBase.pbi"
;- Constants
;for the sake of the example the puppy will always start with these values
#Dog_Begin_Wieght = 5.0
#Dog_Begin_Age = 1.0
;- Prototypes
; my format for prototype names for objects are Object_ReturnsPointerArgumentTypes
Prototype.f Dog_FloatPointerFloat(*This.l, Float.f)
;- Structures
Structure Dog_ Extends ObjectBase_
;ObjectBase_ Resides Here
;{
;Base Properties
;MethodPointer.l ;Vector Table Pointer
;Methods
;Constructor.Object_Structor ;On Creation this is called (Shouldn't be called from host unless auto constuct is false)
;Destructor.Object_Structor ;On Release this is called (Shouldn't be called by the host, this is called by release to properly destroy the object)
;Release.Object_Structor ;Release is what should be called from host
;}
;Methods
Bark.Object_NullPointer
Feed.Dog_FloatPointerFloat ;Feed(*This, Amount)
Play.Dog_FloatPointerFloat ;Play(*This, Time)
;Properties
Name.s
Weight.f
Age.f
EndStructure
;- Interface
Interface Dog Extends ObjectBase
;ObjectBase Resides Here
;{
;Constructor(Parameter.l) ;On Creation
;Destructor(Parameter.l = #Null) ;On Release
;Release(Parameter.l = #Null) ;Front End should Call this instead of destructor
;}
Bark()
Feed(Amount.f)
Play(HowLong.f)
EndInterface
;- Macro
Macro CreatePuppy(Name)
CreateObject(@Dog_Constructor(), @Dog_Destructor(), @Name) ;because it's a string we must use the address to pass (PB does this internally but because we are passing through longs we must do thsi manually)
EndMacro
;- Procedures
Procedure Dog_Play(*This_.Dog_, HowLong.f)
*This\Weight = *This\Weight - (HowLong \ 4)
;Example of inter-method call
*This\Bark(*This) ;Must have *This
ProcedureReturn -(HowLong \ 4)
EndProcedure
Procedure Dog_Feed(*This.Dog_, Amount.f)
*This\Weight = *This\Weight + (Amount \ 4)
ProcedureReturn (Amount \ 4)
EndProcedure
Procedure Dog_Bark(*This.Dog_)
MessageRequester(*This\Name, "Woof!")
EndProcedure
Procedure Dog_Constructor(*This.Dog_, Name.s) ;Called From the CreatePuppy macro and ultimatly the create object procedure
;This Function is called to set the object up with all of it's functions and variables other wise the object is unusable
;We must make room for the rest of the object
*This = ReAllocateMemory(*This, SizeOf(Dog_))
;lets set up the Functions
*This\Bark = @Dog_Bark()
*This\Feed = @Dog_Feed()
*This\Play = @Dog_Play()
;Lets Set the starting Properties
*This\Name = Name
*This\Weight = #Dog_Begin_Wieght
*This\Age = #Dog_Begin_Age
;If Successful we must return the Object Address to the calling procedure (Create Object)
ProcedureReturn *This
EndProcedure
Procedure Dog_Destructor(*This.Dog_, Void = #Null)
;this functions is called from Release it's function is to properly destroy any object specific items
*This\Bark = #Null
*This\Feed = #Null
*This\Play = #Null
;Lets Set the starting Properties
*This\Name = #Null$
*This\Weight = #Null
*This\Age = #Null
;If Successful we must return the Object Address to the calling procedure (Release)
;you can destroy the object by release the memory and return #null but it's ussally safer to let release do it
ProcedureReturn *This
EndProcedure