Don't know if this approach as been used before (I use the interface like a pointer to allocate the data structure on construction and to deallocate and invalidate the object on distruction).
I prefer to avoid the use of a parser for various reason and I tried to do what I can using the macros.
Would be very nice if PB would permit the undefine of a costant BTW.
For example #PB_CONST = "string"
and then CompilerUndef #PB_CONST or something like that.
This would permit to use them with macros like variables of some sort.
At least that would be my use.
Anyway, back on topic.
If you have comments, criticism or whatever, let me know.
I especially dislike the need to write so many times the prototype of a funcion (interface, declare, vtable, actual procedure) but I guess without a preprocessor it's the only way to go. Or not ?
Code: Select all
XIncludeFile "OBJ_MACROS.PBI"
;********************************************************************
;*
;* CLASS DEFINITION
;*
;********************************************************************
CLASS_DATA (C_DOG)
; class members
m_sName.s
m_lAge.l
CLASS_METHODS (C_DOG)
; classe methods
Bark (lTimes.l)
Sleep ()
Talk()
; methods for properties access / setting
GetName.s ()
SetName (sName.s)
GetAge.l ()
SetAge (lAge.l)
CLASS_END (C_DOG)
;********************************************************************
;*
;* VTABLE DEFINITION
;*
;********************************************************************
Declare C_DOG_Bark (THIS(C_DOG), lTimes.l)
Declare C_DOG_Sleep (THIS(C_DOG))
Declare C_DOG_Talk (THIS(C_DOG))
Declare.s C_DOG_GetName (THIS(C_DOG))
Declare C_DOG_SetName (THIS(C_DOG), sName.s)
Declare.l C_DOG_GetAge (THIS(C_DOG))
Declare C_DOG_SetAge (THIS(C_DOG), lAge.l)
VTABLE (C_DOG)
Data.l @C_DOG_Bark ()
Data.l @C_DOG_Sleep ()
Data.l @C_DOG_Talk ()
Data.l @C_DOG_GetName ()
Data.l @C_DOG_SetName ()
Data.l @C_DOG_GetAge ()
Data.l @C_DOG_SetAge ()
VTABLE_END
;********************************************************************
;*
;* CONSTRUCTOR - MUST ALWAYS BE DEFINED EVEN IF EMPTY
;*
;********************************************************************
Procedure C_DOG_C_DOG (THIS(C_DOG))
Debug "C_DOG constructor called"
EndProcedure
;********************************************************************
;*
;* DESTRUCTOR - MUST ALWAYS BE DEFINED EVEN IF EMPTY
;*
;********************************************************************
Procedure C_DOG_C_DOG_ (THIS(C_DOG))
Debug "C_DOG destructor called"
EndProcedure
;********************************************************************
;*
;* CLASS METHODS
;*
;********************************************************************
Procedure C_DOG_Bark (THIS(C_DOG), lTimes.l)
Protected k.l
For k = 1 To lTimes
Debug "WOOF !"
Next
EndProcedure
Procedure C_DOG_Sleep (THIS(C_DOG))
Debug "Ronf, ronf, zzz ..."
EndProcedure
Procedure C_DOG_Talk (THIS(C_DOG))
; this way (encapsulated)
Debug "My name is : " + C_DOG_GetName (*This) + " and I'm "+ Str(C_DOG_GetAge (*This)) + " years old."
; or this way (direct)
; Debug "My name is : " + *This\m_sName + " and I'm "+ Str(*This\m_lAge) + " years old."
EndProcedure
Procedure.s C_DOG_GetName (THIS(C_DOG))
ProcedureReturn *This\m_sName
EndProcedure
Procedure C_DOG_SetName (THIS(C_DOG), sName.s)
*This\m_sName = sName
EndProcedure
Procedure.l C_DOG_GetAge (THIS(C_DOG))
ProcedureReturn *This\m_lAge
EndProcedure
Procedure C_DOG_SetAge (THIS(C_DOG), lAge.l)
*This\m_lAge = lAge
EndProcedure
;********************************************************************
;*
;* USAGE EXAMPLE
;*
;********************************************************************
Global k.l, sName.s, lAge.l
Global Dim oDog.C_DOG(4)
Restore lbl_dogs
For k = 0 To 4
NEW (oDog(k), C_DOG) ; if allocation fail, the object will be #Null
If oDog(k) ; test for object validity
Read sName
Read lAge
oDog(k)\SetName(sName)
oDog(k)\SetAge (lAge)
oDog(k)\Sleep()
oDog(k)\Bark(k)
oDog(k)\Talk()
EndIf
Debug ""
Next
For k = 0 To 4
DELETE (oDog(k), C_DOG) ; deallocate object, calling destructor before
Next
DataSection : lbl_dogs:
Data.s "Oliver" : Data.l 2
Data.s "Gustav" : Data.l 4
Data.s "Minou" : Data.l 1
Data.s "Fuffi" : Data.l 5
Data.s "Poldo" : Data.l 8
An this is the include
Code: Select all
;**********************************************************************
;*
;* MACROS FOR SIMPLE CLASS AND OBJECTS IMPLEMENTATION IN PUREBASIC 4.02
;*
;**********************************************************************
Macro THIS (class_name)
*This.t#class_name
EndMacro
Macro CLASS_DATA (class_name)
Structure t#class_name : Virtual.l
EndMacro
Macro CLASS_METHODS (class_name)
EndStructure
Interface class_name
class_name#()
class_name#_()
EndMacro
Macro CLASS_END (class_name)
EndInterface
Declare class_name#_#class_name (*This.t#class_name)
Declare class_name#_#class_name#_ (*This.t#class_name)
EndMacro
Macro VTABLE (class_name)
Global *internal_t#class_name.t#class_name
DataSection : class_name#_Virtual :
Data.l @class_name#_#class_name#()
Data.l @class_name#_#class_name#_()
EndMacro
Macro VTABLE_END
EndDataSection
EndMacro
Macro NEW (oName, class_name)
*internal_t#class_name = AllocateMemory(SizeOf(t#class_name))
If *internal_t#class_name
*internal_t#class_name\Virtual = ?class_name#_Virtual
oName = *internal_t#class_name
oName\class_name#()
EndIf
EndMacro
Macro DELETE (oName, class_name)
oName\class_name#_()
*internal_t#class_name = oName
FreeMemory(*internal_t#class_name)
oName = #Null
EndMacro