Reviews / articles about OS/2 |
Operating systems: ArcaOS, eComStation, IBM OS/2 Warp |
|
|
DATE: 2006-05-05 03:49:17 AUTHOR: Yuri Prokushev
ВведениеДанный цикл статей посвящен независимому от языка объектно-ориентированному программированию в системе eComStation с помощью объектной модели SOM. Статьи попытаются дать полное представление о возможностях объектной модели и различных библиотек классов, существующих на данный момент. В течение всего цикла статей вам понадобятся следующие инструменты:
Предполагается, что читатель знаком с объектно-ориентированным программированием и понимает языки C/C++/Delphi, а также владеет основной терминологией системы eComStation.
Что такое SOM?SOM (System Object Model, Системная Объектная Модель) - это объектная модель, независимая от конкретного языка программирования, предназначенная для создания и хранения двоичных библиотек классов. SOM представляет собой одну из наиболее интересных разработок в области компьютерной индустрии. Объектно-ориентированное программирование (ООП) заслужило безоговорочное признание в качестве основной парадигмы. Однако эффективное использование возможностей ООП ограничивается несовместимостью библиотек классов, сгенерированных различными компиляторами. Так, например, классы, созданные с помощью VAC, не будут доступны пользователям Watcom или GCC. А о других языках вообще нет речи. Причем такая проблема встает даже при использовании одного и того же компилятора, но разных версий. SOM снимает эти ограничения. Также, благодаря SOM, обеспечивается разделение клиента и объектов на уровне двоичного кода. Кроме того, SOM представляет интерес не только для разработчиков eComStation. Настоящий потенциал SOM заключается в ее совместимости практически с любой платформой и любым языком программирования. В SOM интерфейс любого класса описывается с помощью языка IDL (Interface Definition Language, язык определения интерфейса), который может быть преобразован к синтаксису конкретного языка программирования. Так, например, при использовании языка C для написания класса, файл IDL будет преобразован в набор файлов .C и .H, которые в дальнейшем используются как основа для написания приложения SOM или класса SOM. В отличии от библиотек, создаваемых C++ или Delphi, классы SOM могут быть легко доступны в любом другом языке. SOM используется в различных приложениях, в частности, интерфейс рабочего стола WPS (Workplace Shell) полностью построен на базе SOM. Кроме того, SOM является ядром VX-REXX и VisPro REXX. Другой известный пример использования модели SOM - это OpenDoc. Естественно, любое приложение, базирующееся на WPS, является приложением SOM. Примеров таких приложений достаточно. Начиная с широко известных расширителей рабочего стола Object Desktop и xWorkplace и заканчивая такими приложениями, как Audio-Data CD Creator и CW Multimedia Classes. Классы SOM могут поставляться в виде DLL- или EXE-файлов. Использование DLL дает бОльшую свободу. Именно с помощью DLL WPS расширен представленными выше программами. Использование же EXE и для клиента и для классов мало чем отличается от подходов, используемых в языках типа C++.
Распределенные вычисленияDSOM (Distributed System Object Model, Распределенная Системная Объектная Модель) позволяет объектам SOM работать в различных процессах. Эти процессы могут находится как на одной машине, так и на компьютерах, расположенных в сети. Теоретически, такая модель можеть сделать возможным расширение WPS значительно более простым, чем это является сейчас. Если бы WPS был написан с использованием объектов DSOM, то отладка WPS-приложений значительно бы упростилась, ввиду того, что в этом случае появилась бы возможность отлаживать один конкретный класс, а не всю подсистему WPS целиком. DSOM соответствует спецификации CORBA (Common Object Request Broker Architecture), которая определяет стандарт взаимодействия между прикладными программами в неоднородной сети. Спецификация CORBA была разработана консорциумом Object Management Group, объединяющим более 300 компаний - поставщиков программного обеспечения и оборудования, а также потребителей их продукции. В консорциум входят все основные компьютерные фирмы, что определяет его значительное влияние. Спецификация CORBA является частью более широкого стандарта для распределенной обработки, называемого OMA (Object Management Architecture, Архитектура Управления Объектами). CORBA определяет архитектуру посредника объектного запроса (Object Request Broker - ORB), который управляет взаимодействием между объектами и прикладными программами. Через ORB можно посылать запросы между процессами, работающими на одной машине, или между машинами в сети. DSOM представляет собой реализацию CORBA ORB. Developers Toolkit поставляется с несколькими подсистемами. Первая, имеющая название "Replication Framework", позволяет объекту существовать в нескольких процессах одновременно, даже в пределах сети. Изменения в любом клонированном объекте будет отражено во всех клонах объекта. Тип сети и прочие детали скрыты от программиста. Другая подсистема - "Persistence Framework" - используется для объектов, сохраненных на диске. "Event Management Framework" может быть использована для инкапсуляции действий, основанных на событиях, наподобе очереди сообщений Presentation Manager. Заметим также, что данными подсистемыми SOM не ограничен. В дальнейших выпусках мы вернемся к вопросу различных подсистем более подробно.
Подобные технологииУпомянув о SOM будет нечестным ходом умолчать о других подобных технологиях. Наверное, на первое место нужно вынести COM (Component Object Model, Компонентная Объектная Модель), являющейся основным конкурентом CORBA. Данная технология достаточно хорошо известна владельцам операционных систем класса Windows компании Microsoft. Отметим, однако, что распространение COM ограничено всего лишь одной платформой и вряд-ли будет когда-нибудь перенесена на другие. Наряду с COM следует отметить технологию XPCOM (Cross Platform COM), разрабатываемой в рамках проекта Mozilla. В отличие от COM, XPCOM разрабатывалась как переносимая модель составных объектов. Переносимость подтверждается достаточно большим перечнем платформ, для которых разрабатываются приложения Mozilla. Не следует также обходить различные реализации CORBA. На первом месте идет ORBit2 - реализация CORBA для проекта GNOME2. Вторым (хотя, может быть, и первым) идет реализация MICO. Впрочем, перечислять их здесь не имеет смысла, т.к. продуктов данного класса более чем достаточно. Существуют реализации ориентированные на различные языки программирования: C, C++, Delphi, Java и пр.
SOM против C++/DelphiТеперь, когда мы поверхностно познакомились с некоторыми аспектами SOM и Toolkit, давайте рассмотрим ООП-модель SOM. Сначала давайте рассмотрим, чем отличаются эти две модели ООП? SOM использует модель более ориентированную на стадию выполнения, в то время как C++ и Delphi модели ООП более ириентированы на стадию компиляции. В случае SOM это означает, что точная функция, которая будет вызвана или, например, размер объекта заранее неизвестны. Они станут известны во время выполнения. Методы вызываются по одной из следующих схем:
Приведенный список упорядочен по снижению скорости выполнения. Нахождение кода по смещению аналогичен виртуальным функциям C++ и Delphi. Метод нахождения по имени позволяет вызывать такие методы, имя которых неизвестно до тех пор, пока программа не запущена. Наиболее гибкий и времязатратный метод - метод нахождения с помощью dispatch-функции. Данный метод позволяет вызывать методы в зависимости от специфических предпочтений приложения. В отличии от C++, SOM не позволяет перегружать (автору больше импонирует перевод, точнее отражающий суть, "переопределение". Хотя и он не до конца точен.) методы .
МетаклассыДля программистов на C++ или Delphi термин метакласс вызывает некоторые сложности в понимании. В C++ и Delphi самым базовым объектом является класс. Методы и данные класса являются постоянными (static) членами класса. В случае SOM ситуация иная. В упрощенном понимании, классы являются экземплярами метаклассов, а объекты являются экземплярами классов. Метакласс содержит все функции и данные, относящиеся к классу. Говоря иначе, метакласс может содержат данные и методы, которые в C++ и Delphi явно или косвенно объявлены статическими, к которым относятся, например, конструкторы.
Простой пример 1Давайте возьмем пример "Hello World" созданный IBM и добавим какие-нибудь методы и данные. Начнем с HELLO.IDL. #ifndef hello_h #define hello_h #include <somcls.idl> interface M_Hello; interface Hello : SOMObject { string hello_(); attribute string hellomsg; void sayHello(); #ifdef __SOMIDL__ implementation { releaseorder: hello_, _get_hellomsg, _set_hellomsg,sayHello; callstyle=oidl; filestem = hello; metaclass = M_Hello; somInit: override; }; #endif /* __SOMIDL__ */ }; interface M_Hello : SOMClass { attribute string ClassData; Hello HelloCreate(in string msg); // Данный метод создает эксземпляр класса Hello // и использует значение "msg" для его ининциализации. #ifdef __SOMIDL__ implementation { releaseorder: HelloCreate,_get_ClassData,_set_ClassData; callstyle=oidl; filestem = hello; functionprefix=M_; somInitMIClass: override; somInit: override; // Просто так. Для проверки макросов родительского класса }; #endif /* __SOMIDL__ */ }; #endif /* hello_h */ В класс Hello был добавлен метод void sayHello(). В метакласс M_Hello был добавлен атрибут string ClassData. Определение атрибута автоматически приводит к созданию методов _get_ и _set_, предназначенных для установки и считавания его значения соответственно. Код от IBM может быть найден в каталоге \som\samples\somk\cpp\derived SOMObjects toolkit, если примеры SOM были установлены. Код для метода sayHello() показан ниже. Данный метод иллюстрирует, как найти экземпляры класса и данные класса. Приведеный код написан на C++. SOM_Scope void SOMLINK sayHello(Hello *somSelf) { //Эти две строчки автоматически создаются компилятором SOM HelloData *somThis = HelloGetData(somSelf); HelloMethodDebug("Hello","sayHello"); // Получить экземпляр класса M_Hello *helloCls=(M_Hello *)somSelf->somGetClass(); somPrintf("%s экземпляра класса %s\n",somSelf->_get_hellomsg(),helloCls->_get_ClassData()); } Как вы заметили, здесь доступ к данным и атрибутам осуществляется с помощью методов _get_ и _set_ вместо явного использовании имени атрибута. Также, данные класса доступны с помощью указателя на экземпляр метакласса M_Hello. Замечание: M_Hello является метаклассом для класса Hello. А теперь давайте создадим объекты из наших классов. int main(int argc, char *argv[]) { Hello *a,*b,*c; // создать экземпляр метакласса M_Hello (создание класса helloClsObj) M_Hello *helloClsObj=HelloNewClass(Hello_MajorVersion,Hello_MinorVersion); //Установим данные класса helloClsObj->_set_ClassData("Class 1"); //Вызовем метод класса M_Hello HelloCreate для создания экземпляра //класса Hello (объекты a, b, c) a=helloClsObj->HelloCreate("Привет из объекта A"); b=helloClsObj->HelloCreate("Привет из объекта B"); c=helloClsObj->HelloCreate("Привет из объекта C"); //Вызвать метод sayHello для каждого объекта a->sayHello(); b->sayHello(); c->sayHello(); // Освобождаем объекты a->somFree(); b->somFree(); c->somFree(); return 0; } После компиляции и запуска программы вы увидите следующее: Привет из объекта A экземпляра класса Class 1 Привет из объекта B экземпляра класса Class 1 Привет из объекта C экземпляра класса Class 1 Файлы с примерами для OpenWatcom в этом архиве.
Простой пример 2Давайте теперь разберем подробней еще один пример. Первым делом, при создании нового класса необходимо описать его интерфейс. Как было упомянуто ранее, это осуществляется с помощью языка определения интерфейса IDL. Простейший шаблон выглядит так: #include <somcls.idl> // включение определений базовых классов interface Demo : SOMObject // Определение класса Demo, предком которого является SOMObject { attribute string DemoWord; // Какие-нибудь жутко полезные данные void sayDemoWord(); // Какой-нибудь жутко полезный метод }; Как вы, наверное, заметили, здесь нет никаких метаклассов. Действительно, единственный метод просто выводит содержимое единственного атрибута. Причем атрибут здесь является динамической переменной, т.е. различной в различных экземплярах класса (объектах). Вводить новый метакласс здесь не имеет никакого смысла. После того, как вы создали интерфейс вашего нового класса, надо бы теперь написать его реализацию. Рассмотрим для начала случай использования языка C. Для этого создадим из интерфейса соответствующие шаблоны для кода. Делается это командой: sc -sh;ih;c;def demo.idl В результате вы получите файлы:
Создадим для начала программу, которая будет создавать два экземпляра класса Demo, устанавливать значение атрибута DemoWord и вызывать метод sayDemoWord. Напишем ее на двух языках: C и C++. Для начала самый простой вариант, C++: // Включаем определения интерфейса класса Demo для C++ #include "demo.xh" int main(int argc, char *argv[]) { // Получаем данные об окружении Environment *ev = somGetGlobalEnvironment(); // Создаем экземпляры класса Demo (объекты demo1 и demo2) Demo *demo1 = new Demo; Demo *demo2 = new Demo; //Устанавливаем значения атрибута DemoWord demo1->_set_DemoWord(ev, "123"); demo2->_set_DemoWord(ev, "321"); // Вызываем методы sayDemoWord demo1->sayDemoWord(ev); demo2->sayDemoWord(ev); // Освобождаем объекты delete demo1; delete demo2; // Завершаемся return 0; } Первый вопрос: "Где взять файл demo.xh?". Ответ, впрочем, очевиден. Сгенерировать с помощью sc: sc -sxh demo.idl В результате вы получите файл demo.xh. Здесь следует заметить, что по умолчанию Developer's Toolkit не содержит файлов xh. Их следует сгенерировать с помощью команды somxh По сравнению с предыдущим примером, здесь два отличия:
Параметр ev (появляется при отсутствии модификатора callstyle=oidl) отвечает за данные окружения и используется, например, для исключений. В принципе, можно исползовать callstyle=oidl для упрощения читаемости кода. Однако, такой подход может привести к сложностям при совместном использовании с CORBA и является режимом совместимости с более старыми версиями SOM. Запишем теперь для сравнения такой же код для языка C: #include "demo.h" int main(int argc, char *argv[]) { // Получаем данные об окружении Environment *ev = somGetGlobalEnvironment(); // Создаем экземпляры класса Demo (объекты demo1 и demo2) Demo *demo1 = DemoNew(); Demo *demo2 = DemoNew(); //Устанавливаем значения атрибута DemoWord Demo__set_DemoWord(demo1, ev, "123"); Demo__set_DemoWord(demo2, ev, "321"); // Вызываем методы sayDemoWord Demo_sayDemoWord(demo1, ev); Demo_sayDemoWord(demo2, ev); // Освобождаем объекты _somFree(demo1); _somFree(demo2); // Завершаемся return 0; } Как видим, для использования класса SOM не требуется наличия в языке поддержки объектов или классов. Теперь мы имеем весь код для проверки работы класса. Реализуем теперь сам класс. Т.к. мы решили для разнообразия реализовать класс на языке C, то возьмем сгенерированный ранее файл demo.c и добавим необходимый код: /* * This file was generated by the SOM Compiler and Emitter Framework. * Generated using template emitter: * SOM Emitter emitctm: 2.23.1.9 */ #ifndef SOM_Module_demo_Source #define SOM_Module_demo_Source #endif #define Demo_Class_Source #include "demo.ih" /* * Какой-нибудь жутко полезный метод */ SOM_Scope void SOMLINK sayDemoWord(Demo *somSelf, Environment *ev) { DemoData *somThis = DemoGetData(somSelf); DemoMethodDebug("Demo","sayDemoWord"); somPrintf("Скажем %s\n",_get_DemoWord(somSelf, ev)); } Как видим, в нашем случае требуется всего ничего: получить значение атрибута и вывести его на экран. Что мы успешно и сделали. Заметим только, что при вызове внутри класса somSelf означает экземпляр текушего класса. somThis используется для доступа к данным (атрибутам) данного экземпляра класса. Теперь скомпилируем и слинкуем реализацию и тестовую программу статически. Для этого создадим Makefile: CPPC = wpp386 CC = wcc386 INC = -I. -I$(SOMBASE)\include SC = $(SOMBASE)\bin\sc SCFLAGS = LINKER = wlink LDFLAGS = LIBLIST = $(SOMBASE)\lib\somtk.lib all: $(SOMBASE)\include\somxh.bld test.exe testc.exe test.exe: test.obj demo.obj $(LINKER) $(LDFLAGS) file demo.obj file test.obj library $(LIBLIST) name test.exe test.exe testc.exe: testc.obj demo.obj $(LINKER) $(LDFLAGS) file demo.obj file testc.obj library $(LIBLIST) name testc.exe testc.exe clean: .symbolic -del *.err *.obj *.exe *.map *.xh *.ih *.h $(CLEANFILES) >nul 2>&1 $(SOMBASE)\include\somxh.bld: @echo Данные пример требует файлов заголовков C++ @echo созданных с помощью команды somxh. @exit 1 testc.obj: test.c demo.h $(CC) $(INCLUDEPATH) $(CCFLAGS) $[ -fo=testc.obj test.obj: test.cpp demo.xh $(CPPC) $(INCLUDEPATH) $(CPPCFLAGS) $[ demo.obj: demo.c demo.ih $(CC) $(INCLUDEPATH) $(CCFLAGS) $[ demo.ih: demo.idl demo.h $(SC) -sih $(SCFLAGS) $[ demo.h: demo.idl $(SC) -sh $(SCFLAGS) $[ demo.xh: demo.idl $(SC) -sxh $(SCFLAGS) $[ Здесь тоже нет ничего сложного. Запустив команду wmake, мы получим два файла: test.exe и testc.exe. Первый - реализация на C++, второй - на C. Запустим и получим предсказуемый результат: Скажем 123 Скажем 321 Заметим, что во время компиляции мы получим несколько предупреждений, относительно того, что не указан порядок освобождения методов. В случае простейших методов порядок освобождения не важен. Однако, при достаточно сложных классах вам может понадобится данный модификатор. "Ну и что же?" - можете вы сказать. - "А где же библиотека классов?". Давайте создадим такую библиотеку. Для этого просто внесем ряд модификаций в Makefile: CPPC = wpp386 CC = wcc386 INC = -I. -I$(SOMBASE)\include SC = $(SOMBASE)\bin\sc SCFLAGS = LINKER = wlink LDFLAGS = LIBLIST = $(SOMBASE)\lib\somtk.lib all: $(SOMBASE)\include\somxh.bld test.exe testc.exe testdll.exe test.exe: test.obj demo.obj $(LINKER) $(LDFLAGS) file demo.obj file test.obj library $(LIBLIST) name test.exe test.exe testc.exe: testc.obj demo.obj $(LINKER) $(LDFLAGS) file demo.obj file testc.obj library $(LIBLIST) name testc.exe testc.exe testdll.exe: testdll.obj demo.lib demo.dll $(LINKER) $(LDFLAGS) file testdll.obj library $(LIBLIST) library demo.lib name testdll.exe testdll.exe demo.dll: demodll.obj demoinit.obj demo.def $(LINKER) format os2 lx dll initinstance $(LDFLAGS) file demodll.obj file demoinit.obj library $(LIBLIST) name demo.dll export DemoCClassData, DemoClassData, DemoNewClass, SOMInitModule demo.lib: demo.def implib $@ $[ clean: .symbolic -del *.dll *.def *.lib *.err *.obj *.exe *.map *.xh *.ih *.h $(CLEANFILES) >nul 2>&1 $(SOMBASE)\include\somxh.bld: @echo Данные пример требует файлов заголовков C++ @echo созданных с помощью команды somxh. @exit 1 testc.obj: test.c demo.h $(CC) $(INCLUDEPATH) $(CCFLAGS) $[ -fo=testc.obj testdll.obj: testdll.c demo.h $(CC) $(INCLUDEPATH) $(CCFLAGS) $[ test.obj: test.cpp demo.xh $(CPPC) $(INCLUDEPATH) $(CPPCFLAGS) $[ demo.obj: demo.c demo.ih $(CC) $(INCLUDEPATH) $(CCFLAGS) $[ demodll.obj: demo.c demo.ih $(CC) $(INCLUDEPATH) $(CCFLAGS) -bd $[ -fo=demodll.obj demoinit.obj: demoinit.c demo.ih $(CC) $(INCLUDEPATH) $(CCFLAGS) -bd $[ demo.ih: demo.idl demo.h $(SC) -u -sih $(SCFLAGS) $[ demo.h: demo.idl $(SC) -sh $(SCFLAGS) $[ demo.xh: demo.idl $(SC) -sxh $(SCFLAGS) $[ demo.def: demo.idl $(SC) -sdef $(SCFLAGS) $[ В результате вы получите библиотеку demo.dll с нужным классом, а в testdll.exe будет пример использования данной библиотеки. Отметим, что для создания библиотеки достаточно экспортировать всего 3 (!) объекта, чтобы обеспечить доступ ко всему классу. В случае использования в DSOM или ObjectREXX вам потребуется добавить еще одну функцию, которая будет регистрировать класс (опять же, требуется не во всех случаях). Кроме того, для приложений DSOM и ObjectREXX вам потребуется внести данные о классе в репозиторий интерфейсов (подробнее - в следующих выпусках). Внести данные о классе в репозиторий интерфейсов можно с помощью ключа -u компилятора SOM и эмиттера IR. Довольно просто вызов классов и создание объектов осуществляется из ObjectREXX: /* Боббик - лучший пес всех времен и народов */ parse version . rxlevel . if rxlevel<='4.00' then do say 'Для выполнения данного скрипта необходим ObjectREXX' exit end /* Создаем объекты */ a=.Demo~new b=.Demo~new /* Устанавливаем атрибуты */ a~_set_DemoWord('123') b~_set_DemoWord('321') /* Вызываем */ a~sayDemoWord b~sayDemoWord /* Объекты уничтожать не надо, REXX сам их удалит */ /* Импортирование класса SOM */ ::Class Demo Public EXTERNAL 'SOM Demo' Файлы с примерами для OpenWatcom в этом архиве.
В следующем выпускеМы обзорно познакомимся с базовыми компонентами и классами SOM. Также мы попробуем создать простейший класс доступа к файлам INI и оформить его в виде библиотеки классов. Дополнительная информация:
Комментарии:
|
|
|||||||||||||||||||||||||||||||
(C) OS2.GURU 2001-2021