【问题标题】:how to call fortran routines from C++?如何从 C++ 调用 fortran 例程?
【发布时间】:2016-03-09 01:03:53
【问题描述】:

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

以下是我已完成的步骤:

  1. 从 netlib amos 网页下载 cbesj.f 以及依赖项,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    

在 modemo.h 中

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

【问题讨论】:

  • 你有没有检查过f2c生成的任何C文件中是否定义了函数?
  • 另外,请发布您链接到的文件的内容。这里强烈建议不要链接到源代码。当链接指向图像时,情况会更糟。
  • 至少,你必须让你的头文件将函数声明为extern C,这样它正在寻找的函数名就不会被破坏。
  • 搜索关于 fortran 与 c 的互操作性主题的 Qs 和 As,了解如何在不通过 f2c 的情况下从 C++ 调用 Fortran 例程。
  • 使 Fortran 可互操作。几乎可以肯定,使用 f2c 编译会对代码的性能产生负面影响。使用真正的 Fortran 编译器并将所有内容链接在一起。搜索iso_c_binding,这里有很多例子。

标签: c++ fortran fortran77 f2c netlib


【解决方案1】:

我从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]

事实证明,使用 d1mach.fr1mach.fi1mach.f 的 BLAS 版本,事情变得更加简单,因为它们会自动确定与机器相关的常量,我们不需要手动修改代码。详情请参阅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 

【讨论】:

  • 嗨 roygvib,它真的很像一个魅力,我刚刚测试过它在非常高的订单上可以正常工作,这解决了我在stackoverflow.com/questions/34054482/… 的原始问题。 (虽然 ierr 返回值 3,但它确实对我有用!)我真的非常感谢您在这个问题上的时间和帮助,谢谢!真可惜 netlib.org/amos 没有无错误的源代码(最新)。
  • g++ test_zbesj.cpp -libopenspecfun.a -lgfortran 实际上对我不起作用。但是 g++ test_zbesj.cpp -lopenspecfun -lgfortran 在我编辑 ~/.bashrc 后使用 export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH 和 source ~/.bashrc
  • 我仍在试图找出为什么cbesj 对我不起作用,但还没有成功...这可能是因为旧的 fortran 语法和相关的不一致或类似的东西.无论如何,我认为最好使用双精度版本,因为具有复杂参数的特殊函数会生成非常大的值。
  • 嗨@FranklinDong 我已经更仔细地阅读了代码,似乎我们需要在编译之前手动取消注释机器相关常量的某些部分(参见上面的更新)。所以这不是一个“错误”,最好更新你的另一篇文章(这样它就不会暗示存在错误......)但可以肯定的是代码不是最新的(它是写于 30 多年前!!)
  • 嗨@roygvib,感谢您回来。我还对这两个源代码项目进行了比较,并找出了在 openspecfun 中制作的 ZABS 差异。而且,我也会在不同的机器上用不同的编译器检查 cbesj,并附上你更新的评论。
猜你喜欢
  • 2014-01-14
  • 2012-01-02
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 1970-01-01
  • 2016-10-13
  • 1970-01-01
  • 2012-05-03
相关资源
最近更新 更多