I've worked on a project that's been bumming around on my hard drive since mid-2015 and finally fixed the two bugs that still needed to be fixed. The last bug has been bothering me since Wednesday until I found out how to get it right.
Anyway, let's get some facts straight:
This is another OOP wrapper for Purebasic, which consists only of macros and modules and does not require any precompilers. Almost 600 lines of code offer the following possibilities:
- Interfaces with inheritance (IClass)
- Classes with inheritance (Class)
- Abstract classes with inheritance (Class, in which not all methods are defined)
- Static fields (StaticMembers)
- Fields (Members)
- Default constructor (DefaultConstructor) or private default constructor (PrivateDefaultConstructor)
- Custom constructors (Constructors and Construct)
- Deconstructor (Deconstructor)
- Calling other constructors within the same class (CallConstructor)
- Calling constructors of super classes (CallSuperConstructor)
- Methods (Method)
- Static methods (Procedure)
- Private methods (PrivateMethod)
- Synchronized methods which are locked with an object wide mutex (Synchronized)
- Default methods like free() and toString() where toString() can be overridden
- Overriding of methods
- Calling methods of the super class, even overidden ones (super(BASEKLASS)\Method())
- Access fields of the super class (superm(BASEKLASS)\feld)
- Using the interface of the class (Define obj.type(KLASS) = new(KLASS))
- Using the fields of the class (Define obj.typem(KLASS))
- Call methods within methods (self\Method())
- Use fields within methods (this\field)
- Call methods of child classes, useful when you want to call the overridden method, not your own (child\Method()) (ATTENTION!)
- Check if a method is virtual or not (callable(KLASS, Method))
- Check if an object is an instance of a given class or interface (isInstance(obj, KLASS))
- Thread safe
This is the main file:
Object.pbi
Code: Select all
;DebugLevel 10
DeclareModule ClassUtil
EnableExplicit
; ======================================= Helfer-Makros =======================================
Macro __MacroColon__
:
EndMacro
Macro __JoinMacroParts__(P1, P2=, P3=, P4=, P5=, P6=, P7=, P8=)
P1#P2#P3#P4#P5#P6#P7#P8
EndMacro
Macro __CreateMacro__(name,macroBody)
__JoinMacroParts__(Macro name, __MacroColon__, macroBody, __MacroColon__, EndMacro)
EndMacro
Macro __DQ__
"
EndMacro
Macro __MakeString__(__text__)
__DQ__#__text__#__DQ__
EndMacro
Macro __ClassId__
(MacroExpandedCount)
EndMacro
;Bug: https://www.purebasic.fr/english/viewtopic.php?f=23&t=63868
;Bug was solved!
;Prototype __default_constructor_prototype__(*super = 0)
; ==================================== Klassen-Hierarchie =====================================
Macro IClass(__ClassName__,BaseClassName=Object)
DeclareModule __ClassName__
EnableExplicit
__CreateMacro__(__class__, __ClassName__) :
__CreateMacro__(__base__, BaseClassName) :
UseModule ClassUtil
#__INTERFACE__ = 1
Define __default_constructor__.i
Declare.i __DefaultConstructor__(*super = 0, callUserConstructor.i = #True)
#__STRUC_SIZE__ = BaseClassName#::#__STRUC_SIZE__
Define *__pvTable__ = BaseClassName#::*__pvTable__
Define __pvTableSize__.i = BaseClassName#::__pvTableSize__
#__CLASS_ID__ = __ClassId__
Declare.i __isInstance__(*__pDefaultConstructor__)
Interface I#__ClassName__ Extends BaseClassName#::I#BaseClassName
EndMacro
Macro EndIClass
EndInterface
Structure __class__
EndStructure
Structure __class__#_Inherited Extends __base__#::__base__#_Inherited; Align #PB_Structure_AlignC
__class__.__class__[0]
EndStructure
EndDeclareModule
Module __class__
__default_constructor__ = 0
Procedure.i __DefaultConstructor__(*super = 0, callUserConstructor.i = #True)
ProcedureReturn __base__#::__DefaultConstructor__(*super, callUserConstructor)
EndProcedure
Procedure.i __isInstance__(*__pDefaultConstructor__)
If @__DefaultConstructor__() = *__pDefaultConstructor__
ProcedureReturn #True
EndIf
CompilerIf __MakeString__(__base__) = "ClassUtil"
ProcedureReturn #False
CompilerElse
ProcedureReturn __base__#::__isInstance__(*__pDefaultConstructor__)
CompilerEndIf
EndProcedure
EndModule
EndMacro
Macro Class(__ClassName__,BaseClassName=Object)
DeclareModule __ClassName__
EnableExplicit
__CreateMacro__(__class__, __ClassName__) :
__CreateMacro__(__base__, BaseClassName) :
UseModule ClassUtil
; Function pointer to the default constructor. 0 if there is no default constructor or if it is private.
Define __default_constructor__.i
Declare.i __DefaultConstructor__(*super = 0, callUserConstructor.i = #True)
Declare.i __isInstance__(*__pDefaultConstructor__)
Interface I#__ClassName__ Extends BaseClassName#::I#BaseClassName
EndMacro
Macro StaticMembers
CompilerIf Defined(__INTERFACE__, #PB_Constant)
CompilerError "StaticMembers not allowed in IClass!"
CompilerEndIf
CompilerIf Not Defined(__MEMBERS__, #PB_Constant)
EndInterface
CompilerElse
EndStructure
#__MEMBERS_END_STRUCTURE__ = 1
CompilerEndIf
#__STATIC_MEMBERS__ = 1
EndMacro
Macro Members
CompilerIf Defined(__INTERFACE__, #PB_Constant)
CompilerError "Members not allowed in IClass!"
CompilerEndIf
CompilerIf Not Defined(__STATIC_MEMBERS__, #PB_Constant)
EndInterface
CompilerEndIf
#__MEMBERS__ = 1
Structure __class__; Align #PB_Structure_AlignC
*vTable
*__pBase__.__base__#::I#__base__
*super.__class__#_Inherited
__self__.I#__class__
EndMacro
Macro Constructors
CompilerIf Defined(__INTERFACE__, #PB_Constant)
CompilerError "Constructors not allowed in IClass!"
CompilerEndIf
CompilerIf Not Defined(__MEMBERS__, #PB_Constant)
Members
CompilerEndIf
CompilerIf Not Defined(__MEMBERS_END_STRUCTURE__, #PB_Constant)
EndStructure
CompilerEndIf
#__CONSTRUCTORS__ = 1
EndMacro
Macro Definition
CompilerIf Defined(__INTERFACE__, #PB_Constant)
CompilerError "Definition not allowed in IClass!"
CompilerEndIf
CompilerIf Not Defined(__CONSTRUCTORS__, #PB_Constant)
Constructors
CompilerEndIf
Structure __class__#_Inherited Extends __base__#::__base__#_Inherited; Align #PB_Structure_AlignC
__class__.__class__
EndStructure
; TODO BUG?
#__STRUC_SIZE__ = SizeOf(__class__);OffsetOf(__class__#_Inherited\__class__) - OffsetOf(__class__#_Inherited\__base__)
;Debug __MakeString__(__class__) + "::#__STRUC_SIZE__ = " + #__STRUC_SIZE__, 9
Define *__pvTable__, __pvTableSize__.i
EndDeclareModule
Interface __class__ Extends __class__#::I#__class__
EndInterface
Module __class__
__pvTableSize__ = SizeOf(I#__class__) / SizeOf(Integer)
Dim __vTable__.i(__pvTableSize__)
__vTable__(0) = @__isInstance__()
*__pvTable__ = @__vTable__(1)
#__DEFINITION__ = 1
Declare.i __UserDefaultConstructor__(*__this__.__class__)
Procedure.i __DefaultConstructor__(*super.__class__#_Inherited = 0, callUserConstructor.i = #True)
Shared __vTable__()
Debug __MakeString__(__class__) + "::DefaultConstructor Begin", 10
If Not *super
CompilerIf __MakeString__(__class__) = "Object"
ProcedureReturn #False
CompilerElse
*super = AllocateStructure(__class__#_Inherited)
If Not *super
ProcedureReturn #False
EndIf
Debug __MakeString__(__class__) + "::DefaultConstructor AllocateStructure", 10
If Not __base__#::__DefaultConstructor__(*super)
Goto __error_free__
EndIf
CompilerEndIf
Else
CompilerIf __MakeString__(__class__) <> "Object"
If Not __base__#::__DefaultConstructor__(*super)
ProcedureReturn #False
EndIf
CompilerEndIf
EndIf
Protected *__this__.__class__ = @*super\__class__
*__this__\vTable = @__vTable__(1)
*__this__\super = *super
*__this__\__self__ = @*super\__class__
CompilerIf __MakeString__(__class__) <> "Object"
*__this__\__pBase__ = @*super\__class__ - __base__#::#__STRUC_SIZE__ ;@*super\__base__
Debug "============== " + __MakeString__(__class__) + " ==============", 9
Debug "@*super\" + __MakeString__(__base__) + " = " + @*super\__base__, 9
Debug "@*super\" + __MakeString__(__class__) + " - " + __MakeString__(__base__) + "::#__STRUC_SIZE__ = " + StrU(@*super\__class__ - __base__#::#__STRUC_SIZE__), 9
Debug __MakeString__(__base__) + "::#__STRUC_SIZE__ = " + __base__#::#__STRUC_SIZE__, 9
CompilerElse
*__this__\__pBase__ = 0
CompilerEndIf
If callUserConstructor
If Not __UserDefaultConstructor__(*__this__)
CompilerIf OffsetOf(__class__#_Inherited\__class__) = 0
FreeStructure(*__this__\super)
Debug __MakeString__(__class__) + "::DefaultConstructor FreeStructure", 10
CompilerElse
*__this__\__pBase__\free()
;super()\free()
CompilerEndIf
ProcedureReturn #False
EndIf
EndIf
Debug __MakeString__(__class__) + "::DefaultConstructor End", 10
ProcedureReturn *__this__
__error_free__:
FreeStructure(*super)
Debug __MakeString__(__class__) + "::DefaultConstructor FreeStructure", 10
ProcedureReturn #False
EndProcedure
Procedure.i __isInstance__(*__pDefaultConstructor__)
If @__DefaultConstructor__() = *__pDefaultConstructor__
ProcedureReturn #True
EndIf
CompilerIf __MakeString__(__base__) = "ClassUtil"
ProcedureReturn #False
CompilerElse
ProcedureReturn __base__#::__isInstance__(*__pDefaultConstructor__)
CompilerEndIf
EndProcedure
EndMacro
Macro EndClass
CompilerIf Defined(__INTERFACE__, #PB_Constant)
CompilerError "EndClass not allowed in IClass!"
CompilerEndIf
CompilerIf Not Defined(__DEFINITION__, #PB_Constant)
Definition
CompilerEndIf
; Wurde kein Destructor erstellt, erstelle selbst einen
CompilerIf Not Defined(__DECONSTRUCTOR__, #PB_Constant)
Deconstructor
EndDeconstructor
CompilerEndIf
; Wurde kein Default Constructor erstellt, erstelle selbst einen, aber mache ihn nicht für new zugänglich.
CompilerIf Not Defined(__DEFAULT_CONSTRUCTOR__, #PB_Constant)
DefaultConstructor
EndDefaultConstructor
__default_constructor__ = 0
CompilerEndIf
CompilerIf Not Defined(toString, #PB_Procedure)
Procedure.s toString(*__this__.__class__)
ProcedureReturn "<" + __MakeString__(__class__) + " @" + Hex(*__this__, #PB_Quad) + ">"
EndProcedure
CompilerEndIf
__vTable__(1) = @free()
Debug __MakeString__(__class__) + "::free() @ " + @free(), 9
__vTable__(2) = @toString()
CompilerIf __MakeString__(__base__) <> "ClassUtil"
Define __i__.i
For __i__ = 0 To __base__#::__pvTableSize__ - 1
If __vTable__(__i__ + 1) = 0
__vTable__(__i__ + 1) = PeekI(__base__#::*__pvTable__ + __i__ * SizeOf(Integer))
EndIf
Next
CompilerEndIf
UndefineMacro __class__
UndefineMacro __base__
EndModule
EndMacro
; ============================== Basis-Interface und -Strukturen ==============================
Interface IClassUtil
EndInterface
Structure ClassUtil
*vTable
*__pBase__
*super
__self__.IClassUtil
EndStructure
Structure ClassUtil_Inherited
ClassUtil.i[0]
EndStructure
; ================================= Standard (De)Constructor ==================================
Macro DefaultConstructor
CompilerIf Defined(__DEFAULT_CONSTRUCTOR__, #PB_Constant)
CompilerError "Default constructor already declared."
CompilerEndIf
#__DEFAULT_CONSTRUCTOR__ = 1
Procedure.i __UserDefaultConstructor__(*__this__.__class__)
Debug __MakeString__(__class__) + "::UserDefaultConstructor Begin", 10
Protected __self__.I#__class__ = *__this__
EndMacro
Macro PrivateDefaultConstructor
#__PRIVATE_DEFAULT_CONSTRUCTOR__ = 1
DefaultConstructor
EndMacro
Macro EndDefaultConstructor
Debug __MakeString__(__class__) + "::UserDefaultConstructor End", 10
ProcedureReturn *__this__
EndProcedure
CompilerIf Not Defined(__DEFAULT_CONSTRUCTOR__, #PB_Constant)
CompilerError "EndDefaultConstructor without DefaultConstructor."
CompilerEndIf
CompilerIf Defined(__PRIVATE_DEFAULT_CONSTRUCTOR__, #PB_Constant)
__default_constructor__ = 0
CompilerElse
__default_constructor__ = @__DefaultConstructor__()
CompilerEndIf
EndMacro
Macro Deconstructor
CompilerIf Defined(__DECONSTRUCTOR__, #PB_Constant)
CompilerError "Deconstructor already declared."
CompilerEndIf
#__DECONSTRUCTOR__ = 1
Procedure.i free(*__this__.__class__)
Debug __MakeString__(__class__) + "::Deconstructor Begin", 10
Protected __self__.I#__class__ = *__this__
Debug __MakeString__(__class__) + "::Deconstructor", 9
EndMacro
Macro EndDeconstructor
CompilerIf OffsetOf(__class__#_Inherited\__class__) = 0
FreeStructure(*__this__\super)
Debug __MakeString__(__class__) + "::DefaultConstructor FreeStructure", 10
CompilerElse
Debug "*__this__ = " + *__this__, 9
Debug "*__this__\__pBase__ = " + *__this__\__pBase__, 9
Debug "*__this__\__pBase__\vTable = " + PeekI(*__this__\__pBase__), 9
Debug "@" + __MakeString__(__base__) + "::free() = " + PeekI(PeekI(*__this__\__pBase__)), 9
*__this__\__pBase__\free()
CompilerEndIf
Debug __MakeString__(__class__) + "::Deconstructor End", 10
EndProcedure
CompilerIf Not Defined(__DECONSTRUCTOR__, #PB_Constant)
CompilerError "EndDeconstructor without Deconstructor."
CompilerEndIf
EndMacro
Macro ConstructorFailure
Debug __MakeString__(__class__) + "::ConstructorFailure", 10
CompilerIf #PB_Compiler_Procedure <> "__UserDefaultConstructor__"
*__this__\__pBase__\free()
CompilerEndIf
ProcedureReturn 0
EndMacro
Macro CallConstructor(__ConstructorName__=,__ParameterList__=())
CompilerIf __MakeString__(__ConstructorName__) = ""
If Not __UserDefaultConstructor__(*__this__) : ConstructorFailure : EndIf
CompilerElse
__ConstructorName__#_this = *__this__
If Not __ConstructorName__#__ParameterList__
__ConstructorName__#_this = 0
ProcedureReturn 0
Else
__ConstructorName__#_this = 0
EndIf
CompilerEndIf
EndMacro
Macro CallSuperConstructor(__subclass__, __ConstructorName__,__ParameterList__=())
CompilerIf __MakeString__(__ConstructorName__) = ""
CompilerError "Can not call DefaultConstructor of class '" + _MakeString__(__subclass__) + "', because it was already called."
CompilerElse
__subclass__#::__ConstructorName__#_this = @*__this__\super\__subclass__
If Not __subclass__#::__ConstructorName__#__ParameterList__
__subclass__#::__ConstructorName__#_this = 0
ProcedureReturn 0
Else
__subclass__#::__ConstructorName__#_this = 0
EndIf
CompilerEndIf
EndMacro
Macro Construct(__ConstructorName__,__ParamaterList__)
CompilerIf Not Defined(__CONSTRUCTORS__, #PB_Constant)
CompilerError "Construct without ClassConstructors"
CompilerEndIf
Threaded __ConstructorName__#_this.i
Declare.i __ConstructorName__#__ParamaterList__
EndMacro
Macro Constructor(__ConstructorName__,__ParamaterList__=())
CompilerIf Not Defined(__CONSTRUCTORS__, #PB_Constant)
CompilerError "Constructor without ClassConstructors"
CompilerEndIf
#__CONSTRUCTOR_#MacroExpandedCount#__ = 1
Procedure.i __ConstructorName__#__ParamaterList__
Debug __MakeString__(__class__) + "::Constructor " + #PB_Compiler_Procedure + " Begin", 10
Protected *__this__.__class__
If __ConstructorName__#_this
*__this__ = __ConstructorName__#_this
Else
*__this__ = __DefaultConstructor__(0, #False)
EndIf
Protected __self__.I#__class__ = *__this__
EndMacro
Macro EndConstructor
CompilerIf Not Defined(__CONSTRUCTOR_#MacroExpandedCount#__, #PB_Constant)
CompilerError "EndConstructor without Constructor"
CompilerEndIf
Debug __MakeString__(__class__) + "::Constructor " + #PB_Compiler_Procedure + " End", 10
ProcedureReturn *__this__
EndProcedure
EndMacro
; ====================================== Zugriffs-Helfer ======================================
Declare.i __firstIsInstance__(*__this__.ClassUtil, *__pDefaultConstructor__)
; Gibt das Interface zum Klassennamen zurück
Macro type(__ClassName__) :
__ClassName__#::I#__ClassName__
EndMacro
; Gibt die Struktur zum Klassennamen zurück
Macro typem(__ClassName__)
__ClassName__#::__ClassName__
EndMacro
; Ruft den Standard Konstruktur einer Klasse auf
Macro new(__ClassName__)
CallFunctionFast(__ClassName__#::__default_constructor__, 0, #True)
EndMacro
; Gibt Zugriff auf die Methoden einer bestimmten Unterklasse oder der eigenen Klasse.
Macro super(BaseClass=__base__,__this__=this)
__this__\super\BaseClass\__self__
EndMacro
; Gibt Zugriff auf die Strukturfelder einer bestimmten Unterklasse oder der eigenen Klasse.
Macro superm(BaseClass=__base__,__this__=this)
__this__\super\BaseClass
EndMacro
; Ruft die Methoden der aktuellen Instanz auf. Das müssen nicht die eigenen sein, sondern können auch überschriebene sein.
Macro self
__self__
EndMacro
; Gibt Zugriff auf die Strukturfelder der eigenen Klasse.
Macro this
*__this__.__class__
EndMacro
; Ruft die Methoden der direkten Unterklasse auf oder provoziert einen Ungültigen Speicherzugriff, wenn keine existiert und instantiiert wurde.
Macro child
__child__
EndMacro
; Gibt den Pointer zu einer bestimmten Methode einer Klasse zurück. Ist dieser 0, ist die Methode virtuell.
Macro callable(__ClassName__,__MethodName__)
Bool((OffsetOf(__ClassName__#::I#__ClassName__\__MethodName__()) / SizeOf(Integer) < __ClassName__#::__pvTableSize__) And PeekI(__ClassName__#::*__pvTable__ + OffsetOf(__ClassName__#::I#__ClassName__\__MethodName__())))
EndMacro
; Gibt #True zurück, wenn das übergebene Objekt einer Instanz der angegebenen Klasse oder einer Kindklasse ist.
Macro isInstance(__Object__,__ClassName__)
(ClassUtil::__firstIsInstance__(__Object__, __ClassName__#::@__DefaultConstructor__()))
EndMacro
;???
Macro restrictedCast(__varName__, __object__, __ClassName__)
*__varName__.__ClassName__#::__ClassName__ = __object__
EndMacro
; ========================================= Methoden ==========================================
Macro Method(__P1__,__P2__,__P3__=)
CompilerIf __MakeString__(__P3__) = ""
Declare __P1__#__P2__
__vTable__(OffsetOf(I#__class__\__P1__()) / SizeOf(Integer) + 1) = @__P1__()
Procedure __P1__#__P2__
Protected __result__.i = 0
Debug __MakeString__(__class__) + "::" + __MakeString__(__P1__#__P2__) + " Begin", 10
CompilerElse
Declare.__P1__ __P2__#__P3__
__vTable__(OffsetOf(I#__class__\__P2__()) / SizeOf(Integer) + 1) = @__P2__()
Procedure.__P1__ __P2__#__P3__
Protected __result__.__P1__
Debug __MakeString__(__class__) + "::" + __MakeString__(__P2__#__P3__) + " Begin", 10
CompilerEndIf
CompilerIf Not Defined(*__this__, #PB_Variable)
CompilerError "Parameter 'this' is missing in Method's parameter list."
CompilerEndIf
Protected __self__.I#__class__ = *__this__
*__this__ = *__this__\super\__class__\__self__
Protected __child__.I#__class__ = *__this__ + SizeOf(__class__)
EndMacro
Macro PrivateMethod(__P1__,__P2__,__P3__=)
CompilerIf __MakeString__(__P3__) = ""
Procedure __P1__#__P2__
;Protected __result__.i = 0
Debug __MakeString__(__class__) + "::" + __MakeString__(__P1__#__P2__) + " Begin", 10
CompilerElse
Procedure.__P1__ __P2__#__P3__
;Protected __result__.__P1__
Debug __MakeString__(__class__) + "::" + __MakeString__(__P2__#__P3__) + " Begin", 10
CompilerEndIf
CompilerIf Not Defined(*__this__, #PB_Variable)
CompilerError "Parameter 'this' is missing in Method's parameter list."
CompilerEndIf
Protected __self__.I#__class__ = *__this__
*__this__ = *__this__\super\__class__\__self__
Protected __child__.I#__class__ = *__this__ + SizeOf(__class__)
EndMacro
Macro Synchronized(__P1__,__P2__,__P3__=)
Method(__P1__,__P2__,__P3__)
LockMutex(*__this__\super\Object\__objectLock)
EndMacro
Macro EndMethod
Debug "EndMethod", 10
;__method_end__:
;ProcedureReturn __result__
EndProcedure
EndMacro
Macro EndSynchronized
;__method_end__:
UnlockMutex(*__this__\super\Object\__objectLock)
Debug "EndSynchronized", 10
;ProcedureReturn __result__
EndProcedure
EndMacro
Macro MethodReturn(__RETURN__)
;__result__ = __RETURN__
;Goto __method_end__
Debug "MethodReturn", 10
ProcedureReturn __RETURN__
EndMacro
Macro SynchronizedReturn(__RETURN__)
;__result__ = __RETURN__
;Goto __method_end__
UnlockMutex(*__this__\super\Object\__objectLock)
Debug "SynchronizedReturn", 10
ProcedureReturn __RETURN__
EndMacro
EndDeclareModule
Module ClassUtil
Procedure.i __firstIsInstance__(*__this__.ClassUtil, *__pDefaultConstructor__)
If Not *__this__
ProcedureReturn #False
EndIf
ProcedureReturn CallFunctionFast(PeekI(*__this__\vTable - SizeOf(Integer)), *__pDefaultConstructor__)
EndProcedure
EndModule
UseModule ClassUtil
Class(Object, ClassUtil)
free.i()
toString.s()
Members
__objectLock.i
Definition
PrivateDefaultConstructor
this\__objectLock = CreateMutex()
EndDefaultConstructor
Deconstructor
FreeMutex(this\__objectLock)
EndDeconstructor
EndClass
Mostly translated with http://www.DeepL.com/Translator