VpPuZzle. Observe. Part 1

Материал из wikiru.visual-prolog.com

Автор: Виктор Юхтенко

VpPuZzle. Обзор


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 masic 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:

  • Source code
  • Statically linked libraries
  • Dynamically linked libraries (DLLs)

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 but is not clearly supported.

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 size of the application is the sum of the size of used components
  • Developers do not have the opportunity to extend the application’s functionality without the rebuilding the whole application.

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:

  • Main applicaion - DLL
  • DLL - Main applicaion
  • DLL - DLL

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

  • The main application and DLLs created by VIP have the same memory space;
  • All instances of classes belong to the domain Object.

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 and appropriate library.

The pzl-system:

  • makes automatic loading of DLLs with the needed class
  • transports the constructor calls and object pointers back
  • gives the opportunity to interact classes placed to different DLLs (not only to the application and DLL)
  • unloads automatically the DLLs, when all classes are finalized by garbage collector
  • leads to minimal changes in the source codes of classes
  • doesn’t change dramatically the structure of the main application

The pzl-technology:

  • includes all nessesery libraries and classes, which support the running of the pzl-system
  • gives the possibility to update the existing application projects so it can use the PZL technology
  • gives the possibility to generate the code of DLLs automatically
  • contains all needed tools to use the pzl-system practically

The technology means several entities to be described:

  • VIP Pack as the pzl-component
  • Component Delegate (proxy)
  • VIP Project as the pzlContainers
  • Main Application as the pzlPort

Pzl-component

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

  • this class inherits from the class pzlComponent
  • the base interface of this class supports interface pzlComponent
  • the base interface of this class contains the special constant componentDescriptor_C
  • the interface name and the class name must be different
  • The constructor of the class declared as new:(object AnyUserDefinedObject)<br\>The argument has no special meaning in the context of the Pzl System.

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, placed.

Регистрация компонент

Для того, чтобы знать, где на данном компьютере размещается та или иная pzl-компонента, используется механизм регистрации компонент. Информация о регистрации компонент может быть сохранена либо в файле, который находится в распоряжении и под управлением пользователя, либо эта информация может быть сохранена в реестре Windows. Одна и та же компонента может быть зарегистрирована в нескольких местах.

Средстав pzl-технологии могут зарегистрировать или снять с регистрации pzl-компоненты в любом из заданных мест раздельно или совместно в составе группы.

Регистрация компонент

Для того, чтобы знать, где на данном компьютере размещается та или иная pzl-компонента, используется механизм регистрации компонент. Информация о регистрации компонент может быть сохранена либо в файле, который находится в распоряжении и под управлением пользователя, либо эта информация может быть сохранена в реестре Windows. Одна и та же компонента может быть зарегистрирована в нескольких местах.

Средстав pzl-технологии могут зарегистрировать или снять с регистрации pzl-компоненты в любом из заданных мест раздельно или совместно в составе группы.

Три способа создания экземпляров

Важнейшей частью процесса взаимодействия двух классов является то, что вызывающий класс должен иметь в своем распоряжении указатель на экземпляр вызвываемого класса - обьект класса.

Существуют три способа вызова создания экземпляра pzl-компоненты:

  • путем вызова конструктора new(...)
  • путем вызова предиката newByName(...)
  • путем вызова предиката newByID(...)

Первый способ уже обсуждался выше и выглядит следующим образом:

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

Это соответствует обычным правилам VIP.

Второй способ использует pzl-систему явно и выглядит так:

...
MyClassObj=pzl::newByName(MyClass,...), 
MyClassInstance=tryConvert(iMyClass, MyClassObj), 
MyClassInstance:сallNeededPredicate(),

Здесь iMyClass является интерфейсом класса myClass, а “MyClass” является строковым именем класса (alias) myClass.

Третий способ также явно использует pzl-систему и выглядит так:

 
...
MyClassObj=pzl::newByID(str(MyClass),...), 
MyClassInstance=tryConvert(iMyClass, MyClassObj), 
MyClassInstance:сallNeededPredicate(),

Здесь iMyClass является интерфейсом класса myClass, а str(“MyClass”) является строковым представлением идентификатора класса.

Заметим, что строковое представление идентификатора str(...) является удобным, но не обеспечивает надежной уникальности. Для обеспечения уникальности следует пользоваться численным идентификатором, который должен выглядеть следующим образом (числа, разумеется в каждом случае, свои):

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

Выбор способа использования зависит от обстоятельств, в частности от ответственности кода. Например, в ходе освоения техники программирования с использованием pzl-системы, можно пользоваться строковым идентификатором str(...), а в случае реального коммерческого программирования предпочтительнее использовать цифровой идентификатор uid(...)

