ich habe ein Projekt, das schon seit Mitte 2015 auf meiner Festplatte herumgammelt, wieder ausgekramt und endlich die zwei Bugs behoben, die es noch zu lösen galt. Der letzte Bug hat mich jetzt auch seit Mittwoch geplagt bis ich herausgefunden habe wie ich da richtig ran gehen könnte.
Egal, erst mal ein paar Fakten:
Dies hier ist ein weiterer OOP-Wrapper für Purebasic, der nur aus Makros und Modulen besteht und keinerlei Precompiler benötigt. Knapp 600 Zeilen Code bieten die folgenden Möglichkeiten:
- Interfaces mit Vererbung (IClass)
- Klassen mit Vererbung (Class)
- Abstrakte Klassen mit Vererbung (Class, in der nicht alle Methoden definiert sind)
- Statische Felddefinitionen (StaticMembers)
- Felder (Members)
- Standardkonstruktor (DefaultConstructor) oder privater Standardkonstruktor (PrivateDefaultConstructor)
- Benutzerdefinierte Konstruktoren (Constructors)
- Dekonstruktor (Deconstructor)
- Aufrufen anderer Konstruktoren aus einem Konstruktor der selben Klasse (CallConstructor)
- Aufrufen eines Konstruktors der übergeordneten Klasse (CallSuperConstructor)
- Methoden (Method)
- Statische Methoden (Procedure)
- Private Methoden (PrivateMethod)
- Synchronisierte Methoden, die mit einem Objektweiten Mutex gesperrt sind (Synchronized)
- Standardmethoden wie free() und toString(), wobei toString() überschrieben werden kann
- Überschreiben von Methoden
- Aufruf von gleichnamigen Methoden der übergeordneten Klassen (super(BASISKLASSE)\Methode())
- Zugriff auf die Felder übergeordneter Klassen (superm(BASISKLASSE)\feld)
- Direktes Nutzen des Interfaces der Klasse (Define obj.type(KLASSE) = new(KLASSE))
- Direktes Nutzen der Struktur der Klasse (Define obj.typem(KLASSE))
- Zugriff auf Methoden innerhalb von Methoden (self\Methode())
- Zugriff auf Felder innerhalb von Methoden (this\feld)
- Zugriff auf die Methoden untergeordneter Klassen (child\Methode()) (VORSICHT!)
- Überprüfen, ob eine Methode virtuell ist oder nicht (callable(KLASSE, Methode))
- Überprüfen, ob ein Objekt eine Instanz einer Klasse oder eines Interfaces ist (isInstance(obj, KLASSE))
- Threadsicherheit!
Hier zunächst mal das Include "Object.pbi"
Code: Alles auswählen
;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