similar to mk-soft's great 'Module Base Class' macro-based utility, I have another offering which does a similar job, but in a slightly different way.
mk-soft's utility offers up interface-based 'classes', each of which really need to be embedded within their own Purebasic modules, whilst I required a similar utility which can readily create simple 'classes' both inside and outside of PB modules. Also, my little utility does not store individual class info in a Map() or some such. All done with macros.
My aim was to make creating interface based classes a little more concise and simpler than working directly with Interfaces, class structures and Vtables and the like. Makes for slightly more readable code inmo.
LilClass offers the following :
- Very simple syntax
- Simple inheritance
- Method over-riding
- Class constructor and destructor
- No need to deal with vTables.

Regards.
8th Feb 2020.
Added an OverrideClassMethodForIndividualObject() macro which would only be useful for certain esoteric applications (such as my own!)
This can be used on an individually instantiated object to override the given method without altering the underlying class or other objects of the same class. That is, all other class objects will still use the original class method (unless otherwise over-ridden themselves!)
Once this has been used on an individual object, any changes to the underlying class methods will not be reflected in the given object and so this should only be used after all class methods have been defined.
By way of an example, imagine two objects of the same class. Both will, at the outset at least, expose the same class methods. If we use this new macro on one of the objects to alter one of the class methods, say a \GetWidth() method for example, then the two objects will still expose the same class methods, but one of these objects will use the new \GetWidth() method implementation whilst the other will defer to the original \GetWidth(). All new objects of this same class will continue to use the original \GetWidth().
This is of course in addition to the ability to switch all class methods for an individual object when creating the object (by substituting an alternative vTable pointer).
I admit this is a somewhat 'specialised' facility and most people would not have need for it - only complete nutters like myself and fangbeast!
7th Feb 2020.
Bugs fixed.
Added an alternative way of creating a class object. For example, suppose you have defined a 'Box' class then you can simply use NewBox() to instantiate a class object of this type. This does have a couple of limitations mind (due to limitations with PB's macros) in that it can fail if you try to use it without defining any class methods for example. If you lay your class definitions out logically (see the demos) then the compiler shouldn't complain. If it does then add the class name to EndClassProperties(). That is use EndClassProperties(Box) instead. If this doesn't stop PB complaining then simply use NewClassObject(Box) together with InitClassObject() instead of NewBox().
LilClass.pbi
Code: Select all
CompilerIf Defined(INCLUDE_LilClass, #PB_Constant)=0
#INCLUDE_LilClass=1
;/////////////////////////////////////////////////////////////////////////////////
;***LilClass***
;
;Created by srod - 2020.
;Platforms : ALL.
;/////////////////////////////////////////////////////////////////////////////////
#LILCLASS_NUMMETHODSPERBASECLASS = 2
;/////////////////////////////////////////////////////////////////////////////////
;-BASE CLASS.
Interface LilClass_BaseClass ;Extends iUnknown
InitObject()
DestroyObject()
EndInterface
Structure ClassTemplate_LilClass_BaseClass
*LilClass_vTable
Array _LilClass_vTable.i(0)
LilClass_blnIndividualVtablePtr.i
EndStructure
;Dummy constructor/destructor.
Procedure LilClass_BaseMethod_InitDestroy(*this)
EndProcedure
Global Dim LilClass_VT_LilClass_BaseClass.i(#LILCLASS_NUMMETHODSPERBASECLASS-1)
LilClass_VT_LilClass_BaseClass(0) = @LilClass_BaseMethod_InitDestroy() ;If extending iUnknown then change index to 3.
LilClass_VT_LilClass_BaseClass(1) = @LilClass_BaseMethod_InitDestroy() ;If extending iUnknown then change index to 4.
;/////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////
;-CLASS DEFINITION MACROS.
;The following allows us to create an appropriate NEW() procedure for the underlying class.
Macro _LilClass_Internal_CreateNew(ClassName)
CompilerIf Not(Defined(_LilClass_LABEL_New#ClassName, #PB_Constant))
#_LilClass_LABEL_New#ClassName = 1
Procedure.i New#ClassName(vTable=0)
Protected *this.ClassTemplate_#ClassName, this.LilClass_BaseClass
*this = AllocateStructure(ClassTemplate_#ClassName)
If *this
*this\LilClass_vTable = @LilClass_VT_#ClassName()
If vTable
*this\LilClass_vTable = vTable
EndIf
this = *this
this\InitObject()
EndIf
ProcedureReturn *this
EndProcedure
CompilerEndIf
EndMacro
Macro Class(ClassName, ExtendedClassName=LilClass_BaseClass)
Declare.i New#ClassName(vTable=0)
;Create a global array to hold the virtual table of method pointers.
Global Dim LilClass_VT_#ClassName.i(#LILCLASS_NUMMETHODSPERBASECLASS-1) ;Make the original class methods (those already defined at least) available to the extended class.
Interface ClassName Extends ExtendedClassName
EndMacro
Macro EndClass(ClassName=)
EndInterface
EndMacro
Macro ClassProperties(ClassName, ExtendedClassName=LilClass_BaseClass)
CopyArray(LilClass_VT_#ExtendedClassName(), LilClass_VT_#ClassName())
ReDim LilClass_VT_#ClassName(SizeOf(ClassName)/SizeOf(Integer)-1)
Structure ClassTemplate_#ClassName Extends ClassTemplate_#ExtendedClassName
EndMacro
Macro EndClassProperties(ClassName=LilClass_BaseClass)
EndStructure
;Create an object-creation function.
_LilClass_Internal_CreateNew(ClassName)
EndMacro
;/////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////
;-CLASS METHOD MACROS.
;To use the following, place after a class method's EndProcedure and the procedure must be named as in the following example :
;Procedure ClassMethod_ClassName_MethodName() where 'MethodName' is the name of the method as listed in the class definition.
Macro AsClassMethod(ClassName, MethodName)
: LilClass_VT_#ClassName(OffsetOf(ClassName\MethodName())/SizeOf(INTEGER)) = @ClassMethod_#ClassName#_#MethodName()
;Create an object-creation function.
_LilClass_Internal_CreateNew(ClassName)
EndMacro
;With the following alternative to the above macro, we can name the procedure anything we wish.
;Also, do not place a call to the following macro after EndProcedure. Place it on its own line instead.
Macro AddClassMethod(ClassName, MethodName, address)
LilClass_VT_#ClassName(OffsetOf(ClassName\MethodName())/SizeOf(INTEGER)) = address
;Create an object-creation function.
_LilClass_Internal_CreateNew(ClassName)
EndMacro
;The following copies base class method pointers to an extended class. Use if the extended class template was defined before the base class methods.
;Cannot use CopyArray() as this will resize the destination array.
Macro InheritBaseClassMethods(ClassName, ExtendedClassName)
Define.i LilClass_ivxcxcY
For LilClass_ivxcxcY = 0 To ArraySize(LilClass_VT_#ExtendedClassName())
LilClass_VT_#ClassName(LilClass_ivxcxcY) = LilClass_VT_#ExtendedClassName(LilClass_ivxcxcY)
Next
;Create an object-creation function.
_lilClass_Internal_CreateNew(ClassName)
EndMacro
;The following allows an instantiated object to override the given method without altering the underlying class.
;That is, all other class objects will still use the original class method.
;Once this has been used, any changes to the underlying class methods will not be reflected in the given object and so this should only be used
;after all class methods have been defined.
Macro OverrideClassMethodForIndividualObject(VarName, ClassName, MethodName, address)
Define.ClassTemplate_#ClassName *LilClass_PtrClassTemplate
*LilClass_PtrClassTemplate = VarName
If *LilClass_PtrClassTemplate\LilClass_blnIndividualVtablePtr = 0
ReDim *LilClass_PtrClassTemplate\_LilClass_vTable(SizeOf(ClassName)/SizeOf(Integer)-1)
CopyMemory(*LilClass_PtrClassTemplate\LilClass_vTable, @*LilClass_PtrClassTemplate\_LilClass_vTable(), SizeOf(ClassName))
*LilClass_PtrClassTemplate\LilClass_vTable = @*LilClass_PtrClassTemplate\_LilClass_vTable()
*LilClass_PtrClassTemplate\LilClass_blnIndividualVtablePtr = 1
EndIf
*LilClass_PtrClassTemplate\_LilClass_vTable(OffsetOf(ClassName\MethodName())/SizeOf(INTEGER)) = address
EndMacro
;/////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////
;-CLASS CREATION/INITIALISATION MACROS.
Macro NewClassObject(ClassName)
AllocateStructure(ClassTemplate_#ClassName)
EndMacro
;The following initialises the given class variable by setting the virtual table to map to the default class table.
;It also calls the class constructor.
Macro InitClassObject(VarName, ClassName)
Define.INTEGER *ptr#ClassName
Define.LilClass_BaseClass vvvLilClass_#ClassName
*ptr#ClassName = VarName
*ptr#ClassName\i = @LilClass_VT_#ClassName()
;Call any optional class constructor.
vvvLilClass_#ClassName = VarName
vvvLilClass_#ClassName\InitObject()
EndMacro
;The following initialises the given class variable by setting the virtual table to map to the given address.
;This allows an individual class object to share the class template, but to utilise different class methods.
;It also calls the class constructor.
Macro InitClassObjectEx(VarName, vTableAddress)
Define.INTEGER *ptrVVxv32_
Define.LilClass_BaseClass vvvLilClassVVxv32_
*ptrVVxv32_ = VarName
*ptrVVxv32_\i = vTableAddress
;Call any optional class constructor.
vvvLilClassVVxv32_ = VarName
vvvLilClassVVxv32_\InitObject()
EndMacro
;/////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////
;-CLASS DESTRUCTION MACRO.
Macro DestroyClassObject(VarName)
;Call any optional class destructor.
Define.LilClass_BaseClass vvvLilClassVVxv32
vvvLilClassVVxv32 = VarName
vvvLilClassVVxv32\DestroyObject()
FreeStructure(VarName)
VarName = 0
EndMacro
;/////////////////////////////////////////////////////////////////////////////////
CompilerEndIf