Класс-делегат (proxy)

При использования обычного для VIP стиля программирования

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

в случае, если вызывающий и вызываемый классы помещены в разные сущности (EXE и DLL или в разные DLL) , процесс создания экземпляра класса использует класс-делегат (proxy). При этом вызывающий класс вызывает предикат new() класса-делегата, а класс-делегат взаимодействует с PZL-системой, которая возвращает классу-делегату указатель на экземпляр вызываемого класса. Класс-делегат преобразует указатель, имеющий тип object в тип вызываемого класса и, таким образом, создание указателя экземпляра завершается.

Текст класса-делегата очень прост и единообразен, что создает возможность его автоматической генерации для каждой pzl-компоненты. Пример текста класса-делегата для класса "AboutDialog" приведен ниже

implement aboutDialog
   open core
 
constants
  className = "AboutDialogProxy".
  version = "1.0".
 
clauses
    classInfo(className, version).
 
clauses
  new(ObjIn)=convert(iAboutDialog,ObjOut):-
   ObjOut=pzl::new(iAboutDialog::componentID_C, ObjIn).
 
end implement aboutDialog

Пакет AboutDialog.pack имеет текст

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

Средства pzl-технологии генерируют классы-делегаты автоматически.

Комбинированное использование оригинального класса и класса-делегата

В случаях, когд вызываемый и вызывающий классы помещены в одну сущность (в EXE или в одну и ту же DLL), взаимодействие классов в форме

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

происходит непосредственно и pzl-система не принимает участие во взаимодействии. Здесь действуют обычные правила VIP. Пакет в этом случае выглядит следующим образом

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

С технологической точки зрения удобно иметь все упоминания о файлах, относящихся к pzl-компоненте - пакеты, представляющие оригинальный класс и класс-делегат, в одном пакете.

Возможность комбинированного использования соответствующих деклараций и имплементаций дает использование условной компиляции. В зависимости от значения параметра условной компиляции используется либо оригинальный класс, либо класс-делегат.

Пример текста такого комбинированного пакета приведен ниже.

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

Тот же принцип используется для комбинированного использования оригинального класса и класса-делегата в файле заголовка AboutDialog.ph.

Таким образом, если используется оригинальный класс, то константа useAboutDialogOriginal_C должна иметь значение true, а, если используется класс-делегат, то константа useAboutDialogOriginal_C должна иметь значение false.

Значение константы, которая определяет способ взаимодействия, определяется в файле PzlConfig.i, в котором собрана информация обо всех pzl-компонентах данной сущности (EXE или DLL).

Pzl-контейнер

Pzl-контейнер является проектом системы программирования Visual Prolog (один контейнер - один проект). Он может быть проектом, создающим исполняемое приложение (EXE) или проектом, создающим DLL.

Pzl-компонента "не знает", в каком контейнере она помещается. В отличие от pzl-компоненты pzl-контейнер "знает" какие pzl-компоненты он содержит. Все pzl-компоненты, которые помещены в данный pzl-контейнер, должны быть включены в предопределенный пакет PzlConfig.pack. Имплементация класса pzlConfig содержит обращения к классам pzl-компонент.

Ниже приведена структура PzlConfig, как это представляется в проектном окне IDE как главного приложения Exe, так и контейнера

PzlConfigStructure.png

Отсутствующие здесь файлы PzlConfig.cl и PzlConfig.ph не содержат информации по конкретным pzl-компонентам и включены в набор файлов, помещенных в директорию PzlSystem. Приведенные же три файла PzlConfig.i, PzlConfig.pro и PzlConfig.pack обеспечивают работу pzl-компонент, включенных в данный проект. Это обстоятельство позволяет перемещать pzl-компоненты из одного контейнера в другой без модификации остальных частей проектов-контейнеров. Средства pzl-технологии обеспечивают автоматически согласованную модификацию файлов пакета pzlConfig при добавлении или удалении соответсвующих pzl-компонент из проекта-контейнера.

Являясь обычным Vip проектом, pzl-контейнер может содержать не только pzl-компоненты, но и любые другие пакеты, соответствющие потребностям.

VpPuZzle. Обзор

Свойство pzl-технологии, заключающееся в том, что две pzl-компоненты, помещенные в один проект (генерирующий EXE или DLL), взаимодействуют непосредственно без участия pzl-системы, и взаимодействуют через pzl-систему, если они помещаются в разные проекты, позволяет легко делить приложения на части тогда, когда программист находит это удобным. Дополнительных изменений в проектах не требуется. Средства pzl-технологии позволяют производить такое перемещение без вникания в технические детали организации pzl-механизмов.

Ссылки