VpPuZzle. Observe. Part 1
Автор: Виктор Юхтенко
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
Класс A | Класс 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.
- myClass is the static class with the predicate (not constructor) new() at the calling side.
- The predicate call new() transported to DLL and real constructor is invoked
MyClassInstance=myClass ::new()
- MyClassInstance converted by
MyClassObject=convert(object,MyClassInstance)
- MyClassObject delivered to calling side
- 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(…) <vip> So the source code of the calling class is not changed. ==Механизмы Pzl-cистемы== '''Pzl-Система''' представляется классом '''pzl''' и включает библиотеки, именуемые '''PzlSystem'''. Pzl-Система: *автоматически загружает DLL, содержащую класс, к которому производится обращение *транспортирует вызовы конструктора и возвращает указатель на экземпляр объекта *обеспечивает возможность взаимодействия классов, помещенных в различные DLL, а не только классов основного приложения и DLL *автоматически выгружает DLL, когда прекращено использование всех классов данной DLL *обеспечивает минимальные изменения в текстах программ *не требует существенного изменения структуры основного приложения Pzl-Технология: *включает все неоходимые библиотеки и тексты классов, поддерживающих функционирование pzl-системы *позволяет легко встроить PZL-Механизмы в действующие проекты *автоматически генерирует все необходимые коды *содержит все необходимые инструменты для практического использования pzl-системы PZL-Технология требует понимания концепций: *VIP-пакет (VipPack) как PZL-компонента (PzlComponent) *Представитель Компоненты (Component Delegate (proxy)) *VIP проекты как Pzl-Контейнеры (PzlContainer) *Главное приложение как PZL-Порт (pzlPort) ===Pzl-компонента (PzlComponent)=== Pzl-компонента является обычным классом, помещенным в пакет системы Visual Prolog. Главными особенностями являются: *этот класс наследует от класса pzlComponent *базовый интерфейс этого класса поддерживает интерфейс pzlComponent *базовый интерфейс этого класса содержит специальную константу-дескриптор componentDescriptor_C *имя интерфейса и имя класса должны отличаться *конструктор класса является единственным и представляется как new:(object AnyUserDefinedObject). Аргумент конструктора не имеет специльного назначения в рамках Pzl-системы. Константа-дескриптор componentDescriptor_C принадлежит специальному домену pzlComponentInfo_D. Один из аргументов структурированного домена pzlComponentInfo_D определяет уникальный идентификатор компоненты. Идентификатор может иметь форму string или это может быть универсальный идентификатор, как это обычно принято в MSCOM технологии. Компонента имеет дополнительно имя, которое может быть использовано для создания экземпляра компоненты. Это имя также является частью структуры константы-дескриптора componentDescriptor_C. Код класса A после его преобразования в Pzl-компоненту приведен в ниже. <vip> 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
Класс B, преобразованный в 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, так и контейнера
Отсутствующие здесь файлы 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-механизмов.