Положим, вам нужно из приложения, написанного на C++
, вызвать подпрограммы, написанные на FORTRAN
’е.
Да, такое может легко с вами случиться. Сначала о том, зачем это понадобилось мне.
Если вам хочется сразу к делу, пропускайте следующий пункт.
В проекте CATS’ PDEs часто приходится работать с большими разреженными матрицами — матрицами, большинство элементов которых равно нулю и не хранится. Существует, вообще говоря, довольно много1 форматов хранения для таких матриц.
CSC–формат (Comressed Sparse Column, разреженно–столбцовый) является наиболее гибким и популярным в МКЭ форматом. На нём же и основан не менее популярный стандарт хранения матриц в виде ASCII–текста Harwell–Boeing2 (да-да, те самые боинги).
Матрицы в HB–формате нынешний софт понимает и любит. Например, та же Mathematica
умеет в импорт/экспорт HB–матриц
в свой SparseArray, который, кстати, использует CSR–формат (Comressed Sparse Row, разреженно–строчный) андерхуд.
Очевидно3, что хороший тулкит вроде CATS’ PDEs, в котором предполагается работа с разреженными матрицами, должен поддерживать i/o стандартных форматов типа Harwell–Boeing, Matrix Market и так далее:
- Ввод. Чтобы тестировать решатели систем на матрицах, полученных из реальных задач моделирования течения жидкостей, акустики, экономики и так далее. Вот ссылка на коллекцию Harwell–Boeing.
- Вывод. Чтобы с собранными в вашем приложении матрицами мог работать другой софт — например, чтобы тот же MatrixPlot в Математике нарисовать. Конечно, вы можете передать вашу матрицу и в плотном виде. Если она поместится в оперативную память и у вас есть желание подождать 1000 лет.
CATS’ PDEs написан преимущественно на C++11
.
Существуют готовые библиотеки ввода/вывода для Harwell–Boeing, написанные на C
/C++
:
Однако ввод/вывод делать я решил на FORTRAN
’е и сейчас объясню, наконец, зачем.
Так уж вышло, что Harwell–Boeing придумали в далёком 1992-м; если вы посмотрите в [2, с. 9], как он устроен, то довольно быстро поймёте,
что формат очень FORTRAN
–ориентирован — уже по первому предложению:
Our collection is held in an 80–column, fixed–length format for portability…
В отличие от C
/C++
, FORTRAN
очень богат на форматы ввода/вывода.
Поэтому чтобы написать i/o на C
/C++
… придётся писать свой FORTRAN
–like форматный i/o!
Что, кстати, обе вышеуказанные библиотеки и делают: первая — 4 226 строк, вторая — 1 604. И это против элегантных 129, написанных на FORTRAN
’е
Впрочем, свой FORTRAN
на C
/C++
— это ещё половина беды. Например, вот эту
матрицу первое решение вообще не прочитает, потому что поддержку дескриптора P
автор не сделал (упс, не дописали свой FORTRAN
).
А ещё в этом же решении мной был найден баг (смотрите 1232-ю строчку здесь или просто поищите “Alexander” — зачем мне врать). Короче, сами понимаете…
Надеюсь, на вопрос зачем я ответил.
- платформа: Windows 10 x64,
- компилятор
C++
: MSVC (Visual Studio 2015), - компилятор
FORTRAN
: ifort.
Вероятно, Visual Studio* у вас уже есть. Intel Fortran суть часть Intel Parallel Studio, купить её можно здесь. Если вы студент, ресёрчер и т.п., можете забрать бесплатно — достаточно иметь вузовскую почту.
Скорее всего вам не захочется устанавливать все модули Intel Parallel Studio — оставьте чекбоксы только на ifort. Крайне советую установить их расширение для работы с FORTRAN
’ом для Visual Studio* (нажать чекбокс во время установки). Дальше я объясню, почему это удобно.
В Intel Parallel Studio нет GUI. Неочевидная вещь: если вы забыли установить какой-то модуль (или установили лишний), можно это исправить… через удаление. После запуска деинстоллера выбираете опцию “Modify” и вперёд.
Вызывать будем подпрограммы FORTRAN
’а из C++
–приложения. Точка входа в sln/C++ Sources/user.cpp
, подпрограммы — в sln/FORTRAN Static Lib/add.f90
и ″/square.f90
.
Есть два пути: создать статическую (в Windows обычно имеет расширение .lib) или динамическую (shared) библиотеку (″ .dll). По своей природе статическая библиотека суть объектный файл (.o, .obj) и подключается к приложению во время линковки, динамическая — исполняемый файл (.exe), подключается в рантайме.
Для наших целей достаточно написать (возможно, много) подпрограмм на FORTRAN
’е, скормить их ifort’рану и собрать статическую библиотеку, которую затем успешно линковать к C++
. На времени компиляции основного приложения это не отразится и все будут счастливы.
Запускаем терминал ifort: [Start] — All Apps — Intel Parallel Studio* — Compiler*
. Соберём и прилинкуем статическую библиотеку.
-
Получаем объектные файлы:
> ifort /c add.f90 square.f90
-
Собираем библиотеку:
> lib /out:f.lib add.obj square.obj
-
Компилируем приложение с MSVC и запускаем:
> cl /EHsc /Ox user.cpp f.lib
> user.exe
Результат выполнения:
Ту же штуку вне среды ifort у вас так просто провернуть не получится, потому что MSVC понятия не имеет, где искать FORTRAN
’онские библиотеки (типа ifconsol.lib и т.д., как предупреждает документация):
Проблему можно решить, прилинковав вручную недостающие библиотеки. Дефолтно искать их стоит где-то в C:\Program Files*\IntelSWTools\compilers_and_libraries*\windows\compiler\lib
.
Легко догадаться, что то же веселье вас ждёт и при работе из Visual Studio* (а бог не зря вам её послал — едва ли вы будете компилировать и дебажить большой проект вручную из терминала). Ну и ещё добавится возня с разными платформами — Win32 / x64, разными конфигурациями — Release / Debug и конфликтами C Run–Time Library — добавленные вручную библиотеки могут быть скомпилированы с разными ключами CRT.
Чтобы сего веселья избежать, удобно использовать интеловское расширение для Visual Studio*, которое в предыдущем пункте я советовал установить.
Положим, у нас есть в решении два проекта: пустой проект Visual C++ для user.cpp (C++ Sources в этом репозитории) и статическая библиотека Visual Fortran для add.f90 и square.f90 (FORTRAN Static Lib ″).
Второй создать очень просто (я предполагаю, что расширение установлено) — File — New Project — Intel(R) Visual Fortran — Library — Static Library
.
Головной модуль расположен в C++ Sources, поэтому необходимо, чтобы компилировался именно этот проект: Right Click — Set as SrartUp Project
.
Для настройки (почти) достаточно прочитать эту и эту странички документации и посмотреть примеры проектов, дефолтно расположенных примерно в C:\Program Files*\IntelSWTools\samples*\en\compiler_f\psxe\MixedLanguage
. Однако имеют место небольшие опечатки и недосказанности, поэтому я лучше здесь об этом напишу.
По какой-то причине Visual Fortran–проекты не переключаются на x64–платформу автоматически (естественно, если вы хотите делать 64-битные приложения, вы скачали эту версию ifort). Чтобы это исправить, нажимаем на селектбокс платформ Win32 / x64: Configuration Manager… — Platform — (напротив “FORTRAN Static Lib”) <New…> — x64 — Copy settings from: <Empty>
.
Теперь попробуйте билдить FORTRAN Static Lib, переключая платформы. Если всё верно, в режиме x64 в окошке с логами вы заметите что-то вроде Compiling with Intel(R) Visual Fortran Compiler* [Intel(R) 64]...
, а в режиме Win32 — Compiling with Intel(R) Visual Fortran Compiler* [IA-32]...
.
Осталось научить C++ Sources искать FORTRAN
’онские библиотеки (см. предыдущий пункт) в нужных местах. Настройка выполняется один раз для всех Visual C++–проектов.
Кликаем View — Other Windows — Property Manager — “C++ Sources” — Debug | Win32 — Microsoft.Cpp.Win32.user
. Для настройки x64 выбираем ″ — Debug | x64 — ″
соответственно (Release–конфигурацию настраивать вручную не придётся).
Далее идём в VC++ Directories
. В Include Directories
добавляем $(IFORT_COMPILER[VERSION])compiler\include
(юху, Intel и макросы сделали для пути!). Вместо [VERSION] поставьте версию, у меня на момент написания стоит 16.
Проверьте, что в Evaluated value:
стоит что-то вменяемое типа C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2016.3.207\windows\compiler\include
— скопируйте в проводник и откройте эту папку.
В Library Directories
добавляем $(IFORT_COMPILER[VERSION])compiler\lib\ia32
и $(IFORT_COMPILER[VERSION])compiler\lib\intel64
для Win32 и x64 соответственно.
Сбилдите FORTRAN Static Lib–проект и добавьте FORTRAN Static Lib.lib в C++ Sources (я предпочитаю вручную, но можно и в Project Properties подключить). Готово, можно компилировать.
Удобно, что исходники теперь аккуратно расфасованы по разным проектам и код можно редактировать в любимой IDE. Более того, дебаг работает привычным образом:
- Yousef Saad, SPARSKIT: A Basic Toolkit for Sparse Matrix Computations ↩
- Iain Duff, Roger Grimes, and John Lewis, User’s Guide for the Harwell–Boeing Sparse Matrix Collection ↩
- Почему-то в университетских курсах для инженеров вообще не рассказывают о том, что стандартные форматы для хранения матриц уже придумали. В России это как-то не принято (по крайней мере, у нас в НГТУ) — быть может, из-за не любви к истории (в одном только названии «боинг» столько романтики!). В иностранных курсах такое я встречал. Как следствие, студенты пишут свои велосипеды с нулём переносимости. Но вас я от этого уберёг — добавьте Matrix Market в закладки и живите спокойно. ↩