The scary stuff, written once for all (updated to 1.03)
BaseClass.pb
Code: Select all
; BaseClass.pb
; This is the base class (and the associated support macros) shared by all the classes created by myself.
; So it is included in every class definition I make.
; v1.03 by Luis, PB 5.00, 15 Nov 2012
CompilerIf Defined(BaseClass, #PB_Structure) = #False
; SHARED INFO COMMON TO ALL INSTANCES OF THIS CLASS
Structure SharedClass
NumberOfIstances.i ; the current number of instances
ClassName$ ; the name of the class
EndStructure
; BASE CLASS DEFINITION
Structure BaseClass
*vtable ; virtual table pointer
*shared.SharedClass ; shared data pointer
EndStructure
; BASE OBJECT DEFINITION
Interface BaseObject
GetClassName$() ; get the name of the class
EndInterface
; VIRTUAL TABLE START
Macro VTABLE_START (YOUR_CLASS_NAME)
DataSection
vtable_#YOUR_CLASS_NAME#:
Data.i @YOUR_CLASS_NAME#_GetClassName()
EndMacro
; VIRTUAL TABLE END
Macro VTABLE_END
EndDataSection
EndMacro
; DOUBLE QUOTE
Macro CLASS_DQUOTE
"
EndMacro
Macro CLASS_SUPPORT_CODE (YOUR_CLASS_NAME)
Define *YOUR_CLASS_NAME#_Shared.SharedClass
;// GENERATE DEFAULT CONSTRUCTOR -> [YOUR_CLASS_NAME]_AllocateMemory()
Procedure YOUR_CLASS_NAME#_AllocateMemory()
Shared *YOUR_CLASS_NAME#_Shared.SharedClass
Protected *this.YOUR_CLASS_NAME#Class = AllocateMemory(SizeOf(YOUR_CLASS_NAME#Class))
If *this
; Allocate the shared data, if needed ...
If *YOUR_CLASS_NAME#_Shared = 0 ; this is the first instance
*YOUR_CLASS_NAME#_Shared = AllocateMemory(SizeOf(SharedClass))
EndIf
; set vtable
*this\vtable = ?vtable_#YOUR_CLASS_NAME
; set the class name
*this\shared = *YOUR_CLASS_NAME#_Shared
*this\shared\ClassName$ = CLASS_DQUOTE#YOUR_CLASS_NAME#CLASS_DQUOTE
; instances counter + 1
*this\shared\NumberOfIstances + 1
EndIf
ProcedureReturn *this
EndProcedure
;// GENERATE STATIC PROCEDURE -> [YOUR_CLASS_NAME]_Instances()
Procedure.i YOUR_CLASS_NAME#_Instances()
Shared *YOUR_CLASS_NAME#_Shared.SharedClass
If *YOUR_CLASS_NAME#_Shared
ProcedureReturn *YOUR_CLASS_NAME#_Shared\NumberOfIstances
EndIf
ProcedureReturn 0
EndProcedure
;// GENERATE DEFAULT DESTRUCTOR -> [YOUR_CLASS_NAME]_FreeMemory(*this)
Procedure YOUR_CLASS_NAME#_FreeMemory(*this.YOUR_CLASS_NAME#Class)
Shared *YOUR_CLASS_NAME#_Shared.SharedClass
If *this
; instances counter - 1
*this\shared\NumberOfIstances - 1
; no instances left
If *this\shared\NumberOfIstances = 0
FreeMemory(*YOUR_CLASS_NAME#_Shared) : *YOUR_CLASS_NAME#_Shared = 0
EndIf
ClearStructure(*this, YOUR_CLASS_NAME#Class)
FreeMemory(*this)
EndIf
EndProcedure
;// GENERATE DEFAULT METHOD -> [YOUR_CLASS_NAME]_GetClassName(*this)
Procedure.s YOUR_CLASS_NAME#_GetClassName(*this.YOUR_CLASS_NAME#Class)
ProcedureReturn *this\shared\ClassName$
EndProcedure
EndMacro
CompilerEndIf
The template I fill in when starting a new class, Template.pb
Code: Select all
EnableExplicit
; Include the base class shared by all objects
XIncludeFile "BaseClass.pb"
;********************************************************************
;- CONSTANTS
;********************************************************************
;********************************************************************
;- CLASS STRUCTURE
;********************************************************************
; Always extend it from BaseClass
Structure [YOUR_CLASS_NAME]Class Extends BaseClass
; TODO
EndStructure
;********************************************************************
;- GENERATE SUPPORT CODE
;********************************************************************
CLASS_SUPPORT_CODE ([YOUR_CLASS_NAME])
;********************************************************************
;- OBJECT CREATION
;********************************************************************
Procedure.i New_[YOUR_CLASS_NAME]()
Protected *this.[YOUR_CLASS_NAME]Class
; If the class must be a singleton we have to put the check in here:
; If [YOUR_CLASS_NAME]_Count() = 1
; ProcedureReturn 0 ; only one instance
; EndIf
; Must invoke *at least* [YOUR_CLASS_NAME]_AllocateMemory()
*this = [YOUR_CLASS_NAME]_AllocateMemory()
; And then it must initialize the class instance ...
If *this
; TODO
EndIf
ProcedureReturn *this
EndProcedure
;********************************************************************
;- PUBLIC METHODS
;********************************************************************
Procedure [YOUR_CLASS_NAME]_Destroy (*this.[YOUR_CLASS_NAME]Class)
; The allocated class data, if any, must be freed here
; example: Freememory (*this\ptr)
; At the end, it must invoke *at least* [YOUR_CLASS_NAME]_FreeMemory()
; It will invoke automatically a ClearStructure() followed by a FreeMemory() on the class structure.
[YOUR_CLASS_NAME]_FreeMemory (*this)
EndProcedure
;********************************************************************
;- CLASS INTERFACE
;********************************************************************
; Use [YOUR_CLASS_NAME] for the interface name, and always extend from BaseObject
Interface [YOUR_CLASS_NAME] Extends BaseObject
Destroy()
; TODO
EndInterface
;********************************************************************
;- VIRTUAL TABLE
;********************************************************************
; The list order MUST match the one used for the interface
VTABLE_START ([YOUR_CLASS_NAME])
Data.i @[YOUR_CLASS_NAME]_Destroy()
; TODO
VTABLE_END
When I create a class I load up template.pb, find/replace all
[YOUR_CLASS_NAME] with the name of my new class, for example
Triangle and save the file under the new name
TriangleClass.pb.
Then I start to write the code for it, an example is in the next post.