Главная страница Visual 2000 · Общий список статей
Для тех, кто не хочет расставаться с ФОРТРАН'омАндрей Колесов, Ольга Павлова
© 1991, Андрей Колесов, Ольга ПавловаПримечание автора. Эта статья написана очень давно и с некоторыми ее положениями автор сегодня не согласен. (Например, о том, что Фортран безнадежно устарел. Но ведь в начале 90-х инструментарий для Фортрана выглядел действительно довольно бледно по сравнению со средствами быстрой разработки.) Тем не менее, хотя здесь идет речь о связке Фортран-QB, основные подходы к реализации идей смешанного программирования сохраняют свою актуальность и более того — неплохо отражают ряд вопросов, которые нужно иметь в виду тем, кто, например, создает DLL-процедуры на Фортране.
Как обеспечить развитие и дальнейшее использование...
Еще несколько лет назад FORTRAN
От Фортрана лучше отказаться, но не спеша
Смешанное программирование — без нужды не увлекайтесь
Пример модернизации расчетного комплекса программ
И все-таки смешанное программирование: QuickBASIC-FORTRAN
Вызов FORTRAN-процедур из модулей QuickBASIC
1. Способ передачи аргументов
Пример смешанного программирования
2. Пересылка массивов
3. Соответствие типов аргументов
4. Выделение памяти для операции ввода-вывода
5. Компоновка загрузочного модуля
Заключение
ПРИЛОЖЕНИЯ
Как обеспечить развитие и дальнейшее использование всего накопленного багажа программных средств — прикладных программ, библиотек подпрограмм — при переходе с одних систем программирования к другим? Некоторые варианты решения этих проблем рассматриваются в данной статье на примере перехода от языка FORTRAN к QuickBASIC.
Еще несколько лет назад FORTRAN
Еще несколько лет назад FORTRAN был безусловно самым популярным языком программирования на ЕС и СМ ЭВМ. Наряду со всем известными недостатками, у него были и существенные достоинства, среди которых стоит отметить следующие:
На его основе созданы мощные комплексы расчетных задач, системы математического моделирования и т.д.
Пришло время персональных компьютеров и перед пользователями языка FORTRAN встал вечный вопрос: что делать? Как развивать ранее созданные программы и на чем вести разработку в дальнейшем?
От Фортрана лучше отказаться, но не спеша
Программистам, только что пришедшим работать на персональные компьютеры со своими FORTRAN-программами, в первую очередь, надо дать такой совет — не спешите переписывать на другие языки все свои, даже относительно небольшие, но уже отлаженные программы. У вас и так будет достаточно проблем с созданием новых!
Первый этап перехода с другой ЭВМ на персональный компьютер решается достаточно просто — исходные тексты переписываются на дискеты, потом на винчестер, перетранслируются — и готовый EXE-модуль готов к выполнению. Для этого лучше всего система Microsoft FORTRAN Compiler для MS-DOS, версии не ниже 4.0, которая полностью соответствует стандарту ANSI 77 FORTRAN.
Более того, функциональные возможности своих программ можно существенно расширить за счет использования библиотек подпрограмм, реализованных на персональных компьютерах. В качестве примера следует привести графическую библиотеку подпрограмм Enhanced Graphics Toolkit, демонстрационная программа которой выполнена именно на языке FORTRAN.
Но что делать дальше? Тривиальное решение — продолжать писать на языке FORTRAN и дальше — не совсем целесообразно по ряду причин. Кроме общего тезиса, что FORTRAN уже никак не соответствует современным представлениям о языке программирования, он имеет и ряд очень существенных практических недостатков. Среди них следует отметить слабые возможности реализации режима диалога, структурирования данных, работы с файлами произвольной структуры, управления памятью и пр. И может быть самое главное — отсутствие интегрированной среды разработки программ, вследствие чего затраты на разработку и отладку программ резко возрастают по сравнению современными системами типа Turbo или Quick.
Таким образом, переход к другому языку программирования практически неизбежен. Но при этом возникают две проблемы:
Смешанное программирование — без нужды не увлекайтесь
Здесь мы непосредственно подходим к вопросу о смешанном программировании — создании EXE-модуля программы из процедур, реализованных на разных языках. В связи с этим, я готов дать второй совет — в смешанном программировании нет ничего страшного и сверхестественного, но без особой необходимости связываться с этим не стоит.
Во-первых, проблемы стыковки будут постоянно возрастать по мере роста сложности программ — здесь и для одноязычных EXE-модулей хватает проблем. Во-вторых, разработка программы будет выполняться в среде основного языка, соответственно оперативные возможности отладки будут касаться только модулей, написанных на данном языке (остальные процедуры, реализованные на других языках, могут использоваться только в виде библиотек подпрограмм).
Поэтому вопросы, связанные с необходимым функциональным развитием готовых EXE-модулей, лучше решать не путем их непосредственной реконструкции и включения в них процедур на новом языке, а с помощью реализации многопрограммного комплекса, в котором каждая функциональный EXE-модуль создан средствами одного языка. На практике такой вариант часто осуществляется довольно просто, так как модернизация программных комплексов связана, в первую очередь, не с изменением ранее созданных расчетных алгоритмов, а с расширением сервисных возможностей системы — диалога, графической обработки, хранения данных в виде базы данных и т.д.
Пример модернизации расчетного комплекса программ
Реализацию такого подхода можно проиллюстрировать на примере создания программного комплекса ANALIT (геофильтрационные расчеты). Первоначально расчетная программа была создана на языке FORTRANе для ЕС ЭВМ (ANALIT-EC). Расчеты выполнялись в пакетном режиме, исходные данные готовились в виде текстового файла, результаты выдавались на печать в табличном виде (рис.1). Для повышения эффективности работы пользователя на IBM PC потребовалось существенно расширить сервисные возможности комплекса и реализовать:
В результате такой доработки был создан программный комплекс, структура которого приведена на рис. 2. Главный модуль системы написан на QuickBASIC, он обеспечивает выполнение всех функций диалоговой среды пользователя, а также ряд дополнительных операций обработки данных, — в частности, взаимодействия с графическим пакетом SURFER, при выводе результатов расчетов в виде карты изолиний. Взаимодействие с расчетным модулем производится с помощью временных символьных последовательных файлов .INP и .OUT. Таким образом, расчетный EXE-модуль, реализованный на языке FORTRAN, был интегрирован в новую версию системы с минимальными изменениями:
Представленный вариант реорганизации является достаточно характерным для расчетных программных комплексов, доставшихся нам в наследство от ЕС и СМ ЭВМ. Большинство из них было ориентировано на пакетный режим работы. Впрочем, если там и был реализован диалоговый режим, то скорее всего именно блоки диалогового взаимодействия будут корректироваться в связи с переходом на персональные компьютеры.
И все-таки смешанное программирование: QuickBASIC-FORTRAN
Так что же делать, если без смешанного программирования не обойтись? Если Вам не хочется переписывать огромное число расчетных подпрограмм, написанных Вами на языке FORTRAN (очень естественное состояние), или Вы этого просто не можете сделать, так как эти процедуры представлены в виде библиотек объектных модулей?
Итак, если Вы решили в качестве своего базового языка использовать QuickBASIC версии 4.0 и старше, Ваши проблемы могут быть решены достаточно просто. При этом оговоримся сразу, что основные положения вопроса смешанного программирования, которые будут обсуждаться ниже, относятся не только к стыковке с языком FORTRAN, но и с другими языками — Си, Паскалем, Ассемблером и пр.
Прежде всего, следует иметь в виду, что существует два варианта взаимодействия модулей: вызов FORTRAN-процедур в среде QuickBASIC и наоборот. Очевидно, в нашем случае нас гораздо больше интересует первый вариант — разработка новых программ будет связана в основном с созданием и отладкой новых BASIC-модулей и работа будет выполняться в среде QuickBASIC.
Необходимо также иметь в виду следующее важное замечание. В руководствах по языку QuickBASIC в разделах, посвященных смешанному программированию, всегда вначале говорится о стыковке с "другими языками", но все дальнейшее описание сводится к проблеме взаимодействия с C-процедурами. На самом деле работа с FORTRAN-процедурами несколько отличается от работы с C-процедурами.
Вызов FORTRAN-процедур из модулей QuickBASIC
В QuickBASIC существует два вида описания внешней процедуры:
DECLARE Statement (BASIC Procedures) — для BASIC-процедур;
DECLARE Statement (NonBASIC Procedures) — для процедур, написанных на
других языках.
Соответственно реализованы и два вида обращения к процедурам-подпрограммам:
CALL Statement (BASIC Procedures);
CALL, CALLS Statement (NonBASIC Procedures).
Для правильного использования той или иной формы вызова FORTRAN-процедур необходимо четко представлять способы передачи адресов аргументов из BASIC-программ.
При этом следует иметь в виду следующие правила:
В языке FORTRAN реализована передача аргументов по смещению или по полному адресу в зависимости от используемой модели памяти. Соответственно должны применяются два вида вызова FORTRAN-процедур:
Следует иметь в виду, что по умолчанию Microsoft Fortran Сompiler предполагает использование ключа /AL.
Таким образом, с учетом всего вышесказанного работа с внешними FORTRAN-процедурами на языке QuickBASIC выполняется следующим образом.
В языке BASIC для передачи аргумента-массива используется адрес блока управления массивом (там хранится адрес массива, его размерность), а при обращении к FORTRAN-процедурам — адрес массива. Поэтому при вызове FORTRAN-процедуры адрес массива передается с помощью его первого (физического!) элемента.
Пример вызова BASIC- и FORTRAN-процедур:
DECLARE BasicSub(In AS INTEGER, A() AS SINGLE)
DECLARE FortranSub(In AS INTAGER, A1 AS SINGLE)
DIM A(-2 TO 21) AS SINGLE
...
CALL BasicSub(I%,A())        ' вызов BASIC-процедуры
CALL FortrancSub(I%,A(-2))   ' вызов FORTARN-процедуры
...
Обратите внимание, что в операторе DECLARE для BASIC-процедуры второй аргумент описывается как массив, а для FORTRAN-процедуры — как простая переменная.
Процедура, которая получает адрес массива, не должна ни в коем случае выполнять обращения к языку BASIC, т.к. при этом может измениться адрес размещения данного массива, если он был описан как динамический.
3. Соответствие типов аргументов
Совершенно очевидно, что типы данных при описании передаваемых и получаемых параметров должны совпадать:
        QuickBASIC                FORTRAN
        ————————————————-
        INTEGER                   INTEGER*2
        LONG                      INTEGER*4 (по умолчанию)
        SINGLE                    REAL*4 (по умолчанию)
        DOUBLE                    REAL*8
