0

test-Q.cppファイルにFortranサブルーチンを呼び出すC++コードがファイルにありますgetqpf.F。ファイルtest-Q.cppでは、Fortranコードを外部として宣言し、getqpf_()名前マングリング規則を使用して関数を呼び出しています。およびコンパイラはGNU/Linuxで使用されていますgccgfortran

C++ファイルの先頭からの抜粋を次に示します。

extern "C" {
            void  getqpf_  (double *tri, 
                    int nsamp, 
                    int lwin,
                    int nfreqfit, 
                    double dt, 
                    float null, 
                    int L2,
                    double df,
                    double *qq, 
                    double *pf, 
                    double *ampls, 
                    double *work1, 
                    double *work2, 
                    double *work3, 
                    double *work4,
                    int mem, 
                    int morder, 
                    int nfs, 
                    double *xReal, 
                    double *xImag, 
                    double *xAbs,
                    double *x1,
                    int cen,
                    int top,
                    int bot, 
                    float cut,
                    int nfst,
                    int raw);  

        } // end

これは、Fortranファイルからの対応するスニペットです。

   subroutine getqpf (tri, nsamp, lwin, nfreqfit, dt, null, L2, df,
     1                   qq, pf, ampls, work1, work2, work3, work4,
     2                   mem, morder, nfs, xReal, xImag, xAbs, x1,
     3                   cen,top,bot, cut,nfst,raw)



      integer  morder, lwin, nsamp, nfreqfit, delay, nfs

      real     tri(*)
      real     qq(*), pf(*), ampls(*)

      real * 8 work1(*), work2(*), work3(*), work4(*)
      real * 8 xReal(*), xImag(*), xabs(*), x1(*)

      real * 8 dt8, cut8, df8
      real     null, cut
      integer  nfst
      logical  mem, L2, cen, top, bot, raw


      integer nf

C program logic code starts here
          nf = nfreqfit
          delay = 0
          dt8  = dt
          cut8 = cut

Fortranコードは、他のCコード関数を呼び出します。gfortranおよびコンパイラを使用するGNU/Linuxではgcc、次の方法ですべてのファイルをコンパイルおよびリンクしました。

 g++ -c test-Q.cpp -I./boost/boost_1_52_0/ -g
 gcc -c paul2.c -g
 gcc -c paul2_L1.c -g
 gcc -c paul6.c -g
 gcc -c paul6_L1.c -g 
 gcc -c fit_slope.c -g
 gfortran -c getqpf.F -g
 g++ -o test-Q test-Q.o paul2.o paul2_L1.o paul6.o paul6_L1.o fit_slope.o getqpf.o -g

バイナリを正常にビルドすることはできますが、行で発生するセグメンテーション違反がありますnf = nfreqfit。これは、Fortranファイルの最上部にあります。バイナリで実行gdbすると、次の出力が生成されます。

Program received signal SIGSEGV, Segmentation fault.
0x0000000000406fd3 in getqpf (tri=..., nsamp=Cannot access memory at address 0x3e9
) at getqpf.F:44
44        nf = nfreqfit

ここで何が起こっているのですか、そしてなぜセグメンテーション違反があるのですか?C++コードとFortranコードの間でメモリが適切に受け渡されていないようです。

アップデート

IanHが以下の回答で言及しているように、問題は参照によって引数を渡さないことが原因です。C ++を使用する場合、関数は次のように宣言する必要があります。

 extern"C" {
            void  getqpf_  (float *tri, 
                    int &nsamp, 
                    int &lwin,
                    int &nfreqfit, 
                    float &dt, 
                    float &null, 
                    int &L2,
                    float &df,
                    float *qq, 
                    float *pf, 
                    float *ampls, 
                    double *work1, 
                    double *work2, 
                    double *work3, 
                    double *work4,
                    int &mem, 
                    int &morder, 
                    int &nfs, 
                    double *xReal, 
                    double *xImag, 
                    double *xAbs,
                    double *x1,
                    int &cen,
                    int &top,
                    int &bot, 
                    float &cut,
                    int &nfst,
                    int &raw);  

        } // end 

アンパサンドの存在に注意してください。次に、関数をコードで次のように呼び出すことができます。

getqpf_ (tri,       
    nsamp, 
    lwin,
    nfreqfit, 
    dt, 
    null, 
    L2,
    df,
    qq, 
    pf, 
    ampls, 
    work1, 
    work2, 
    work3, 
    work4,
    mem, 
    morder, 
    nfs, 
    xReal, 
    xImag, 
    xAbs,
    x1,
    cen,
    top,
    bot, 
    cut,
    nfst,
    raw); 

などの変数はとしてnsamp宣言されていることに注意してくださいint nsamp = 1001

4

2 に答える 2

3

F2003のC相互運用性の使用に関するMSBの推奨事項を支持する一方で、特定の問題は参照渡し/値渡しの不一致であることに注意してください(これは、C相互運用性を使用する場合でも考慮する必要があります)。典型的なFortran実装はすべての引数を参照によって渡しますが、C(++)ではデフォルトは値によるものです。C ++側では、すべてのint引数とfloat引数、および一部のdouble引数にポインター指定子()がないことに注意してください*。これらの引数は値によって渡されますが、Fortran側にはそれを示すものは何もありません。F2003以前は、これは通常、Fortranコードでコンパイラ固有のディレクティブを使用して行われました。

F2003のC相互運用機能を使用すると、BIND(C)属性を持つプロシージャへの引数のデフォルトの受け渡し規則は参照によるものです。値によって渡される引数には、宣言にVALUE属性が含まれている必要があります。

于 2012-11-16T21:02:15.323 に答える
2

FortranISOCバインディングの使用をお勧めします。Stackoverflowとgfortranのマニュアルに例があります。これはFortran2003言語標準の一部であり、それ以前はFortran 95のテクニカルレポートです。これにより、コンパイラーとプラットフォームの移植性が高まります。コンパイラ固有の呼び出し規約や名前のマングリングについて心配する必要はありません。

于 2012-11-16T17:50:01.233 に答える