Artifact 9ee9e6af3f45c2a2b6211bda9850014bee8a571f:
- File doc/FreshLibOOP.md — part of check-in [3c68946a23] at 2016-02-24 07:00:25 on branch trunk — Edits of the help documents. Removed some outdated chapters of FreshLib reference. Changed a little the visual style. Increased font size for better use on high dpi devices. (user: johnfound size: 11877)
FreshLib object oriented programming library.
Introduction
The latest versions of FreshLib, silently migrated to a new object oriented programming macro implementation. Now it implements real OOP architecture with inheritance, polymorphism, virtual methods and properties.
The implementation uses jump tables, so it is pretty fast, especially compared to the previous implementation.
The syntax is based on pseudo-instructions and is compatible with assembly language programming style.
The main target for this library is the programming of GUI interfaces, because the structure of GUI, usually fit very well in the OOP paradigm. But of course, the library can be used for general purpose OOP programming.
Object definition. Encapsulation
[#object] object macro
[#endobj] endobj macro
The definition of the object is provided by object / endobj macros. The
syntax is very similar to struct macros, but the definition can be inherited
and the body may contain more elements:
;begin fasm object NAME[, PARENT] BODY endobj ;end
PARENT can be either other object type name or not be specified at all. If PARENT is
missing the object has no parent and does not inherits any fields. If PARENT is specified,
the object inherits all fields from the parent object.
All field names in the object definition must be local names related to the object name, i.e. must start with dot character.
The body may contain following elements:
- General FASM data definition fields:
;begin fasm object TAnimal .length dd ? .height dd ? .weight dd ? endobj ;end
The fields are aimed to provide storage of private for the object data. Of course, it is assembly language and the fields are not hidden from the user, but a good practice is to not use them as an interface. (There are other members that are to be used as an interface - the parameters and methods).
object parameter definition through
paramdirective. Will be described in details later.object method definition through
methoddirective. Will be described in details later.
Object parameters; param directive;
[#param] param macro
The parameters (aka "properties") are object data fields, that are not necessary memory cells. Instead their values can be read and write by several flexible ways. These ways are transparent for the programmer. The FreshLib implementation allows 3 ways to reading writing parameter values:
By direct read/write to a memory fields.
By calling object method that to read or write parameter value using any software method, for example computing the value, or reading it from a file or even from the network.
Not reading/writing at all - the parameters can be read-only and/or write-only.
The syntax of the definition is:
;begin fasm param PARAM_NAME, GET_TARGET, SET_TARGET ;end
PARAM_NAME is the name of the parameter. It must be local related to the
object name (to start with dot).
GET_TARGET defines how the value of the parameter will be get. This
parameters can be either NONE, object field name or object method name. In
the first case, the parameter can't be read. In the second, the read of the
parameter becomes simple structure field read and in the third, the read of
the parameter will cause the object method to be call and the return value of
the method will be accepted as a value of the parameter.
The get method must have no arguments and must be defined in the object definition as normal method.
SET_TARGET defines how the value will be write to the parameter. Again we
can use NONE, field name or method name. The only difference with the
GET_TARGET is that the set method has one argument - the new value of the
parameter.
Object methods; method directive; abstract directive;
[#method] method macro
[#abstract] abstract macro
This directive defines a method field in the object. The syntax is very
similar to the proc macro directive:
;begin fasm method NAME, [ARGUMENTS] or abstract NAME, [ARGUMENTS] ;end
NAME must be local related to the object name. The arguments can be
arbitrary count and also must start with dot (local).
Besides the explicitly defined arguments, one hidden argument .self is
defined for every method, that contains a pointer to the object instance in
the moment when the method is called. The .self argument is the first of all
arguments in the method.
The only difference between method and abstract variants is that the
methods defined with abstract keyword does not need to be implemented in the
given class. The abstract methods are simply placeholders for the children
object methods. They allow polymorphic calls of the method in the children
objects.
Every non-abstract method, defined in the object need to be implemented later
in the code. The implementation looks just like proc procedure definition,
with the difference that method keyword is used. In the implementation,
there is no arguments, because they are already defined in the object type
definition.
The methods can be override in the children classes. The inherited method is
still accessible through inherided command. This way the methods chains can
be called when needed.
;begin fasm method OBJ_NAME.METHOD_NAME begin CODE endp ;end
As is seen from the above description, the object type itself contains in the same container (capsule) all needed data and the code, needed to process this data. This property of the objects is called Encapsulation.
Some think that "encapsulation" is something about hiding data or code inside the objects, but this is not the case in the common case.
And of course in assembly language hiding data or code is impossible at all. The user have always access to all data fields and code in the object. There is no private fields or methods. It is simply not the assembly way.
Example of object definition with inheritance.
[!1:_images/catdog.png][Fig.1 Inheritance]
In the following example can be seen all the above syntax in practice. The
type TAnimal defines several common data fields, the parameter .Energy
that is read-only and gives the current kinetic energy of the animal and the
abstract method .Jump that is not implemented for TAnimal, as long as the
different animals jumps differently and need special implementation.
The children types TCat and TDog implements their own methods .Jump.
;begin fasm object TAnimal .length dd ? .height dd ? .weight dd ? .speed dd ?
param .Energy, .GetEnergy, NONE
method .GetEnergy
abstract .Jump, .height
endobj
object TCat, TAnimal
method .Jump, .height
endobj
object TDog, TAnimal
method .Jump, .height
endobj
method TAnimal.GetEnergy
begin
push ecx
mov ecx, [.self]
mov eax, [ecx+TAnimal.speed]
imul eax, eax
imul eax, [ecx+TAnimal.weight]
sar eax, 1 ; E = m.v^2/2
pop ecx
return
endp
method TCat.Jump
begin
; code that makes the cat jump not as other animals.
return
endp
method TDog.Jump
begin
; code that makes the dog jump not as other animals.
return
endp
;end
Object manipulation macros.
Creating objects
[#create] create macro
Once the object is defined, the user can create object instances, using the
macro instruction create. The syntax is:
;begin fasm create DST, TYPE ;end
The macro allocates memory for the object instance, fills needed fields and executes the method Create if such is defined for the given object type.
DST is the destination where we want the pointer to the object instance to
be stored. Can be register or memory location. Same as for mov instruction.
TYPE is an object type, previously defined with object macro.
Destroy object.
[#destroy] destroy macro
The allocated object instance must be destroyed when not needed with the macro
instruction destroy. The syntax is:
;begin fasm destroy SRC ;end
SRC is a pointer to the object instance. Can be register or memory content.
The macro will first call the Destroy method of the object if such is defined and then will free the allocated dynamic memory.
Set/Get object parameters.
[#get] get macro
[#set] set macro
The value of object parameter can be get with the macro get. The syntax is:
;begin fasm get DST, PTR_OBJ, TYPE:PARAM ;end
The macro gets the value of TYPE:PARAM parameter of the object PTR_OBJ and
store it in DST.
DST and PTR_OBJ can be register or memory location, TYPE must be already
defined object type and PARAM parameter, defined in this object or any of its
predecessors.
Notice, the TYPE and PARAM must be separated by colon ":", not a dot.
The value of object parameter can be set to new value with the macto set.
The syntax is similar to the previous, only in different order:
;begin fasm set PTR_OBJ, TYPE:PARAM, VALUE ;end
PTR_OBJ is the object which parameter have to be set.
TYPE:PARAM specifies the parameter name.
VALUE is the new value of the parameter.
Executing object methods.
[#exec] exec macro
The macro instruction exec executes some object method. The syntax is:
;begin fasm exec PTR_OBJ, TYPE:METHOD, [ARG] ;end
The macros has various count of arguments, depending on the arguments the method accepts.
TYPE:METHOD must be valid combination of defined object type and method
name, separated by colon ":".
Check object type
[#istype] istype macro
The user can check whether the object belongs to some type by using "istype" macro instruction:
;begin fasm istype OBJECT, TYPE ;end
This macro returns ZF flag set if the object belongs to the type or ZF cleared if not. It will not change any registers.
For example, see following code:
;begin fasm create eax, TCat ; create object instance.
istype eax, TCat
je .yes_cat ; ZF=1 It is a TCat...
int3
.yes_cat: istype eax, TAnimal je .yes_animal ; ZF=1 ...also, it is TAnimal...
int3
.yes_animal: istype eax, TDog je .yes_dog ; ZF=0 ...but is it NOT TDog.
int3 ; will stop here
.yes_dog: ;end
Polymorphism
Polymorphism is one of the main properties of the OOP. It is the ability of the object to execute different code on the same method call, depending on its type.
[!2:_images/poly.png][Fig.2 Polymorphism]
In the context of our library, as long as the types inherit all the methods of
its parents and can redefine it, it means, that in the above example, TCat
and TDog both have method .Jump, inherited from TAnimal. (In this sense,
they are both of type TAnimal), but the implementation of TCat.Jump and
TDog.Jump differs.
This way, we can use the same code to process both types.
;begin fasm create eax, TCat create ebx, TDog
exec eax, TAnimal:Jump, 100
exec ebx, TAnimal:Jump, 100
;end
You can see, that the same code will call in the first case TCat.Jump and in
the second case TDog.Jump methods.