如何从C ++调用fortran例程?

时间:2015-12-04 06:54:11

标签: c++ fortran fortran77 f2c netlib

我希望从我的C ++代码中调用fortran例程cbesj.f,我该如何实现?

以下是我所做的步骤:

  1. 从netlib amos网页下载cbesj.f plus依赖项,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 它返回bug:

  4.   

    test_cbesj.cpp :(。text + 0xd6):对`cbesj_(complex *,float *,long *,long *,complex *,long *,long *)'的未定义引用

    在我的问题中引用cbesj_子例程的正确方法是什么?谢谢!

    感谢casey: 我认为你的方法是最好的。但我仍然有过错,为什么?我们走了:

    **In previous versions, the unset_userdata() method used to accept an associative array of key => 'dummy value' pairs. This is no longer supported.**
    
    在modemo.h中

    f77 -c *.f    
    

    在test_cbesj.cpp中,

    //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
    

    然后,

    #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;
    }
    

    感谢roygvib的回复!实际上是好建议。这是改变的test_cbesj.cpp:

    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);
    

    没有更多的seg错误。但由于某些原因,代码无法正常工作:

    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;
    

    并且答案是错误的,ierr也从源代码中说出来:

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

1 个答案:

答案 0 :(得分:2)

我从netlib下载了cbesj(或zbesj)及相关文件,但出于某种原因,他们无法使用gfortran4.8(在Linux x86_64上)。更确切地说,cbesj始终提供ierr=4,而我无法编译zbesj,因为zabs包含太多参数(但请参阅下面的更新)。

所以我放弃使用原始的AMOS代码,并尝试了同样基于AMOS的openspecfun。我只是通过键入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

由于this site表示答案为-0.394517...+ 0.297628...i,因此zbesj的结果似乎是正确的。

[更新]

通过更仔细地阅读原始代码并与openspecfun进行比较,发现用户需要取消注释i1mach.fr1mach.f(或d1mach.f的相应部分)取决于机器环境。对于Linux x86_64,取消注释标记为

的部分似乎已足够
C     MACHINE CONSTANTS FOR THE IBM PC

通过此修改,cbesjierr=0正常工作;否则它会给出ierr=4因为某些常量默认为0。对于双精度版本,我们还需要处理用户定义的ZABS,其定义与内在版本冲突。英特尔Fortran(ifort)识别用户定义的ZABS并成功编译它们(尽管有很多警告),而gfortran停止编译语法错误(假设它是内在的一个) )。 openspecfunc通过使用内在函数重写所有ZABS来避免此问题。无论如何,通过上述修改,cbesjzbesj都正常工作(正如预期的那样)。

[更新2]

事实证明,使用BLAS版本的d1mach.fr1mach.fi1mach.f,事情变得更加简单,因为它们会自动确定与机器相关的常量,而我们不需要手动修改代码。有关详细信息,请参阅Netlib/FAQ页面。同样的技巧也适用于SLATEC(例如,这个page)。

wget http://www.netlib.org/blas/i1mach.f 
wget http://www.netlib.org/blas/r1mach.f
wget http://www.netlib.org/blas/d1mach.f