Достаточно характерной ошибкой является ситуация, когда программист забывает, что описание данных в языках BASIC и FORTRAN несколько отличается. Особенно часто это происходит с целочисленными аргументами. Обратите на это внимание.
Определенные проблемы возникают при необходимости передачи символьных данных, так как дескриптор строки в языке BASIC, адрес которого передается при вызове внешней процедуры, не совместим с форматами строк в других языках. Наиболее простой выход в этом случае — представление символьных данных в виде численного массива. Например:
' Sym$ - символьная переменная
Symn$ = Sym$ + " "     ' новая переменная - выравнивание
                       ' до четного числа символов
LenM%=INT(LEN(Symn$) \ 2 )     'длина целочисленного массива
DIM SymInt%(1 TO LenM%)            ' резервируем массив
FOR i%=1 TO LenM%
   SymInt%(i%)=CVI(MID(Symn$,2*i%-1))    ' преобразование
NEXT i%
CALL FortranSub(SymInt%(1),LenM%)  ' вызов процедуры
....
Собственно говоря, в ранних версиях языка FORTRAN передача текстовых
переменных выполнялась именно таким образом.
4. Выделение памяти для операции ввода-вывода
При создании многоязычных программ возникают определенные проблемы при необходимости динамического распределения памяти. В частности, для FORTRAN-процедур это проявляется при использовании в них операций ввода-вывода. В этом случае при запуске EXE-модуля на выполнение выдается сообщение:
run-time error F6700 — heap space limit exceeded
Для решения подобных конфликтных ситуаций необходимо зарезервировать 4К байт памяти в COMMON-блоке с именем NMALLOC. Это делается включением в один из BASIC-модулей следующего фрагмента:
DIM Mallocbuf%(2048)
COMMON SHARED /NMALLOC/ Mallocbuf%()
Имя массива может быть любым, главное — его размер в байтах. При создании Quick-библиотеки эти строки необходимо поместить в один из модулей, входящих в данную библиотеку.
5. Компоновка загрузочного модуля
Компоновка загрузочного модуля выполняется с использованием библиотек QuickBASIC и опцией /NOE.
Пример смешанного программирования
Приведенные выше правила смешанного программирования проиллюстрируем на примере создания EXE-модуля вне среды QuickBASIC и при работе в среде интерпретатора. Программа состоит из главного модуля TEST_BAS.BAS, в котором производится обращение к внешней подпрограмме SubFort — модуль SUBFORT.FOR (см. текст программ, приведенный ниже). Подпрограмма SubFort выполняет вывод на экран значений полученных параметров и производит их корректировку. В главном модуле производится вывод параметров до и после вызова внешней подпрограммы.
В приведенном примере предполагается, что библиотеки QuickBASIC находятся в поддиректории LIB\, а все остальные используемые файлы — в текущей директории.
1. Создание EXE-модуля вне среды QuickBASIC
1) Компиляция исходного BASIC-модуля:
BC.EXE TEST_BAS.BAS /O/C:512
2) Компиляция исходного FORTRAN-модуля (/AL — большая (large) модель памяти):
FL.EXE /c /AL SUBFORT.FOR
3) Компоновка — создание EXE-модуля:
LINK.EXE TEST_BAS.OBJ+SUBFORT.OBJ,TEST_BAS.EXE,,LIB\ /NOE
В результате выполнения полученного модуля TEST_BAS.EXE на экран выдается:
TEST_BAS.BAS — значения переменных до обработки:
Integer1, Integer2, Single3 = 11   12   13
Массив Idim%(-1 to 8):-2  0  2  4  6  8  10  12  14  16
SUBFORT.FOR - значения полученных параметров:
Integer1, Integer2, Real3 =    11    12     13.000
Массив Idim(10):  -2    0    2    4    6    8   10   12   14   16
TEST_BAS.BAS — значения переменных после обработки:
Integer1, Integer2, Single3 = 11   102   103
Массив Idim%(-1 to 8): 41  42  43  44  45  46  47  48  49  50
Обратите внимание на следующие моменты:
Для работы в среде QuickBASIC внешние FORTRAN-процедуры должны быть записаны в библиотеки: .QLB — Quick-библиотеку и .LIB — библиотеку объектных модулей. Резервирование памяти для операций ввода-вывода FORTRAN-процедур выполняется вспомогательным модулем NMALLOC.BAS. Общая последовательность действий выглядит следующим образом:
1) компиляция вспомогательного BASIC-модуля:
BC.EXE NMALLOC.BAS /O/C:512
2) компиляция исходного FORTRAN-модуля:
FL.EXE /c /AL SUBFORT.FOR
3) создание объектной библиотеки с именем FOTR.LIB:
LIB.EXE FORT.LIB+NMALLOC.OBJ+SUBFORT.OBJ;
4) создание Quick-библиотеки с именем FORT.QLB:
LINK.EXE /Q FORT.LIB,FORT.QLB,,LIB\BQLB45.LIB+LIB\BCOM.LIB;
Очевидно, что в данной статье рассмотрены далеко не все варианты организации многоязычных программных комплексов и вопросы смешанного программирования. Скорее это рекомендации тем, кто еще не начал работать в этом направлении и находится в состоянии размышления о возможных вариантах модернизации своих программных комплексов. Поэтому автор с благодарностью воспримет любые отклики по этой актуальной проблеме и готов продолжить ее обсуждение со всеми заинтересованными лицами.
'***************************************************
'            Модуль TEST_BAS.BAS
'***************************************************
' Пример использования смешанного программирования
'         QuickBASIC - FORTRAN
''''''''''''''''''''''''''''''''''''''''''''''''''''
'  Резервирование памяти для корректной работы с
'  динамической памятью (этот фрагмент достаточно включить
'  только в один модуль .BAS или в Quick-библиотеку):
      DIM Manlocbuf%(2048)
      COMMON SHARED /NMALLOC/ Manlocbuf%()
'——————————————————————————
   DIM Idim%(-1 TO 8)
   FOR i% = -1 TO 8: Idim%(i%) = i% * 2: NEXT
   Integer1% = 11: Integer2% = 12: Single3 = 13!
   '
   PRINT "TEST_BAS.BAS - значения переменных до обработки:"
   PRINT "Integer1, Integer2, Single3 ="; Integer1%; Integer2%; Single3
   ' Значение Integer1% осталось неизменным
   PRINT "Массив Idim%(-1 to 8):";
   FOR i% = -1 TO 8: PRINT Idim%(i%); : NEXT: PRINT ""
   PRINT ""
      '
      ' Обращение к процедуре, написанной на Фортране и
      ' оттранслированной с ключом /AL или /AH
      ' (Large or Huge Memory Model),
      ' - передача полного адреса аргумента (удаленная ссылка)
      '  ———————————————————-
      ' 1-й параметр передает значение переменной,
      '  остальные параметры - саму переменную (адрес):
     CALLS SubFort((Integer1%), Integer2%, Single3, Idim%(-1))
      ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
      ' Обращение к процедуре, оттранслированной с ключом /AM
      ' (Medium Memory Model) - передача смещения  адреса аргумента
      ' (ближняя ссылка) - выглядело бы так:  
      ' CALL SubFort((Integer1%), Integer2%, Single3, Idim%(-1))
      ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
      ' Обращение к процедуре, написанной на QuickBASIC,
      ' выглядело бы так:
      '  CALL SubFort((Integer1%), Integer2%, Single3, Idim%())
   '
   PRINT ""
   PRINT "TEST_BAS.BAS - значения переменных после обработки:"
   PRINT "Integer1, Integer2, Single3 ="; Integer1%; Integer2%; Single3
   ' Значение Integer1% осталось неизменным
   PRINT "Массив Idim%(-1 to 8):";
   FOR i% = -1 TO 8: PRINT Idim%(i%); : NEXT
   PRINT ""
END
c **************************************************
c          Модуль SUBFORT.FOR
c **************************************************    
      subroutine SUBFORT(Integer1,Integer2,Real3,Idim)
c       описание типов параметров:    
      integer*2 Integer1,Integer2,Idim(10)
      real Real3
c
      print 20
 20   format (' SUBFORT.FOR - значения полученных параметров:')
      print 21,Integer1,Integer2,Real3
 21   format (' Integer1, Integer2, Real3 = ',2I5,F10.3)
      print 22,Idim
 22   format (' Массив Idim(10):',10I5)
c
c     изменение значений переменных
      Integer1=101
      Integer2=102
      Real3=103.
      do 40 i=1,10
 40   Idim(i)=40+i
c     перед возвращением в QuickBASIC- перевести строку
      print 30
 30   format (/)
      return
      end
DEFINT I-N
'*****************************************************
'             NMALLOC.BAS
'*****************************************************
'      Вспомогательный программный модуль:
'  обеспечивает резервирование 4K памяти, необходимой
'  для корректной работы с динамической памятью при
'  смешанном программировании:
'     QuickBASIC - (C, FORTRAN, PASCAL)
'——————————————————————————-
' ВКЛЮЧАТЬ ОБЯЗАТЕЛЬНО - при наличии операций
' ввода-вывода в процедурах на Фортране
'——————————————————————————-
'   При отсутствии данного модуля может быть получено
'сообщение:
'     run-time error F6700 - heap space limit exceeded
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Порядок формирования Quick-библиотеки:
'   1) трансляция модуля:
'       BC.EXE NMALLOC.BAS /O/T/C:512
'   2) включение в библиотеку MIXED.LIB
'      (или создание новой библиотеки):
'       LIB.EXE MIXED.LIB+NMALLOC.BAS;
'   3) создание Quick-библиотеки:
'       LINK.EXE /Q MIXED.LIB,MIXED.QLB,,BQLB45.LIB+BCOM45.LIB;
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
     DIM Manlocbuf%(2048)
     COMMON SHARED /NMALLOC/ Manlocbuf%()
END