CERN ROOT - программа для анализа больших объемов данных, ориентированная прежде всего на использование в научной среде. Программа обладает широкими возможностями по визуализации данных и имеет встроенный интерпретатор языка C++ CINT. Как известно, в физике до сих пор имеет широкое применение язык программирования FORTRAN, на котором написано значительное количество интересных библиотек. Однажды мой коллега-физик попросил меня помочь разобраться в этом вопросе - ему нужно было вызывать функции фортрановской библиотеки MINUIT (разработанной тоже в CERN) непосредственно из интерпретатора CINT. Благодаря поискам решения этой задачи родился этот пример.
Итак, прежде всего создадим функцию на фортране, которая будет вызываться из CINT. Пусть это будет функция, складывающая два числа, также создадим в ней COMMON блок для того, чтобы иметь возможность поэкспериментировать с изменением глобальных переменных:
Теперь замечания по использованию фортрановского кода в коде C++:
Итак, прежде всего создадим функцию на фортране, которая будет вызываться из CINT. Пусть это будет функция, складывающая два числа, также создадим в ней COMMON блок для того, чтобы иметь возможность поэкспериментировать с изменением глобальных переменных:
INTEGER FUNCTION SUM( A, B ) DOUBLE PRECISION GL1 DOUBLE PRECISION GL2 INTEGER GL3 COMMON /GL/ GL1( 1, 2 ), GL2, GL3 INTEGER A, B SUM = A + B GL1( 1, 1 ) = 0.1 GL1( 1, 2 ) = 0.2 GL2 = 10.89 GL3 = 8 RETURN ENDРазместим этот код в файле Sum.f. Теперь нам нужно создать код на C++, который, во-первых, будет вызывать функцию SUM(), а во-вторых, сможет быть вызван из интерпретатора CINT. Пусть вызов SUM() будет организован в виде функтора (т.е. класса с определенным оператором ()) - это упростит семантику вызова из CINT. Разместим наш код в файлах Sum.h и Sum.c. Это содержимое Sum.h:
#include <iostream> #include "TObject.h" extern "C" { extern int sum_( int*, int* ); } extern struct fort_struct { double a[ 2 ][ 1 ]; double b; int c; } gl_; class Sum : public TObject { public: int operator()( int a, int b ) { return sum_( &a, &b ); } void accessGl( void ) const { printGl(); gl_.a[ 0 ][ 0 ] = 34.9; gl_.a[ 1 ][ 0 ] = 4.1; gl_.b = 0.88; gl_.c = 70; printGl(); } private: void printGl( void ) const { std::cout << "gl_ = " << gl_.a[ 0 ][ 0 ] << ", " << gl_.a[ 1 ][ 0 ] << ", " << gl_.b << ", " << gl_.c << std::endl; } ClassDef( Sum, 1 ) };Это содержимое Sum.c:
#include "Sum.h" ClassImp( Sum )Сразу отметим наличие определений ClassDef() и ClassImp() - это то, что позволит сделать из нашего Sum объект, с которым интерпретатору CINT будет комфортно работать.
Теперь замечания по использованию фортрановского кода в коде C++:
- Фортрановские функции должны связываться с C++ кодом как extern "C", при этом название функции записывается в нижнем регистре (несмотря на то, что в исходном коде оно может быть в верхнем регистре), а в конце добавляется символ _ - именно так компилятор фортрана записывает названия функций в объектном коде. В нашем случае функция SUM() трансформировалась в sum_().
- Аргументы передаются в фортрановскую функцию через указатели, поэтому функция двух целочисленных аргументов SUM() превратилась в sum_( int*, int* ).
- Глобальные переменные, объявленные в блоке COMMON, также записываются в нижнем регистре с добавлением символа _. Также обратите пристальное внимание на то, что столбцы и строки многомерных массивов в C++ и фортране меняются местами, так, переменная GL1( 1, 2 ) стала double a[ 2 ][ 1 ] в коде C++.
all: rootcint -f dict_sum.cxx -c Sum.h g++ -fPIC -I$(ROOTSYS)/include/root -c dict_sum.cxx Sum.c g77 -fPIC -c Sum.f -o Sum_f.o g++ -shared -o Sum.so dict_sum.o Sum.o Sum_f.o clean: rm *.o *.so dict_sum.cxx dict_sum.h
Обратите внимание на использование rootcint. Эта утилита создает вспомогательный код, необходимый для использования Sum в CINT, название файла dict_sum.cxx выбрано произвольно. Фортрановский код компилируется с помощью компилятора g77, но ничто не мешает заменить его на gfortran, если это необходимо. После запуска make создается объект Sum.so, который мы будем загружать в CINT, а затем использовать определенные в нем объекты. Пример сессии ROOT:
root [0] .L Sum.so
root [1] Sum s
root [2] int res = s(2,4)
root [3] .p res
(int)6
root [4] s.accessGl()
gl_ = 0.1, 0.2, 10.89, 8
gl_ = 34.9, 4.1, 0.88, 70
root [5]
Комментариев нет:
Отправить комментарий