Главная страница Visual 2000 · Общий список статей

Для тех, кто не хочет расставаться с ФОРТРАН'ом

Андрей Колесов, Ольга Павлова

© 1991, Андрей Колесов, Ольга Павлова
Авторский вариант. Статья была опубликована c незначительной литературной правкой в журнале "Мир ПК" 1'92.

Примечание автора. Эта статья написана очень давно и с некоторыми ее положениями автор сегодня не согласен. (Например, о том, что Фортран безнадежно устарел. Но ведь в начале 90-х инструментарий для Фортрана выглядел действительно довольно бледно по сравнению со средствами быстрой разработки.) Тем не менее, хотя здесь идет речь о связке Фортран-QB, основные подходы к реализации идей смешанного программирования сохраняют свою актуальность и более того — неплохо отражают ряд вопросов, которые нужно иметь в виду тем, кто, например, создает DLL-процедуры на Фортране.


Как обеспечить развитие и дальнейшее использование...
Еще несколько лет назад FORTRAN
От Фортрана лучше отказаться, но не спеша
Смешанное программирование — без нужды не увлекайтесь
Пример модернизации расчетного комплекса программ
И все-таки смешанное программирование: QuickBASIC-FORTRAN

Вызов FORTRAN-процедур из модулей QuickBASIC

Пример смешанного программирования
Заключение

ПРИЛОЖЕНИЯ

В начало статьи


Сама статья:

Как обеспечить развитие и дальнейшее использование...

Как обеспечить развитие и дальнейшее использование всего накопленного багажа программных средств — прикладных программ, библиотек подпрограмм — при переходе с одних систем программирования к другим? Некоторые варианты решения этих проблем рассматриваются в данной статье на примере перехода от языка 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.

Таким образом, переход к другому языку программирования практически неизбежен. Но при этом возникают две проблемы:

  1. необходимо обеспечить возможность развития и модернизации уже созданных программных комплексов;
  2. при разработке даже принципиально новых программ желательно иметь возможность использования готовых библиотек подпрограмм, реализованных на языке FORTRAN (или для языка FORTRAN).

В начало статьи

Смешанное программирование — без нужды не увлекайтесь

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

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

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

В начало статьи

Пример модернизации расчетного комплекса программ

Реализацию такого подхода можно проиллюстрировать на примере создания программного комплекса ANALIT (геофильтрационные расчеты). Первоначально расчетная программа была создана на языке FORTRANе для ЕС ЭВМ (ANALIT-EC). Расчеты выполнялись в пакетном режиме, исходные данные готовились в виде текстового файла, результаты выдавались на печать в табличном виде (рис.1). Для повышения эффективности работы пользователя на IBM PC потребовалось существенно расширить сервисные возможности комплекса и реализовать:

  1. диалоговый режим работы;
  2. графическое представление результатов расчетов;
  3. хранение исходных данных и результатов расчетов в виде базы данных.

В результате такой доработки был создан программный комплекс, структура которого приведена на рис. 2. Главный модуль системы написан на QuickBASIC, он обеспечивает выполнение всех функций диалоговой среды пользователя, а также ряд дополнительных операций обработки данных, — в частности, взаимодействия с графическим пакетом SURFER, при выводе результатов расчетов в виде карты изолиний. Взаимодействие с расчетным модулем производится с помощью временных символьных последовательных файлов .INP и .OUT. Таким образом, расчетный EXE-модуль, реализованный на языке FORTRAN, был интегрирован в новую версию системы с минимальными изменениями:

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

Представленный вариант реорганизации является достаточно характерным для расчетных программных комплексов, доставшихся нам в наследство от ЕС и СМ ЭВМ. Большинство из них было ориентировано на пакетный режим работы. Впрочем, если там и был реализован диалоговый режим, то скорее всего именно блоки диалогового взаимодействия будут корректироваться в связи с переходом на персональные компьютеры.

В начало статьи

И все-таки смешанное программирование: QuickBASIC-FORTRAN

Так что же делать, если без смешанного программирования не обойтись? Если Вам не хочется переписывать огромное число расчетных подпрограмм, написанных Вами на языке FORTRAN (очень естественное состояние), или Вы этого просто не можете сделать, так как эти процедуры представлены в виде библиотек объектных модулей?

Итак, если Вы решили в качестве своего базового языка использовать QuickBASIC версии 4.0 и старше, Ваши проблемы могут быть решены достаточно просто. При этом оговоримся сразу, что основные положения вопроса смешанного программирования, которые будут обсуждаться ниже, относятся не только к стыковке с языком FORTRAN, но и с другими языками — Си, Паскалем, Ассемблером и пр.

Прежде всего, следует иметь в виду, что существует два варианта взаимодействия модулей: вызов FORTRAN-процедур в среде QuickBASIC и наоборот. Очевидно, в нашем случае нас гораздо больше интересует первый вариант — разработка новых программ будет связана в основном с созданием и отладкой новых BASIC-модулей и работа будет выполняться в среде QuickBASIC.

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

В начало статьи

Вызов FORTRAN-процедур из модулей QuickBASIC

В начало статьи

1. Способ передачи аргументов

В QuickBASIC существует два вида описания внешней процедуры:

DECLARE Statement (BASIC Procedures) — для BASIC-процедур;

DECLARE Statement (NonBASIC Procedures) — для процедур, написанных на
других языках.

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

CALL Statement (BASIC Procedures);

CALL, CALLS Statement (NonBASIC Procedures).

Для правильного использования той или иной формы вызова FORTRAN-процедур необходимо четко представлять способы передачи адресов аргументов из BASIC-программ.

При этом следует иметь в виду следующие правила:

  1. для внешних процедур-подпрограмм описание DECLARE может отсутствовать, в этом случае способ передачи параметров определяется видом операторов CALL или CALLS;
  2. наличие описания DECLARE однозначно определяет способ передачи аргументов;
  3. при использовании внешних процедур-функций (Function-процедур) наличие оператора DECLARE обязательно;
  4. оператор CALL по умолчанию предполагает передачу адресов аргументов смещением (ближняя ссылка), а использование ключа SEG или оператора CALLS позволяет выполнять передачу аргумента по полному адресу (удаленная ссылка).

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

  1. CALL (смещение адреса) — при вызове процедур, оттранслированных с ключом /AM, — модель средней (MEDIUM) памяти;
  2. CALLS (полный адрес) — при вызове процедур, оттранслированных с ключом /AL или /AH, — модели большой (LARGE) или сверхбольшой (HUGE) памяти.

Следует иметь в виду, что по умолчанию Microsoft Fortran Сompiler предполагает использование ключа /AL.

Таким образом, с учетом всего вышесказанного работа с внешними FORTRAN-процедурами на языке QuickBASIC выполняется следующим образом.

В начало статьи

2. Пересылка массивов

В языке 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

Обратите внимание на следующие моменты:

  1. параметр Integer1 передавался в подпрограмму SubFort по значению, поэтому его содержимое в модуле TEST_BAS осталось неизменным;
  2. при выводе информации в подпрограмме SubFort первый символ на экран не выдается (оператор PRINT).

В начало статьи

2. Работа в среде QuickBASIC

Для работы в среде 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

'***************************************************
'            Модуль 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

В начало статьи

Модуль SUBFORT.FOR

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

В начало статьи

Модуль NMALLOC.BAS

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

В начало статьи