Copyright (c) Prolog Development Center SPb

Pzl-technology - dll-based component approach 

DLL-based component technology (The Visual Prolog Puzzle or VPPZL or just PZL) is the set of agreements to build VIP-based applications on the basis of the standard VIP packages placed into DLLs. The VIP package fitted to PZL-technology agreements is named pzl-component. The VIP project (executable or DLL), which contains pzl-components, organized in a special way, is named pzl-container. The short description of the pzl-technology and it's basic consepts may be found below.

Motivation

Talking about components we consider component as the peace of software, which can be reused in applications without the change of the source code.

The component-based programming is one of the ways to make software development efficient. The usual ways to represent components in the component-based technologies today are:

As to Visual Prolog the VIP package is the example of the component, which is represented via the source code.

The use of statically linked libraries as components in VIP is possible. Compare to source code the use of statically linked libraries gives the only opportunity to hide the internal algorithms of the component. But components based on both source code and static libraries have the negative features:

The best way of the component representation is the DLL. With the use of DLLs almost all goals of the efficient development are reached. Applications become expandable and easily modifiable. Microsoft Component Model technology (MSCOM) is the one of the possible sets of agreements regarding the use of DLLs. The Microsoft agreements, which make DLLs as the COM components are widely used. Visual Prolog currently supports the use of the MS COM components. But to make the use of MSCOM in the VIP-style takes time and still has some problems. Placing the code to DLLs we (being programmers) meet problems which we must have in mind. We must: take care for the loading and unloading the DLLs organize the general error handling think about the common output stream.

The goal was to support the use of DLLs with no knowledge about DLLs, using the current VIP IDE.

The usual way to use the DLL

Let’s assume we have two interacting dynamic classes shown in the table

Class A Class B
inrerface a 
predicates
pA:().
end interface a
 
class a:a
end class a
 
implement a
clauses
...
Ob=b::new(),
Ob:pB().
clauses
pA():-
...
end implement a
inrerface b 
predicates
pB:().
end interface b
 
class b:b
end class b
 
implement b
clauses
...
Oa=a::new(),
Oa:pA().
clauses
pB():-
...
end implement b


Let’s assume now that on some reasons we wish to place the implementation of the class B into DLL. The standard MS Windows methods are supported in VIP6 well enough by the IDE and the PFC class pfc\application\useDLL.

It is the easy procedure: using the IDE it is needed to create project with the target type DLL. Then the package, which includes the class B, must be included to the appropriate DLL project. The exported predicate must be created and then it must be linked to the predicates of the class B.

The part of the application, which uses class placed to the DLL, must be also modified. Say for the calling the predicate pB the code in the class A will look like

...
ObjDll = useDll::load(DLLFileName),
pB_Ref=ObjDll:getPredicateHandle(pB_Exp),
pB_Ref(),
...

It is not important how much the code above has practical sense. The main point is that the code of the class A, which calls the predicate pB, must be changed dramatically and it becomes hardly dependant on the procedure of the interaction with the class placed to DLL.

Moreover, the creation of the instance of the class A and the call of the predicate pA from the class B side will need to create some special code.

VPPuZzle: Basic Idea

The PZL technology makes it possible to split the application on parts, which are placed to DLL so the codes of the classes are not dramatically changed. The called and calling classes are indifferent in that case to the place where they are placed. It means, that all the communication way work well for calling and called classes:

The two features of VIP system are used to make this possible:

The PZL technology is built around the basic idea: When some peace of code needs to call the predicate of some class placed to DLL, then the call new() is transported to DLL, the new instance of the needed class is created and then the pointer to the created object is transported back to the caller. Between main application and DLL this pointer is transported belonging to the Object domain.

But on the back way to the caller it is converted to the domain of the called class.

So in the reality the chain of substitutions and conversions is made, when we use the call:

... 
MyClassInstance=myClass::new(),
...

