I want a possible to:
- simple way to declare of class (including vTables)
- Inheritance (including overwrite of methods from parents)
- Pointer to objects
- Support of modules (private and public objects and classes)
- Constructor
- Destructors
- Copy-Constructors
- Memory-handling by PureBaisc and not with AllocateMemory()
- Debugging-support
Here is a simple example:
Code: Select all
Class(cName)
ParentClass(cParent) ;optional
DeclareMethods
<list oft he methods, like in Interface-EndInterface >
Properties
<attributes oft he class/object, like in Structure-EndStructure >
Methods
<definitions of the methods >
EndClass
Properties are always private! There is no easy way to access them in Purebasic outside the class. Also there is no way to define static properties, use global variables instead.
The definitions of the methods are like the definition of procedures, for example:
Code: Select all
Method(i,Set,Var.x)
*self\Value=Var
self\CheckValue()
MethodeReturn *self\Value
EndMethod
Unfortunately a definition like "Method.i name(" is not possible. The parameter list can be 10 parmaters long (can be extended in the Method-Macro of my code).
The methods must match to the declartion, it is for my code has no possibly to check this.
self
With *self\ a method can access the properties (read and write), with self\ it is possible to call other methods oft he class.
Important: The definition of the class contains program code. That’s why you can’t define classes in procedures.
Special methods Initalize, Dispose and Clone
Initalize()
Also known as constructor. This method is called, when a new object of this class is created. When the class has a parent class, both Initalize() are called. First the parent Inialize(), then the child Inialize().
Dispose()
Also known as destuctor. This method is called, when an object will be destroyed. Like Initalize() all Dispose() of a class were called, but in different order. First the child Dispose() then the parent Dispose().
Clone()
When a clone of an object is created, it is a 1:1 copy of the original object. When the class allocate memory with AllocateMemory() the clone and the original object reference to the same memory. When now the original free the memory the clone doesn’t get any information about this! When now the clone tries to access the memory, the program crash!
The Clone-method exists to solve this problem. The Clone-Method has no parameter!
My code has already copied the complete object (including arrays, lists and maps). You must only handle the pointer, handles and so on by your own.
For example (in the properties are *buffer and bufferlen attributes)
Code: Select all
Method(i,Clone)
define *save=*self\buffer
if *self\buffer
*self\buffer=AllocateMemory(*self\bufferlen)
if *self\buffer=0
MethodReturn #false
endif
CopyMemory(*save,*self\buffer,*self\bufferlen)
endif
MethodReturn #true
EndMethod
IMPORTANT! When you return #False the Dispose()-Method is called next. You should set all critical properties to harmless values, which will not destroy the original object.
Example – like above, only with two memory buffers. When the first AllocateMemory() failed, you must set the pointer to the second memory to zero, because it reference to the original object:
Code: Select all
Method(i,Clone)
define *save=*self\buffer
if *self\buffer
*self\buffer=AllocateMemory(*self\bufferlen)
if *self\buffer=0
*self\buffer2=0 ; *buffer2 references to the original, set to 0
MethodReturn #false
endif
CopyMemory(*save,*self\buffer,*self\bufferlen)
endif
if *self\buffer2
*save=*self\buffer2
*self\buffer2=AllocateMemory(*self\bufferlen2)
if *self\buffer2=0
MethodReturn #false
endif
CopyMemory(*save,*self\buffer2,*self\bufferlen2)
endif
MethodReturn #true
EndMethod
PureBasic hates Dispose
It is easy to detect, when an object is created. But it is impossible to detect, when an object/variable is destroyed.
Local objects exist until “End” or in procedures until “EndProcedure” or “ProcedureReturn”.
Because of this you must replace this commands with _End, _EndProcedure and _ProcedureReturn. When you miss an _EndProcedure my macros detect this and stop the compiler. A missing _End can be easy detect by the missing “[INFO] _end” on the debug-window. A missing _ProcedureReturn is not so easy to detect.
When the Debugger is running, every local created object increase a counter and when the object is destroyed, the counter is decreased. _End check this counter and when it is not 0, there must be a missing _ProcedureReturn and a message is printed to the debugger window ("[WARNING] not disposed objects: ").
How bad is a missing _ProcedureReturn?
Local objects are created in local variables. PureBasic handle this and free the used memory by the objects. But all handles, allocated memory and so on will not be freed, because this should handle the Dispose() method.
_End
_End will not work in procedures. It must be placed in the main code.
_FakeEnd
Same as end, this command Dispose() every local and global object, but the program will not be terminated. Usefull for debugging.
Activate complete object monitoring
When you define a constant before the Include of the Module_oop.pbi, a complete monitoring of all objects will activate.
Code: Select all
#__class_debug_all_objects=#True
XIncludeFile("Module_oop.pbi")
This monitoring will slow down the creation and destruction of every object. Also it needs more memory. The monitoring-routines are only active when the debugger is on.
Create an object
Define_Object(<object name>,<class name>)
Protected_Object(<object name>,<class name>)
Global_Object(<object name>,<class name>)
Static_Object(<object name>,<class name>)
When you call one of this macros, an object is defined and initialized. Important, these macros contain program code. When you use it like here:
Code: Select all
If #False
Define_Object(MyObj,cMyClass)
EndIf
Pointer
Code: Select all
*<pointer name>.<class name>
Code: Select all
Define_Object(obj1,cMyClass)
Define_Object(obj2,cMyClass)
Define *pObj.cMyClass
obj1=obj2 ;Bad
*pObj=@obj1 ; Won't work
*pObj=obj1 ; Work
With *Obj=@obj1 will not work, because obj1 is for PureBasic already a pointer. You create here a pointer of a pointer.
Allocate_Object(<class name>)
Free_Object(<object>)
With these functions you allocate memory for a new object. You must release the object by your own with Free_Object().
Important: _End will not Dispose() allocated objects! It will only warn you, when you forget to Disposed allocated objects ("[WARNING] not disposed allocated objects: ").
Inheritance and method overwrite
For example:
Code: Select all
Class(cVar)
DeclareMethods
Set(x)
Get()
Properties
value.i
Methods
Method(i,Set,x.i)
*self\value=x
EndMethod
Method(i,Get)
MethodReturn *self\value
EndMethod
EndClass
Class(cVar2)
ParentClass(cVar)
DeclareMethods
OldSet(x)
Properties
Methods
AliasMethod(Set,OldSet)
Method(i,Set,x)
*self\value=x*2
EndMethod
EndClass
Define_Object(obj1,cVar2)
Define *obj2.cVar
obj1\Set(20)
Debug obj1\Get(); Return 40
obj1\OldSet(20)
Debug obj1\Get(); Return 20
*obj2=obj1
*obj2\Set(30)
Debug obj2\Get(30); Return 60!
With AliasMethod you can save a method of the parent class before you overwrite it with a new one. The alias must be declared in the DeclareMethods section.
It is important, that the method type and the parameters aren’t changed. It is not possible to check this by my routines, because PureBasice doesn’t support any type check.
Maybe you are surprised that the pointer *obj2 (type cVar) called the Set method of cVar2. The object decides which version is called and the object is a cVar2 class.
You can set a pointer of a parent class (here cVar) to an object of the child class (cVar2) without any problems, because all methods and properties of the parent are also in the child.
A save way to set an object
PureBasic allows setting a pointer of a class to an object of a complete different class. When you now use the pointer, random methods with random parameters are called and the result will be funny. To find such kind of errors are very difficult.
I create a function to check the class of an object, here is an example.
Code: Select all
*obj2=Object_CheckClass(obj1,cVar)
When in the above example obj1 is from a diffrent class, the pointer *obj2 will be filled with a „null object“. When now the program tries to use this object, the program will crash “clean”. This is much easier to debug than random events.
There is a special function designed for the procedures, which needed pointers to objects in parameters.
Object_ForceClass(<object>,<class name>)
When the check fails, automatically the debugger is called and the program is terminated.
Special class oop::object
oop::object is a root-class. A pointer to this class can contain any object of any class. This class contains some methods that may be useful:
Method GetClassName()
Return the name of the class of the object. Important, not the class of the pointer!
Method Size()
Return the base-size of an object.
Method CloneFrom(<source-object>)
Destroy the object and create a copy oft he source object. The objects must belong to the exact same class.
Example:
Code: Select all
Protected_Object(obj1,cVar)
Protected_Object(obj2,cVar)
obj2\Set(30)
obj1\CloneFrom(obj2)
Method AllocateClone()
Create a clone of the object and return the new allocated object. This object must be destroyed with Free_Object() manually. When cloning fails, it return 0 otherwise the object.
Method Reset()
Dispose() and re-Initalize() the object.
Module
When you want to use classes, you must enable it with this line in DeclareModul:
Code: Select all
UseModule EnableClass
Simple use for example Define_Object() in the DeclareModul-EndDeclareModule section to create a public object or in the Module-EndModule-Section for privat objects.
Private class
Definite the class in Module-EndModule-section for a private class.
Public class
You must declare the class between DeclareModule-EndDeclareModule and define it in the Module-EndModule.
Here is an example, how to do this:
Code: Select all
DeclareModule TestModul1
UseModule EnableClass
DeclareClass(cTM1)
DeclareMethods
Get()
Set(v.i)
Properties
value.i
EndDeclareClass
EndDeclareModule
Module TestModul1
DefineClass(cTM1)
Method(i,Get)
MethodReturn *self\value
EndMethod
Method(i,Set,v.i)
*self\value=v
EndMethod
EndDefineClass
EndModule
Objects in classes
It is possible to use objects in the properties of a class. You must declare it in the properties-field and then initalize in the Initalize()-Method of the class. My code handles the Clone and Destruct of the Object, but you must Initalize it manually.
Declare_Object(<object-name>,<class>[,<arraysize>)
Only useable in the properties-section. Array size is optional. Array-Size is same as in a structure. When you want to access a object in an array, type something like: *self\<object-name>[1].
Initalize_Object(<object-name>,<class>[,<arraysize>)
Should only used in the Initalize-Method of a class.
Example:
Code: Select all
Class(cDeep2);- cDeep2
DeclareMethods
Properties
Declare_Object(var,cDeep1,2)
Methods
Method(i,Initalize)
Initalize_Object(var,cDeep1,2)
*self\fakemem=fakealloc()
EndMethod
EndClass
SizeOf_Class(<class name>)
Return the size of an object of the class.
DebugCreate_Obj(<obj name>,<message>)
Create a message in the debug-window, which contain the object name, class name and the position in sourcecode which the object was created.
Setup the IDE
I suggest to add this words to the "Costum-Keywords" list in the preferences:
Code: Select all
Class
DeclareClass
DefineClass
ParentClass
DeclareMethods
Properties
Methods
EndDeclareClass
Method
MethodReturn
EndMethod
AliasMethod
EndClass
EndDefineClass
Allocate_Object
Free_Object
Protected_Object
Global_Object
Define_Object
Static_Object
Declare_Object
Initalize_Object
_FakeEnd
_End
_EndProcedure
_ProcedureReturn
self
Code: Select all
_EndProcedure -1 0
Class 0 1
DeclareClass 0 1
DefineClass 0 1
DeclareMethods 0 1
EndClass -2 0
EndDeclareClass -2 0
EndDefineClass -1 0
EndMethod -1 0
Method 0 1
Methods -1 1
Properties -1 1