как вызывать подпрограммы fortran из C++?

Я хочу вызвать подпрограмму fortran cbesj.f из своего кода на C++, и как мне этого добиться?

Вот шаги, которые я сделал:

  1. Загрузите cbesj.f плюс зависимости с веб-страницы netlib amos, http://www.netlib.org/cgi-bin/netlibfiles.pl?filename=/amos/cbesj.f

  2. В исходном каталоге

    f2c -C++PR *.f

    g++ -c *.c

    ar cr libmydemo.a *.o

  3. [test_cbesj.cpp][1] и [mydemo.h][2] используются для вызова подпрограммы таким образом,

    g++ test_cbesj.cpp -lf2c -lm -L. -lmydemo возвращает ошибку:

test_cbesj.cpp:(.text+0xd6): неопределенная ссылка на `cbesj_(complex*, float*, long*, long*, complex*, long*, long*)'

Как правильно обращаться к подпрограмме cbesj_ в моей задаче? Спасибо!

Спасибо за кейси: я думаю, что ваш подход является лучшим. Но я все равно поставил ошибку, почему? Вот так:

f77 -c *.f    

в модемо.ч

//File mydemo.h                                                                                                                              
#ifndef MYDEMO_H
#define MYDEMO_H
#include <stdio.h>      /* Standard Library of Input and Output */
#include "f2c.h"

extern"C" int cacai_(complex *z__, real *fnu, integer *kode, integer *mr,         integer *n, complex *y, integer *nz, real *rl, real *tol, real *el\
im, real *alim);
extern"C" int cairy_(complex *z__, integer *id, integer *kode, complex *ai,         integer *nz, integer *ierr);
extern"C" int casyi_(complex *z__, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, real *rl, real *tol, real *elim, real     \
  *alim);
extern"C" int cbesj_(complex *z__, real *fnu, integer *kode, integer *n,     complex *cy, integer *nz, integer *ierr);
extern"C" int cbinu_(complex *z__, real *fnu, integer *kode, integer *n,     complex *cy, integer *nz, real *rl, real *fnul, real *tol, real *el\
im, real *alim);
extern"C" int cbknu_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, real *tol, real *elim, real *alim);
extern"C" int cbuni_(complex *z__, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, integer *nui, integer *nlast, real *fnul, \
real *tol, real *elim, real *alim);
extern"C" int ckscl_(complex *zr, real *fnu, integer *n, complex *y, integer *nz, complex *rz, real *ascle, real *tol, real *elim);
extern"C" int cmlri_(complex *z__, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, real *tol);
extern"C" int crati_(complex *z__, real *fnu, integer *n, complex *cy, real *tol);
extern"C" int cs1s2_(complex *zr, complex *s1, complex *s2, integer *nz, real *ascle, real *alim, integer *iuf);
extern"C" int cseri_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, real *tol, real *elim, real *alim);
extern"C" int cshch_(complex *z__, complex *csh, complex *cch);
extern"C" int cuchk_(complex *y, integer *nz, real *ascle, real *tol);
extern"C" int cunhj_(complex *z__, real *fnu, integer *ipmtr, real *tol, complex *phi, complex *arg, complex *zeta1, complex *zeta2, complex\
 *asum, complex *bsum);
extern"C" int cuni1_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, integer *nlast, real *fnul, real *tol     \
  , real *elim, real *alim);
extern"C" int cuni2_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, integer *nlast, real *fnul, real *tol     \
  , real *elim, real *alim);
extern"C" int cunik_(complex *zr, real *fnu, integer *ikflg, integer *ipmtr, real *tol, integer *init, complex *phi, complex *zeta1, complex\
 *zeta2, complex *sum, complex *cwrk);
extern"C" int cuoik_(complex *z__, real *fnu, integer *kode, integer *ikflg,     integer *n, complex *y, integer *nuf, real *tol, real *elim, re\
al *alim);
extern"C" int cwrsk_(complex *zr, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, complex *cw, real *tol, real *elim, real *a\
lim);
extern"C" real gamln_(real *z__, integer *ierr);
extern"C" integer i1mach_(integer *i__);
extern"C" real r1mach_(integer *i__);
extern"C" int xerror_(char *mess, integer *nmess, integer *l1, integer *l2,     ftnlen mess_len);
    #endif

в test_cbesj.cpp,

#include "mydemo.h"
#include "f2c.h"
#include <math.h>
#include <iostream>
#include <stdio.h>
#include <assert.h>
using namespace std;
int main(void)
{
  //  double x=86840.;                                                                                                                       
  //int nu=46431,j, err;                                                                                                                     
  complex *z,z__;
  z__.r = 3.0;z__.i = 2.0;z = &z__;
  cout << z->r << '\t' << z->i << endl;
  real *fnu;float fnu__ = 3.0;fnu = &fnu__;
  integer *kode ;long int kode__=1;kode=&kode__;
  integer *n    ;long int n__=1;n=&n__;
  complex *cy;
  integer *nz;
  integer *ierr;
  cbesj_(z, fnu, kode, n, cy, nz, ierr);
  cout << cy->r << '\t' << cy->i << endl;

  return 0;
}

Затем,

g++ -c -g test_cbesj.cpp
g++ -o test *.o -lg2c
./test
3   2
Segmentation fault (core dumped)
gdb test 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /media/Downloads/amos-4/test...done.
(gdb) run
Starting program: /media/Downloads/amos-4/test 
3   2

Program received signal SIGSEGV, Segmentation fault.
0x0804b355 in cbesj_ ()
(gdb) frame 0
#0  0x0804b355 in cbesj_ ()
(gdb) frame 1
#1  0x0805a3ca in main () at test_cbesj.cpp:21
21    cbesj_(z, fnu, kode, n, cy, nz, ierr);

Спасибо за ответ roygvib! На самом деле хорошие предложения. Вот измененный файл test_cbesj.cpp:

complex z, cy;
  float fnu;
  long int kode, n, nz, ierr;

  z.r = 3.0; z.i = 2.0;
  fnu = 3.0;
  n = 1; kode = 1;
  cout.precision(16);
  cbesj_( &z, &fnu, &kode, &n, &cy, &nz, &ierr );
  cout << cy.r << '\t' << cy.i << endl;
  cout << "nz=" << nz << endl;
  cout << "ierr=" << ierr << Lendl;

Нет больше ошибки сегмента. Но по некоторым причинам код не работает должным образом:

./test
-1.343533039093018  -1.343533992767334
nz=0
ierr=4

и ответы неверны, и ierr также говорит об этом из исходного кода:

C           NZ     - NUMBER OF COMPONENTS SET TO ZERO DUE TO UNDERFLOW,
C                    NZ= 0   , NORMAL RETURN
C                    NZ.GT.0 , LAST NZ COMPONENTS OF CY SET TO ZERO
C                              DUE TO UNDERFLOW, CY(I)=CMPLX(0.0,0.0),
C                              I = N-NZ+1,...,N
C           IERR   - ERROR FLAG
C                    IERR=0, NORMAL RETURN - COMPUTATION COMPLETED
C                    IERR=1, INPUT ERROR   - NO COMPUTATION
C                    IERR=2, OVERFLOW      - NO COMPUTATION, AIMAG(Z)
C                            TOO LARGE ON KODE=1
C                    IERR=3, CABS(Z) OR FNU+N-1 LARGE - COMPUTATION DONE
C                            BUT LOSSES OF SIGNIFCANCE BY ARGUMENT
C                            REDUCTION PRODUCE LESS THAN HALF OF MACHINE
C                            ACCURACY
C                    IERR=4, CABS(Z) OR FNU+N-1 TOO LARGE - NO COMPUTA-
C                            TION BECAUSE OF COMPLETE LOSSES OF SIGNIFI-
C                            CANCE BY ARGUMENT REDUCTION
C                    IERR=5, ERROR              - NO COMPUTATION,
C                            ALGORITHM TERMINATION CONDITION NOT MET

person Franklin Dong    schedule 04.12.2015    source источник
comment
Вы проверили, определена ли функция в каком-либо из файлов C, созданных f2c?   -  person R Sahu    schedule 04.12.2015
comment
Кроме того, пожалуйста, опубликуйте содержимое файлов, на которые вы ссылаетесь. Ссылки на исходный код здесь настоятельно не рекомендуются. Хуже, когда ссылки ведут на изображения.   -  person R Sahu    schedule 04.12.2015
comment
По крайней мере, вам нужно будет объявить функцию в заголовке как extern C, чтобы имя функции, которое она ищет, не было искажено.   -  person Anon Mail    schedule 04.12.2015
comment
Найдите вопросы и ответы по теме Взаимодействие Fortran с c, узнайте, как вызывать подпрограмму Fortran из C++, не пропуская ее через f2c.   -  person High Performance Mark    schedule 04.12.2015
comment
сделал Фортран интероперабельным. Компиляция с помощью f2c почти гарантированно негативно скажется на производительности кода. Используйте настоящий компилятор Fortran и свяжите все вместе. Ищите iso_c_binding, здесь много примеров.   -  person casey    schedule 04.12.2015
comment
Спасибо вам, ребята! Особенно Кейси. Но у меня все еще есть ошибка seg, и я не знаю, что делать дальше. Пожалуйста, порекомендуйте! ваше здоровье.   -  person Franklin Dong    schedule 04.12.2015
comment
Спасибо, roygvib, ошибка seg устранена, но подпрограмма fortran возвращает флаг ошибки 4   -  person Franklin Dong    schedule 04.12.2015
comment
@FranklinDong Возможно, вы загружали файлы с netlib.org/amos? Сейчас я пробую cbesj и zbesj с gfortran, но возникают проблемы... (cbesj дает ierr=4, а zbesj не может быть скомпилирован из-за zabs()). Если я использую коды с другого сайта (например, openspecfun), все работает нормально.   -  person roygvib    schedule 04.12.2015
comment
@roygvib, да да и да. Я скачал файлы с netlib.org/amos и подумал, что все должно работать так же хорошо. а cbesj дает ierr=4 только за исключением случая nu=0, Z=(0.0,0.0). Я предполагаю, что значение передано неправильно. Не могли бы вы дать ссылку на openspecfun? Спасибо.   -  person Franklin Dong    schedule 04.12.2015
comment
Связаны ли они каким-либо образом с C-функциями j0, j1, jn, y0, y1, yn?   -  person cup    schedule 24.12.2015


Ответы (1)


Я скачал cbesj (или zbesj) и связанные файлы с netlib, но они почему-то не работали с gfortran4 .8 (в Linux x86_64). Точнее, cbesj всегда дает ierr=4, а zbesj я не смог скомпилировать, потому что zabs имеет слишком много аргументов (но, пожалуйста, смотрите обновление ниже).


Поэтому я отказался от исходных кодов AMOS и попробовал openspecfun, который также основан на AMOS. Я скомпилировал последний, просто набрав make (с gfortran), который сгенерировал libopenspecfun.a и т. д. Затем я сделал следующий код для тестирования zbesj:

#include <cstdio>

extern "C" {
    void zbesj_( double *zr, double *zi, double *fnu, int *kode, int *n,
                 double *Jr, double *Ji, int *nz, int *ierr );
}

int main()
{
    int    n, nz, ierr, kode;
    double fnu, zr, zi, Jr, Ji;

    fnu = 2.5;
    zr = 1.0; zi = 2.0;
    n = 1; kode = 1;

    zbesj_( &zr, &zi, &fnu, &kode, &n, &Jr, &Ji, &nz, &ierr );

    printf( "fnu = %20.15f\n", fnu );
    printf( "z   = %20.15f %20.15f\n", zr, zi );
    printf( "J   = %20.15f %20.15f\n", Jr, Ji );
    printf( "nz, ierr = %d %d\n", nz, ierr );

    return 0;
}

и составлен как

g++ test_zbesj.cpp libopenspecfun.a -lgfortran

который дает

fnu =    2.500000000000000
z   =    1.000000000000000    2.000000000000000
J   =   -0.394517225893988    0.297628229902939
nz, ierr = 0 0

Потому что этот сайт говорит, что ответ -0.394517...+ 0.297628...i, результат zbesj кажется правильным.


[Обновлять]

Внимательно прочитав исходный код, а также сравнив его с openspecfun, было обнаружено, что пользователю необходимо раскомментировать соответствующие разделы i1mach.f и r1mach.f (или d1mach.f) в зависимости от машинной среды. Для Linux x86_64 достаточно раскомментировать раздел с тегом

C     MACHINE CONSTANTS FOR THE IBM PC

С этой модификацией cbesj корректно работал с ierr=0; в противном случае он дает ierr=4, потому что некоторые константы по умолчанию равны 0. Для версии с двойной точностью нам также нужно иметь дело с определяемым пользователем ZABS, определение которого конфликтует со встроенным. Intel Fortran (ifort) распознает определяемые пользователем ZABS как таковые и успешно их компилирует (хотя и с большим количеством предупреждений), в то время как gfortran прекращает компиляцию с синтаксической ошибкой (при условии, что это встроенная ошибка). openspecfunc позволяет избежать этой проблемы, переписывая все ZABS на встроенный. Во всяком случае, с указанными выше изменениями и cbesj, и zbesj работали корректно (как и ожидалось).


[Обновление 2]

Оказалось, что при использовании BLAS-версии d1mach.f, r1mach.f и i1mach.f все становится еще проще, потому что они автоматически определяют машинно-зависимые константы, и нам не нужно изменять коды вручную. Подробнее см. на странице Netlib/FAQ. Тот же трюк применим и к SLATEC (например, эта страница).

wget http://www.netlib.org/blas/i1mach.f 
wget http://www.netlib.org/blas/r1mach.f
wget http://www.netlib.org/blas/d1mach.f 
person roygvib    schedule 04.12.2015
comment
привет, roygvib, он действительно работает как шарм, и я только что проверил, как он работает, как и ожидалось, для очень высоких заказов, что решило мой первоначальный вопрос на stackoverflow.com/questions/34054482/. (хотя ierr возвращает значение 3, но это действительно помогает мне!) Я очень ценю ваше время и помощь в этом вопросе, и спасибо! Очень жаль, что на netlib.org/amos нет исходного кода без ошибок (обновленного). - person Franklin Dong; 04.12.2015
comment
g++ test_zbesj.cpp -libopenspecfun.a -lgfortran на самом деле у меня не работает. Но g++ test_zbesj.cpp -lopenspecfun -lgfortran заработал после того, как я отредактировал ~/.bashrc со строкой export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH и источником ~/.bashrc - person Franklin Dong; 04.12.2015
comment
Я все еще пытаюсь найти причину, по которой cbesj у меня не работает, но пока безуспешно... Это может быть из-за старого синтаксиса фортрана и связанной с ним несогласованности или чего-то в этом роде. В любом случае, я считаю, что лучше использовать версию с двойной точностью, потому что специальные функции со сложными аргументами могут генерировать очень большие значения. - person roygvib; 04.12.2015
comment
Привет @FranklinDong. Я прочитал код более внимательно, и кажется, что нам нужно вручную раскомментировать часть машинно-зависимых констант перед компиляцией (см. Обновление выше). Так что это не баг и может быть лучше обновить свой другой пост (чтобы он не подразумевал наличие багов...) Но уверен, что код не актуален (написано более чем 30 лет назад!!) - person roygvib; 05.12.2015
comment
привет @roygvib, спасибо, что вернулся. Я также сделал разницу между этими двумя проектами исходных кодов и обнаружил разницу ZABS, сделанную в openspecfun. И я проверю cbesj на разных машинах с разными компиляторами, с комментарием, который вы обновили. - person Franklin Dong; 07.12.2015