Let’s assume the DLL is already loaded.

  1. myClass is the static class with the predicate (not constructor) new() at the calling side.
  2. The predicate call new() transported to DLL and real constructor is invoked MyClassInstance=myClass ::new()
  3. MyClassInstance converted by MyClassObject=convert(object,MyClassInstance)
  4. MyClassObject delivered to calling side
  5. MyClassObject converted to MyClass domain by MyClassInstance=convert(myClass,MyClassObject)

Thus from the pragmatic point of view the call

MyClassInstance =myClass::new()

will take place, and then it is used as usual

MyClassInstance:callNeededPredicate()

So the source code of the calling class is not changed.

The mechanizms of the Pzl-system

The Pzl-system is represented by the class pzl.

The pzl-system:

The pzl-technology:

The technology means several entities to be described:

Pzl-component

PzlComponent is the usual Visual Prolog class. The only difference is that:

The constant componentDescriptor_C belongs to the special domain pzlComponentInfo_D. One of the arguments of the structured domain pzlComponentInfo_D defines the unique component identifier. The iniquity has the sense only in the given application. The identifier may belong to the domain string or it can be a Universal ID, the same as it used in the MSCOM technology. Component has also the alias, which can be used to create the instance in some cases. The Component Alias is the part of the component descriptor also. The source code of the class A after the conversion to the pzlComponent is shown below.

inrerface iA 
supports pzlComponent
constants
componentDescriptor_C:…componentInfo
( componentAlias_C, componentID_C, ... ).
predicates
pA:().
end interface iA
 
class a:iA
constructors
new:(Object).
end class a
 
implement a
inherits pzlComponent
clauses
new(_Object):-
...
 
clauses
...
Ob=b::new(This),
Ob:pB().
clauses
pA():-
...
end implement a

Class B converted to the pzlComponent looks absolutely the same except the name of the class. You can see that there are changes only in declarations and not in the meaningful code of the implementation. You can see that the pzl-component doesn't know nor about the place where it is placed, nor about the place where the component, which it comminicates with, placed.

The registration of the component

Pzl-system must know where the pzl-component is placed at the computer. To provide this the registration mechanism is used. The information regarding the component registration may be stored in the user-defined file or it may be stored in the Windows Registry. The same component may be registerede in more then one place.

The pzl-technology tools give the possibility to register or to deregister any component from the any place of registration one by one or using the group mode.

Three ways to create an instance of the component

The initial and the most important process of the communication of two classes is that one class must get the pointer to the instance of the other created class.

There are three ways to invoke the creation of the instance of the PzlComponent:

The first way was already described and looks like

... 
MyClassInstance =myClass::new(SomeObject),
MyClassInstance:callNeededPredicate()
...

It corresponds to the usual VIP rules.

The second way uses PzlSystem explicitly and looks like

...
MyClassObj=pzl::newByName(“MyClass”,...),
MyClassInstance=tryConvert(iMyClass, MyClassObj),
MyClassInstance:callNeededPredicate(),

Here iMyClass is the base interface of the class myClass and “MyClass” is the string name (alias) of the class myClass.

The third way also uses PzlSystem explicitly and looks like

...
MyClassObj=pzl::newByID(str(“MyClass”),...),
MyClassInstance=tryConvert(iMyClass, MyClassObj),
MyClassInstance:callNeededPredicate(),

Here iMyClass is the interface of the class myClass, and str(“MyClass”) is the string form of the class identifier.

The string form of the class identifier str(...) is the convenient form, but it doesn't provide the uniqueness. To provide the uniqueness it is recommended to use the numeric identifier, which must look like (numbers depend on the user's wishes):

uid(0xB5B1AE3D,0xBD01,0x4A29,0x9A,0x14,0x48,0x56,0x34,0x97,0xC7,0xC9).

The user is free to choose the way to represent the identifier. For instance while learning the programming with the pzl-system use it is convenient to use the string representation of the identifier str(...). In the case of the real commercial programming it is preferable to use the numeric identifier uid(...).

Class delegate (proxy)

To use the usual VIP style

...
MyClassInstance =myClass::new(SomeObject),
MyClassInstance:callNeededPredicate() ...

