О ПРОГРАММНОЙ ПОДДЕРЖКЕ
163
о программной поддержке численного
экспериментирования
В. В. Таркаев
Челябинский государственный университет
В статье дано описание технологии разработки программных систем, предназначенных для проведения больших количеств вычислительных экспериментов и обработки их результатов. Приведен перечень требований, предъявляемых математиком, проводящим вычислительные эксперименты, к пользовательскому интерфейсу таких систем. Дано также описание системы ОЯСИЕЕК, реализующей эту технологию, обеспечивающей удобную работу с данными и "мозаичную сборку" вычислительного процесса.
При разработке программного продукта значительная доля труда уходит на создание отвечающего современным требованиям интерфейса и решение некоторых других чисто технических проблем (организация параллельных процессов, работа с большими объемами данных, графика и т.д.), что отвлекает силы от решения основной задачи. Сейчас существует много различных систем и библиотек, в той или иной мере берущих этот труд на себя, но их использование сложно для человека, не являющегося профессиональным программистом.
В Челябинском государственном университете интенсивно ведутся работы по разработке нового семейства методов негладкой и разрывной оптимизации, использующих аппроксимационный градиент. В этой работе большое значение имеют численные эксперименты (один метод на разных функциях, разные методы на одной функции, изменение параметров метода или входящих в него вычислительных процедур), причем эти эксперименты должны проводиться постоянно и в большом количестве. В этой ситуации существенную роль приобретают инструментальные средства, которыми математик пользуется в ходе подготовки, проведения и обработки результатов. В связи с этим начата разработка программной системы, специально предназначенной для создания комфортных условий программирующему алгоритм математику и математику-исследователю.
1. Введение
Цикл любых математических исследований, в которых реализация на ЭВМ играет существенную роль, с известной долей условности можно разбить на три этапа: во-первых, чисто теоретическое исследование, во-вторых, реализация некоторых процедур на каком-то языке программирования и отладка (устранение ошибок программирования) и, в-третьих, численное эксперимен-
164
В. В. ТАРКАЕВ
тирование, в ходе которого проверяется правильность реализации алгоритма и самого алгоритма, его работоспособность в различных ситуациях и т п.
Понятно, что в различных исследованиях соотношения значимостей этих трех этапов может сильно различаться. Предлагаемая технология разработки и средства ее инструментальнг 1 поддержки (система ОЗСЯЕЕН) ориентированы на проекты, в которых третий этап имеет особое значение, разумеется, уступающее значению теоретического исследования.
Прежде всего, перечислим некоторые проблемы, которые могут возникнуть у математика на этапе программирования, и некоторые потребности, возникающие у него на этапе численного экспериментирования Перечень этот одновременно будет и перечнем инструментальных средств поддержки труда математика-исследователя и описываемой технологией, в рамках которой они разрабатываются.
2. Численное экспериментирование
Прежде всего, поговорим об этапе численного экспериментирования, которому, как уже отмечалось, отводится особое значение.
Разумеется, численные эксперименты в различных исследованиях различны. Однако эксперимент по нахождению экстремума некоторой функции при помощи некоторого метода, по-видимому, достаточно типичен. Полное описание интересующего математика процесса включает в себя десятки параметров различной природы. В данном случае это, прежде всего, указание минимизируемой функции: указание либо ее имени, если она входит в стандартный набор, либо путем задания аналитического выражения или даже нескольких выражений для кусочно-аналитической функции или наличия ограничений. Кроме того, надо задать начальную точку, выбрать из перечня метод и, возможно, еще и его модификацию Но и это еще не все Различными могут быть процедуры выбора направления очередного шага и его величины. Наконец, представляет интерес сравнение между собой различных реализаций одних и тех же алгоритмов и процедур.
Таким образом, одни из инициализационных параметров есть числа — целые, вещественные или векторы, другие суть выбор одного из элементов некоторого перечня (выбор может быть и многоуровневым), третьи — подлежащие интерпретации формулы.
Кроме того, при проведении вычислительного эксперимента необходимо определить, какая именно информация о процессе интересует исследователя на этот раз (это вновь контекстно зависимый выбор), куда именно она должна выдаваться (на экран, в текстовый или бинарный файл) и в каком виде она должна представляться Минимальный набор форм представления в нашем конкретном случае, то есть в случае численного эксперимента по нахождению минимума функции, включает в себя:
- текстовый протокол хода процесса, возможно, несколько различающихся по подробности или по набору выводимой информации его вариантов,
О ПРОГРАММНОЙ ПОДДЕРЖКЕ
165
- графики вещественнозначных функций одного переменного,
- изображение вещественнозначных функций двух переменных либо при помощи линий уровня, либо как двумерных поверхностей в трехмерном пространстве,
- изображение траектории процесса поверх линий уровня
В заключение перечисления потребностей проводящего численный эксперимент математика отметим, что кроме различных способов задания инициализирующих процесс параметров и мозаичной, так сказать, сборки процесса, кроме определения (и, возможно, изменения по ходу дела) набора наблюдаемых параметров и формы их визуализации, в целях повышения эффективности и комфортности своего труда экспериментатор нуждается, как минимум, еще и в следующем
- в возможноеiи запуска одного или более процессов параллельно с удобным для сравнения выводом результатов (например, наложение графиков),
- в возможности запоминания текущих результатов в базе результатов, а также в средствах просмотра старых и новых результатов и средствах обслуживания базы (удаление, добавление, выборка по каким-то признакам — дата, использованный метод и т п — подготовка содержащейся в базе информации для печати и сама печать),
- в поддержке системой сеансов работы, то есть при окончании работы состояния системы с восстановлением её в начале следующего сеанса,
- в возможности формировать экран в соо гветствии со своими привычками и текущими потребностями, это, прежде всего, возможность перемещения окон по экрану и изменения их взаимного расположения, возможность уменьшать и/или удалять второстепенные окна и, наоборот, увеличивать наиболее в данный момент интересные, подбирать по своему вкусу цвега и т п
3. Потребности программирующего математика
Приведем требования, которым должен удовлетворять внутренний программный интерфейс системы для обеспечения удобной работы программирующего математика
- Набор понятий, необходимых для освоения программного интерфейса си стемы, должен быть невелик, причем сами понятия должны, во-первых быть максимально приближены к тем, в которых мыслит программирующий математик, и, во вторых, в максимальной степени маскировать технику реализации той или иной возможности
166
В В ТАРКАЕВ
Возможность программировать "абы как" Речь здесь идет о следvющем человек осваивает некоторый минимальный набор предоставляемых си стемой возможностей, например, относящиеся к выводу результатов сче та, но не желает осваивать остальные и, что более существенно, раз бираться с теми требованиями, которые предъявляет система к стилю написания и структуре программы Не желая ничего этого знать, он имеет возможность создать работоспособную программу, быть может, не вполне с точки зрения системы полноценную Например, результаты выводятся и сохраняются нормально, но изменить какой-либо из описы вающих процесс параметров во время работы программы невозможно, как и запустить его параллельно с другим
- Возможность с минимальными проблемами изготовлять исполняемые файлы, могущие нечто вычислять или демонстрировать вне сисгемы Идеалом в этом отношении было бы обходиться перекомпиляцией и лин ковкой с другими библиотеками
- Обеспечение переносимости написанного математиком текста на, альгер нативные IBM PC и MS DOS аппаратные и программные платформы В идеале, система должна полностью маскировать от математика то, что зависит от платформы
- Возможность программирования каждою из входящих в математическое наполнение системы объектов (процедура вычисления аппрокгимацион ного градиента, тестовая задача и т п ) так, как будто никаких других объектов в системе нег Иначе говоря, должна быть возможность разде лить собственно программирование объекта и описание его лог ических взаимосвязей, подчас весьма нетривиальных, с друг ими объектами
4. Внутренний программный интерфейс системы
Перейдем теперь к обсуждению внутреннего программного интерфейса си стемы, то есть к той ее части, с которой имеет дело математик на этапе про граммирования, интерфейс же математика-экспериментатора, по-видимому, достаточно описан перечнем требований к нему и более обсуждаться не бу дет
Программный интерфейс системы ОБСЯЕЕИ состоит в основном из двух групп функций одна предназначена для организации ввода-вывода данных, другая — для обеспечения "сборки" запускаемого процесса из нескольких содержащихся в системе объектов Обязательно использование лишь первой О ней и поговорим прежде всего
О ПРОГРАММНОЙ ПОДДЕРЖКЕ
167
4.1. Организация ввода-вывода данных
Основным понятием при организации ввода-вывода в системе OSCREEN является понятие метафайла данных (МФД) МФД есть сущность чисто логическая, объект в смысле объектно-ориентированного программирования Это совокупность команд (функций), достаточная для ввода и вывода информации определенного рода МФД не является облегчающей работу надстройкой над некоторым определенным носителем информации, будь то обычный файл или пользователь. Решение о том, куда и в какой форме вывести посылаемую в МФД информацию и откуда взять запрашиваемую, принимается системой самостоятельно в зависимости от текущего режима работы, разумеется, за исключением тех случаев, когда команда открытия МФД не содержала явных на то указаний.
Подчеркнем сразу же, что подобные указания, уместные и, возможно, даже необходимые при создании самос гоятельного исполняемого файла, могут осложнить тот режим работы экспериментатора, который был описан выше (п.2) и для обеспечения которого прежде всего и предназначена предлагаемая система
Каждому базовому типу информации соответствует тип МФД, и, следовательно, их набор зависит от области математических исследований. В нашем случае этот набор включает.
- Диалог (инициализация и протокол работы).
- Вещественнозначную функцию натурального аргумента (значения характеристик итерационного процесса в зависимости от номера итерации)
- Многомерную функцию натурального аргумента N —> R" (ориентированная ломаная — траекюрия итерационного процесса).
- Вещественный функционал Я" —* R (например, сама оптимизируемая функция) Эта разновидность МФД активно используется самой системой для упрощения и ускорения рисования, но пользователю она, по-видимому, не понадобится
Проиллюстрируем на примерах основные принципы работы с МФД Пе ред использованием, подобно обычному файлу, МФД должен быть открыт при помощи функции open_mfd_XXXX() (где ХХХХ — тип открываемого МФД, а в конце закрыт при помощи close_mfd(). При открытии можно указать конкретный носитель информации, что, как отмечалось выше, уместно лишь при разработке предназначенных для эксплуатации вне системы модулей
Приведем фрагмент программы, которая считывает из МФД dialog вещественное основание степени х и натуральный показатель п, которые хранятся в МФД под именами "основание степени" и "показатель степени", вычисляет значение выражения х в степени п и заносит этот результат в МФД под именем "результат".
168
В. В. ТАРКАЕВ
/* Откроем МФД */
mfd_dialog dialog = open_rafd_dialog ();
/* Считаем из МФД dialog необходимые данные */ double х = dialog ("основание степени" ); int n = dialog ("показатель");
/* Возведем х в степень п с помощью функции возведения в степень */ double result = pow ( х, n );
/* Занесем результат в МФД */ dialog ("результат") = result;
/* И закроек диалог */ mfd_close ( dialog ).
Ввод данных обеспечивается перегруженными операторами преобразования типа и оператором "круглые скобки''. Вывод обеспечивается перегрузкой оператора присвоения и оператора "круглые скобки".
Обратим внимание, во-первых, на то, что используемые при вводе и выводе символьные строки, делая текст программы более читаемым, играют к тому же роль идентификаторов при хранении текста запроса в диалоге, используются они и при создании протоколов работы; во-вторых, на то, что ввод и вывод величин всех допустимых в OSCREEN типов осуществляется с использованием простых конструкций. Для ввода это.
вв одимая „ из _mfd__ величина - HM.«_mid ( идентификатор ); а для вывода:
HM*_mid ( идентификатор ) = выводимая_в_т:М_величина.
Работа с МФД других типов организована аналогично, с той лишь разницей, что вместо символьных строк для идентификации данных используются параметры, тип и число которых определяются тем, на каком именно пространстве задана соответствующая этому типу МФД функция
На этом обсуждение МФД, обеспечивающего ввод и вывод данных в системе OSCREEN, завершено. Отметим еще лишь то, что его освоение является первой стадией освоения системы в целом На этом этапе OSCREEN может восприниматься просто как еще одна библиотека функций. Хотя следует помнить, что использование параллельно с ней других библиотек ввода-вывода может привести в некоторых ситуациях к конфликтам, как это нередко случается при использовании одновременно нескольких занимающихся одним и тем же программных средств.
4.2. Интеграция модуля в систему ОБСЕЕБЫ.
Вторая стадия с точки зрения программирования от первой не отличается
О ПРОГРАММНОЙ ПОДДЕРЖКЕ
169
Однако речь в ней идет уже о создании не самостоятельного исполняемого файла, а одного из составляющих математическое наполнение системы модулей Действия, которые следует предпринять для интеграции модуля в систему, носят в основном "организационный" характер При помощи специальных диалоговых процедур или заполнения специальных текстовых файлов в систему должна быть внесена информация о том, с каким пунктом меню он ассоциирован (в случае необходимости добавить новый), к какой категории объектов он относится (чтобы он фигурировал в соответствующих списках), с ним должен быть связан текст, содержащий описание модуля для экспериментатора, с одной стороны, и для системного администратора — с другой Кроме этого, к модулю должны быть добавлены некоторые библиотеки, которые обеспечат корректное взаимодействие модуля с системой
После интеграции модуля в систему он может быть запущен через меню 0SCREEN, а если ввод и вывод данных в нем реализованы при помощи МФД, то система возьмет на себя хлопоты по обеспечению выбора экспериментатором источника входных данных, их считывание из файла (или запрос с терминала), а также по визуализации того из выводимого, что экспериментатор хочет видеть, и сохранению того, что он хочет сохранить, кроме того, благодаря возможности перехвата системой управления в момент обращения к МФД, система может обрабатывать некоторые команды экспериментатора (перемещение окон, изменение их размеров и т п )
Переход от второй стадии к третьей проиллюстрируем на примере
int main () {
/* инициализировали, что следовало, в том числе открыли МФД*/ mit ();
/* пока не выполнено условие остановки,делаем следующий шаг
итерационного процесса */ while ( 1end_oi.process () ) make_next_step ();
/* вывели, что следовало, и закрыли МФД */ terminate ();
return 0; >
Суще».твсиии в этом примере то, что главный цикл, находясь внутри написанной пользователем части, системе недоступен Система может запустить процесс Но она не может, например, его корректно прервать, для этого ей должна быть доступна функция termmateO Весьма непросто (если вообще возможно) в такой ситуации обеспечить пошаговую синхронизацию двух запущенных параллельно процессов Перечень подобных проблем можно было бы и продолжить Все они исчезнут с устранением их общей причины — главный цикл должен находиться вне пользовательской программы, то есть внутри системы Но там он не сможет работать без "знания" системой необходимых
170
В. В. ТАРКАЕВ
для управления модулем функций. Что, в свою очередь, невозможно, если информация об именах, типах возвращаемых величин и списках параметров не внесена в систему предварительно. А это влечет типизацию объектов в том, разумеется, случае, когда не имеется в виду модификация ядра системы для добавления каждого нового пользовательского модуля.
При таком подходе программа расчета из последнего примера просто превратилась бы в совокупность четырех тех же самых функций (Лпз^О, еп<1_о1_ргосв8з(), !»аке_пех1;_згврО, t6rm:mate()), но без функции aai.nO. Разумеется, это верно только при том условии, что систему предварительно "научили" управлять объектами такого типа, то есть у нее внутри реализован некоторый похожий на выше приведенный управляющий цикл.
На разработчика в этом случае налагаются определенные ограничения. Во-первых, он, реализуя объект определенного типа, обязан реализовать все входящие в его интерфейсную часть функции, причем с соблюдением не только типов параметров и возвращаемых величин, но и их смысла. Например, функция еп<1_о1_ргосез8() из последнего примера должна возвращать 1, если процесс может быть продолжен, и 0 в противном случае.
Итак, реализация программы как допустимого в системе ОЗСЯЕЕК объекта, в добавление к поддержке ввода-вывода данных, позволяет обеспечить еще и полноценное управление вычислительным процессом со стороны системы.
4.3. Мозаичная сборка приложения
Осталось описать вторую группу составляющих программный интерфейс ОБСНЕЕИ функций, предназначенных для обеспечения возможности "мозаичной" сборки запускаемого экспериментатором процесса.
Соответствующую технологию проиллюстрируем на примере. Предположим, что решаемая задача — это нахождение минимума некоторого функционала при помощи некоторого итерационного процесса, в котором могут быть использованы различные процедуры нахождения градиента. И пусть эксперименты состоят в переборе различных четверок — функционал, метод, нулевая итерация, градиент. Вернее, в сравнении каких-то характеристик процессов, этими четверками описываемых. Пусть, для начала, среди этих четверок недопустимых нет.
Рекомендуемый образ действий в этой ситуации должен состоять в следующем: при помощи процедуры, принцип которой будет описан в следующем разделе, должно быть обеспечено взаимодействие ядра системы ОЗСЛЕЕН с объектами четырех типов. Под объектом в ОЗСЯЕЕИ будем понимать логически самостоятельную программную единицу, взаимодействующую с ядром системы через описываемый набор интерфейсных функций; причем доступ к объекту как со стороны системы, так и со стороны других объектов осуществляется исключительно через эти интерфейсные функции.
О ПРОГРАММНОЙ ПОДДЕРЖКЕ
171
объект process :< void init exterminate (); int end_of„process (); void make_next_step (); id_type get_id ();
объект metod: void init (); void terminate ();
void set_null_iteration ( vector xO ); void make_next_step (); id_type get_id ();
объект function: void init (); void terminate (); double f ( vector x); id.type get_id ();
объект gradient: void init (); void terminate (); vector grad ( vector x ); id_type get_id (),
Функции с именем get_id() предназначены для ассоциирования объектов с определенными пунктами с о о т в о тс т ву ю ще i о данному типу объектов дерева меню. Каждому типу объектов соответствует дерево. А функция get_id() возвращает специального вида идентификатор, содержащий полную информацию о месте данного объекта в э.том дереве. Смысл остальных функций достаточно очевиден.
Объект function ни в каких других объектах не нуждается. Иначе дело обстоит с другими объектами. Для gradient требуется function. Для method нужны gradient и function. A process нуждается во всех.
Пусть известно, что экспериментатор намерен запустить вычислительный процесс, реализуемый объектом process. Система начнет с вызова относящейся к нему функции mit(), которая в нашем случае должна содержать следу-, ющие три строки:
load_method(); load_function() ; load_gradient().
172
В. В. ТАРКАЕВ
Подобные функции, смысл вызова которых состоит в передаче системе требования загрузить объект определенного типа, пишутся не математиком, а системным программистом, в обязанности которого, в частности, входит разработка специальных служебных объектов, обеспечивающих взаимодействие ядра системы с разрабатываемыми математиком объектами. Отсутствие параметров при вызове этих функций означает, что выбор, какой именно из объектов требуемого типа загружать, должен быть решен системой самостоятельно Например, путем предъявления экспериментатору соответствующего дерева меню. Аналогично в функции initQ объекта method могут стоять строки
load_gradient (); load_function (),
А в функции init() объекта gradient — load_function (),
В тех случаях, когда данный объект (например, gradient) может взаимодействовать не со всеми объектами требующегося ему типа (iunction), в вызове соответствующей функции семейства load должны фигурировать параметры, несущие информацию об этих ограничениях. И тогда экспериментатору будет показано не полное дерево, а лишь удовлетворяющая ограничениям его часть.
5. Технические решения, положенные в основу системы 0SCREEN
В этом разделе будут в общих чертах описаны технические решения, принятые при реализации системы OSCREEN Точнее говоря, речь пойдет о реализации описанного выше ее программного интерфейса и принципах организации взаимодействия ядра системы с создаваемыми пользователем объектами и объектов между собой.
5.1. Реализация МФД
Вновь начнем с МФД. В нем активно используются приемы объектно-ориентированного программирования, в частности, возможное в С++ переопределение операторов и преобразование типов. Основу интерфейса составляет семейство классов mfd_dialog, mfd_n_r и т.д., соответствующих типам МФД. Для иллюстрации используемых приемов приведем частично определения классов mi d_dialog и mfd_n_r-
class mfd_dialog { public:
О ПРОГРАММНОЙ ПОДДЕРЖКЕ
173
/* группа операторов, служащих для вывода
и срабатывающих в ситуации mfd(...) = output_value;*/
int operator = ( int output_value ); float operator = ( float output„value ); double operator = ( double output_value ); vector operator = ( vector output_value );
/* группа операторов, служащих для ввода
и срабатывающих в ситуации input_value = mid(...) */ operator int (); operator iloat (); operator double (); operator vector ();
/* для передачи МФД текста, идентифицирующего вводимую или выводимую величину */
mfd_dialog& operator () ( char *text );
/* Функции для открытия и закрытия mfd_dialog */
friend mfd_dialog open_mfd_dialog ( ); friend int close_mfd ( mfd_dialog & ); },
Принцип работы объясним на словах. Начнем с ввода. Пусть программа содержит следующие строки:
/* Строка 1 */ mfd_dialog dialog = open_mfd_dialog (); /* Строка 2 */ int dim = dialog ( "размерность" ); /* Строка 3 */ double г = dialog ( "радиус" ).
Исполнение второй строки будет происходить следующим образом: сначала вызовется перегруженный в mfd_dialog operator О (char*), который запомнит символьную строку, поданную ему на вход и идентифицирующую требуемую информацию. Этого уже достаточно для поиска соответствующей строки в файле (если данные берутся из текстового файла) или для формулирования запроса экспериментатору. Затем будет вызван operator int(), благодаря чему станет известен тип запрашиваемой величины, то есть содержащая ее символьная строка может быть проверена на корректность и последующее преобразование строки в данном случае в int. В случае некорректности возможен повторный запрос экспериментатора.
При исполнении третьей строки после запоминания символьного идентификатора (operator() (char*)) будет вызван operator doubleQ, что позволит произвести проверку корректности и преобразование к типу на этот раз double.
174
В. В. ТАРКАЕВ
Понятно, что для обеспечения ввода векторов, комплексных чисел или любых других используемых математиками типов достаточно реализовать в mfd_dialog операторы преобразования к этим типам
Принцип реализации вывода данных очень похож на только что описанный Единственное отличие состоит в том, что при исполнении строки
dialog ("Число вычислений функции") = ш,
сначала будет вызван operatorü, а уж затем соответствующий типу переменной mm operator=().
Классы, соответствующие другим типам МФД, во многом аналогичны mfd_dialog. Вот, например, как выглядит определение класса mfd_n_r, представляющего собой последовательность вещественных чисел (некоторые детали здесь опущены):
class mfd_n_r { public:
operator double (); /* служит для ввода элементов последовательности */
double operator = ( double output_element ); /* служит для вывода элемента последовательности */
mfd_n_rfc operator О ( unsigned n ); /* служит для передачи номера вводимого или выводимого элемента последовательности */
mfd_n_rft operator « ( double input_element );/* служит для вывода следующего элемента последовательности */
mid_n_r operator » ( double output„element );/* служит для ввода следующего элемента последовательности */
/* Функции для открытия и закрытия mfd_n_r */
friend mfd_n_r open_mfd_n_r ( ); friend int close_mfd ( mfd_n_r & ); >;
Первые три оператора полностью аналогичны выше описанным операторам. Единственное отличие состоит в том, что текстовые идентификаторы замены номерами элементов. Появление же двух последних обусловлено тем, что ситуация последовательного вывода или ввода элементов последовательности является типичной. Передача номера в этом случае делается излишней, и введение специальных операторов является одним из возможных решений.
О ПРОГРАММНОЙ ПОДДЕРЖКЕ
175
Классы, соответствующие другим типам МФД, "устроены" аналогично. Отличаются они наличием обязательных параметров у функции создания класса (например, размерность для пгё<1_п_Г11 — последовательность векторов в К"), набором операторов преобразования типов и операторов присвоения, а также набором аргументов у оператора (). В некоторых присутствуют специфические информационные функции ( длина последовательности, размерность пространства и проч.).
5.2. Служебные объекты
Назначение второй группы составляющих программный интерфейс 08С11ЕЕЫ функций — семейства 1оа<1_объвкт — будет понятно после объяснения принципов организации системы в целом.
В зависимости от исполняемых функций объекты, составляющие систему ОЗСЯЕЕИ, можно разделить на четыре группы:
- пользовательские объекты;
- набор средств ввода, вывода и обработки информации;
- коммуникационные объекты, обеспечивающие взаимодействие пользовательских объектов между собой и ввод-вывод данных;
- ядро системы/
Пользовательские объекты или, точнее говоря, объекты пользовательских программ в соответствии с изложенными в первом разделе требованиями не зависят от операционной системы и аппаратного обеспечения. Это становится возможным благодаря тому, что они "общаются" исключительно с коммуникационными объектами системы, в которых специфика аппаратной и программной среды учтена. Даже обращение к интерфейсной функции другого пользовательского объекта на самом деле есть замаскированный вызов функции соответствующего коммуникационного объекта. Достигается это благодаря специальной технологии подключения пользовательских объектов к системе.
Состоит эта технология в следующем: для добавления к системе нового типа пользовательских объектов необходимо разработать четыре типа служебных объектов:
- Управляющий объект осуществляет инициализацию, которая включает в себя диалоговые процедуры, если это требуется. Он же будет осуществлять управление объектом в тех случаях, когда это требуется (объекты некоторых типов, например вычисляющие градиент, в управлении со стороны сис темы не нуждаются. Их назначение состоит в исполнении некоторых действий или расчетов по требованию других объектов). В функции управляющего объекта также входит терминирование отработавших объектов подчиненных ему типов.
176
В. В. ТАРКАЕВ
- Внутренний интерфейс в основном состоит из функций, дублирующих интерфейсные функции объекта. Содержательная часть исполняемых ими действий состоит в вызове соответствующих им функций самого объекта. Обоснование такого дублирования будет дано чуть ниже.
- Удаленный интерфейс — еще один набор функций, дублирующих интерфейсные функции объекта. Отличие его от предыдущего состоит в том, что этот набор "входит в состав" других пользовательских объектов, которым он требуется. И именно функции из этого набора вызываются из других пользовательских объектов. Таким образом, с них начинается та цепочка, в конце которой стоят сами интерфейсные функции.
- Коммутат -р — центральное звено этой цепочки. Все взаимодействие объекта с "внешним миром" проходит через него. На нем, в частности, лежит обеспечение одновременного функционирования нескольких объектов одного типа в случае запуска экспериментатором параллельных процессов, проинициализированных различными исходными данными.
Проиллюстрируем оправданность такой организации на примере реализации системы под MS WINDOWS.
Помещать все варианты всех объектов в один ехе-файл нецелесообразно, так как это не только сделает его очень большим, но и потребует необходимой перекомпоновки всей системы при изменении лишь малой ее части (например, при тестировании пользовательской программы), что неудобно и долго. С этой точки зрения довольно естественным представляется разработка пользовательских объектов как самостоятельных ехе-файлов или dll-файлов. Но тогда в работе одного вычислительного процесса будут участвовать несколько ехе или dll-файлов, причем должна быть обеспечена возможность вызова из одного функций другого. Разумеется, эта проблема разрешима. Но разрешение это налагает некоторые требования и на вызываемые, и на вызывающие функции. Например, вызываемая функция обязательно должна быть FAR PASCAL. Кроме того, любая Windows-программа должна содержать функцию WinMain. И то, и другое обязательно, причем обязательно только в MS WINDOWS. Что в соответствии с требованием независимости текста пользовательской программы от операционной системы делает оправданным существование объектов типа внутренний ин^рфейс.
В пользу существования удаленного интерфейса приведем следующий аргумент. Одним из сформулированных требований к системе была возможность запуска нескольких процессов параллельно. В этом случае будет запущено по несколько экземпляров участвующих в процессе ехе-файлов. При этом "общаться" должны ехе-файлы, относящиеся к одному экземпляру процесса. Одним из способов обеспечения этого может быть хранение в вызывающей функции некоторого дескриптора вызываемого объекта. Природа этого дескриптора и способ получения с его помощью доступа к самому объекту опять-таки зависят от операционной системы. В WINDOWS это может быть HANDLE главного окна вызываемого приложения, а в MS DOS, например, просто ука-
О ПРОГРАММНОЙ ПОДДЕРЖКЕ
177
затель на соответствующий экземпляр класса. Но при любом способе вновь возникает необходимость учета специфики операционной системы и использование некоторой специфической техники программирования.
Объект коммутатор необязательно является самостоятельным \УШОО\¥8-приложением. Он может входить в управляющий объект в качестве функционально автономной части, и первичен именно он При необходимости запустить некоторый процесс ядро запускает соответствующий ему коммутатор. Коммутатор запускает главный управляющий объект. Затем все требования загрузки других объектов, требующихся для этого процесса, проходят через коммутатор, что позволяет ему иметь полный список дескрипторов объектов, относящихся к этому процессу. Это не только позволит избежать повторной загрузки одного объекта в рамках одного процесса (в примере из предыдущего раздела функция Хоа^^псЬа.опС) стояла в инициализационных функциях трех использующих ее объектов), но и обеспечить загрузку не в порядке поступления требований, а в порядке, избранном экспериментатором, с последующей проверкой, все ли объекты загружены. Кроме этого, через коммутатор проходят требования открыть МФД, что делает доступной информацию о том, какие МФД с какими экземплярами каких процессов связаны. А это делает возможным обеспечение, например, корректного отображения информации о параллельных процессах и ее правильное сохранение.
Из выше сказанного следует, что коммутатор оказывается нетривиальным лишь для главных объектов процесса. Для всех остальных отделение его от управляющего объекта, по-видимому, неоправдано.
6. Заключение
Подведем итоги. Описанная технология при условии ее соблюдения программирующим математиком предоставляет ему все те возможности, о которых шла речь выше, кроме одной (о ней чуть позже), перекладывая практически все технические трудности на разработчиков ядра системы ОЭСЙЕЕМ и системных программистов, обеспечивающих взаимодействие ядра с разрабатываемыми математиком типами объектов.
Программный интерфейс системы опирается на два понятия' МФД и ОЗСЯЕЕМ-объект и состоит из крайне незначительного числа функций. Все это позволяет надеяться на то, что освоение и использование ОБСЙЕЕН не будет связано со значительными трудностями для человека, обладающего некоторыми навыками программирования на С++(или С)
В заключение остановимся на проблеме вычленения собственно алгоритма из использующей ОЗСЯЕЕЯ программы. Разумеется, единственный способ ее решения состоит в соблюдении программистом определенной дисциплины программирования Один из возможных способов достижения этой цели в данном случае может состоять в соблюдении следующего очевидного правила, оформление той части пользовательской программы, которая содержит сам алгоритм как набор функций, полностью реализующих алгоритм и не содержащих ни одной не относящейся к алгоритму строки. Тогда как оставшаяся часть
178
В И УХОБОТОВ
программы напротив, должна не содержать ни одной строки, относящейся к алгоритму Если говорить упрощенно, то вторая часть пользовательской программы должна состоять только из вызовов интерфейсных функций ОБСВ-ЕЕЯ, передающих системе требования загрузить тот или иной требующийся для работы данного алгоритма объект, открывает и закрывает требующиеся МФД, обеспечивает ввод данных и вывод результатов, никаких вычислений в ней не г — только вызовы функций из первой части
Еще одно соображение в пользу именно такой структуры состоит в гам, что вторая часть, завися не столько о г алгоритма, сколько от типа реализуемого им процесса (тестовая функция, метод ошимизации и т п ), может полностью или с незначительными изменениями переноситься в другие аналогичные объекты А в случае полного совпадения вторая часть может быть "перемещена" в систему, точнее говоря в служебные объекты, обеспечивающие взаимодействие ядра с пользовательскими объектами