in the case, when called and calling classes placed in different entities (in the executable and DLL or in two different DLLs), the process of creation of the instance of the class uses the delegate (proxy) class.

Thus the calling class calls the predicate new() of the proxy class and then proxy class communicates with the Pzl-system and Pzl-system returns the pointer to the instance of the called class to proxy class. Proxy class then converts the object domain to the domain of the called class and the creation of the pointer to the instance is complete.

The text of the proxy class is extremely simple and it is possible to generate the text of the proxy class automatically for each PzlComponent. As an example the text of the class implementation of the class-proxy is shown

implement aboutDialog
open core
 
 clauses new(ObjIn)=convert(iAboutDialog,ObjOut):-
ObjOut=pzl::new(iAboutDialog::componentID_C, ObjIn).

The package file AboutDialog.pack has the text

#include@"AboutDialog\AboutDialog.ph"
#include@"AboutDialog\AboutDialogProxy.pro"

Pzl-technology tools can generate the content of files needed to have a class-proxy automatically.

The combination of the Original and Proxy classes

In the case, when called and calling classes placed in one entity (in the executable or in one DLL), then the communication of the classes in the form

...
MyClassInstance =myClass::new(SomeObject),
MyClassInstance:CallNeededPredicate()
...

happens explicitly, and PZL system doesn’t participate in the interaction. The usual VIP rules work here. The package for this case looks like

#include@"AboutDialog\AboutDialog.ph"
% privately used packages
#include @"pfc\string\string.ph"
% private interfaces
 #include @"resourceIdentifiers.i"
 
% implementations
#include @"AboutDialog\AboutDialog.pro"

It was found convenient to have all files, related to the pzlComponent - the Original and Proxy classes and packages, mentioned in one package.

The conditional compilation gives the possibility to use the appropriate declarations and implementations. Depending on the meaning of the conditional compilation parameter, the original or proxy code is used.

The text of the combined package file is shown below.

#include @"AboutDialog\AboutDialog.ph"
#include @"pfc\string\string.ph"
 
#if iPzlConfig::useAboutDialogOriginal_C=true #then
% privately used packages
 
% private classes
 
% private interfaces
    #include @"resourceIdentifiers.i"
 
% implementations
    #include @"AboutDialog\AboutDialog.pro"
#else
    #include @"AboutDialog\AboutDialogProxy.pro"
#endif

The same principle is used to combine the contents of the header file AboutDialog.ph for the use of the Original and the Proxy related files

So if we need to use the original classes, then the constant useAboutDialogOriginal_C must have the value true, and if we need to use the proxy-related files, then the constant useAboutDialogOriginal_C must have the value false.

The constant, which defines the way of communication, is defined in the file PzlConfig.i of the package PzlConfig.pack , which is the collection of the pzlComponents of the given entity (executable of DLL).

PzlContainer

The PzlContainer is the usual Visual Prolog project (one container - one project). It can be the project, which generates the executable application or it can be the project, which generates the DLL. Unlike the pzl-component, the PzlContainer “knows” which pzl-components it contains. All pzl-components, which are placed to the given pzl-container, must be included to the special package PzlConfig. The implementation of the class pzlConfig deals with the pzl-components also. The picture below shows the structure of the PzlConfig package as it can be seen on the Project window of the IDE.

The file of the interface type PzlConfig.i, implementation type PzlConfig.pro and package type PzlConfig.pack are the only entities, related to the pzl-components, included to the given project.

Files PzlConfig.cl and PzlConfig.ph has no deal with pzlComponents and thus are included to the set of files of the PzlSystem. Because of the information regarding the pzlComponents included to the given pzlContainer is concentrated only in files of the pzlConfig package. This feature gives the possibility to move pzlComponents from one pzlContainer to another pzlContainer with no changes in other parts of the projects.

Pzl-technology tools may update the content of the pzlConfig files automatically, when pzlComponent is added (removed) to (from) the given pzlContainer.

Being the usual project, PzlContainer may contain not only pzlComponents, but it may contain usual Visual Prolog packs also according to the user needs.

The feature of the PZL-technology, when two pzlComponents placed in one entity communicate explicitly with no use of PzlSystem and communicate via PzlSystem, when they are placed in different entities, gives the possibility to split the application on parts whenever user finds it convenient to him. No change in code is needed. Pzl-technology tools can help in this.

Pzl-system

All features of the PZL mechanism are provided by the Pzl-system. Pzl-system contains some classes represented as source code and some classes represented as the statically linked libraries. It was used the feature of Visual Prolog that the source code of libraries can be written in Visual Prolog language. The picture below shows the content of the PzlSystem for the executable entiry (EXE).


And the picture below shows the content of the PzlSystem for the DLL

Pictures/SystemStructure Dll.png

The difference is in the libraries and in the package pzlPort.pack, which used in the case of application. There is no need to remember all these files – the Pzl-technology tools can generate the project for the DLL and any VIP application project can be easily modified to support the Pzl-technology. The name PzlPort was chosen for the core of the PzlSystem, which must be included to the application project.

The PzlPort is responsible for the loading and unloading DLLs. When user calls the pzlComponent constructor, PzlPort finds the DLL, which contains the appropriate pzl-component. If the DLL is not already loaded, then it loads the DLL and communicates with the classes of the pzl-container.

The PzlPort uses the order as follows:

The pzl-technology uses the same principles to support the lifecycle for classes. When the instance of the class is not needed, then all references to this class must be removed. PzlPort is responsible to unload the appropriate DLL, when there is no pzl-components in use. This happens, when the Visual Prolog garbage collector removes the instance of the class.

Thus User has no deal with the DLLs and has the deal the only with classes the same way as it would be using the standard VIP programming style

Compatibility and Authorising

Because of the parts of the applications, based on the pzl-Technology, may be build in different time and with the different versions of the Visual Prolog, the incompatibility between the application and DLLs and between DLLs may happen. Tools placed to pzl-port and pzl-container libraries communicate and check this compatibility before the use of components have started.

If the incompatibility have descovered, then the DLL is unloaded and the exception is generated.

Active Objects registration

PzlSystem performs some functionality, which are not directly concerns with the component technology.

One of these kinds of the functionality is the Active Object registration. Let’s come back to the major construction of the VIP:

...
MyClassInstance =myClass::new()
MyClassInstance:callNeededPredicate(),
...

Once created instance of the class will be destroyed if the pointer (reference) to the instance will not be stored. There are many situations, when one instance may be used by many other classes. Then it is needed to store the pointer to this instance in the place, which all components can easily reach.

When we have the application built using the usual style we may have the static class, which can store pointers to instances. When we use the technology based on the DLLs we need some data store, which all components can refer to. In the pzl-technology the special data store engine embedded to the pzlPort. Any component placed to any container can easily make the operation like

MyClassInstance =myClass::new(), 
pzl::register(“MyClassInstance1”, MyClassInstance),
MyClassInstance:callNeededPredicate(),
...

Any other object can get the object using the call

...
Object =pzl::getObjectByName_nd(“ MyClassInstance1”),
 !,
MyClassInstance=tryConvert(iMyClass, Object),
MyClassInstance:callNeededPredicate(),
...

Class Pzl contains many other useful predicates to manipulate with the object registration.

If we follow the idea of the open architect applications, then the user-defined component may have an access to all components active in the application at the moment. The only condition is that User must know the interfaces of the classes in this case and User must know the principles of the organization of the application.

The common Error Handling space

Vip has convenient error handling system. Pzl-technology supports the common error handling space. Thus any pzl-component has the access to the exception data common to all pzl-components of the application.

The common standard output stream STDO

All pzl-components active at the given moment use the same standard output stream.

Summary

The following features of the Visual Prolog programming system used:

The Pzl-technology has all advantages and disadvantages, which VIP programming has because of it don’t change much the VIP style of programming.

The pzl-technology sets some limitations:

The usual limitations of VIP DLLs must be taken in account: