2

C++ アプリケーションから Fortran コードを使用しようとしています。具体的には、SLATEC のdrc3jj.fとやり取りしようとしています。ただし、Fortran サブルーチンは、関数に渡されたパラメーターに応じてサイズが異なる配列を返します。

配列のサイズが 1 の場合、出力する C++ 配列には適切な値が含まれています。ただし、このサイズが 1 より大きい場合、C++ 配列には、出力値があるべき場所に NaN が含まれます。

以下は私が使用するコードです。これは、Fortran サブルーチンを C++ アプリケーションにリンクするだけです。

#ifndef FORTRANLINKAGE_H
#define FORTRANLINKAGE_H

extern "C"
{
    extern void drc3jj_(double*,double*,double*,double*,double*,
                        double*,double [],int*,int*);
}

#endif // FORTRANLINKAGE_H

お肉はここまで。実際に C++ から Fortran サブルーチンを呼び出し、出力を出力します。

#include "fortranLinkage.h"

#include <iostream>
#include <stdlib.h>

using namespace std;

void wigner3j(double l2, double l3, double m2, double m3, double coeff [])
{
    double l1min,l1max;
    int ierr,size(3);

    drc3jj_(&l2,&l3,&m2,&m3,&l1min,&l1max,coeff,&size,&ierr);

    cout << "Min: " << l1min << "\t Max: " << l1max << "\t Err: " << ierr << endl;
}

int main(int argc, char const *argv[])
{
    int l1(atoi(argv[1])),l2(atoi(argv[2])),m2(atoi(argv[3])),m3(atoi(argv[4]));
    double coeff [3];

    wigner3j(l1,l2,m2,m3,coeff);

    for (int i=0;i<3;i++)
    {
          cout << coeff[i] << endl;
    }
    return 0;
}

プログラムを で呼び出すと./myProgram 2 8 2 8、適切に 1/sqrt(21) が出力されます。./myProgram 2 8 2 7ただし、配列のサイズが実際には 2 であるを試すと、次の結果が得られます。

Min: 9   Max: 10     Err: 0
-nan
-nan
2.08175e-317

NaN は実際には適切な符号を持っています。

とにかく、C++ 配列を Fortran に渡す別の (適切な) 方法はありますか? これも問題ですか?

4

1 に答える 1

8

この問題は、C++ と Fortran の間のインターフェースにあるのではなく、廃止された Fortran の実装にあります。ファイル drc3jj.f は SLATEC ライブラリの一部であり、実行されているマシンに依存する定数 (マシン定数) を返すユーティリティ関数が含まれています。これらはファイルd1mach.fi1mach.fおよびで定義されていますr1mach.f

huge()ただし、Fortran 95 以降では、 、tiny()spacing()などの組み込み関数が存在しepsilon()、どのマシンでも適切な値を返すことが保証されています。

d1mach(int)解決策は、サブルーチン内のへの参照をすべて削除drc3jj()し、適切な組み込み関数に置き換えることです。

さらに、Fortran サブルーチンへの直接リンクは、コンパイラに依存するため、常に注意が必要です。iso_c_bindingfortran90 で使用して、タイプセーフな方法で C へのインターフェイスを定義することをお勧めします。

!wrapper.f90:

subroutine drc3jj_wrap(l2, l3, m2, m3, l1min, l1max, thrcof, ndim, ier) bind(C)

    use iso_c_binding
    implicit none

    real(c_double), value, intent(in)           :: l2, l3, m2, m3
    real(c_double), intent(out)                 :: l1min, l1max
    real(c_double), dimension(ndim), intent(out):: thrcof
    integer (c_int), value, intent(in)          :: ndim
    integer (c_int), intent(out)                :: ier

    interface
          SUBROUTINE DRC3JJ (L2, L3, M2, M3, L1MIN, L1MAX, THRCOF, NDIM, IER)
              INTEGER NDIM, IER
              DOUBLE PRECISION L2, L3, M2, M3, L1MIN, L1MAX, THRCOF(NDIM)
          end SUBROUTINE DRC3JJ
    end interface

    call DRC3JJ(l2, l3, m2, m3, l1min, l1max, thrcof, ndim, ier)

end subroutine drc3jj_wrap

// fortranLinkage2.h
#ifndef FORTRANLINKAGE_H
#define FORTRANLINKAGE_H

extern "C"
{
    extern void drc3jj_wrap(double l2, double l3, double m2, double m3,
                            double *l1max, double *l2max, double *thrcof,
                            int ndim, int *ier);
}

#endif // FORTRANLINKAGE_H

そして、あなたはただ電話します

void wigner3j(double l2, double l3, double m2, double m3, double coeff [])
{
    double l1min,l1max;
    int ierr,size(3);

    drc3jj_wrap(l2,l3,m2,m3,&l1min,&l1max,coeff,size,&ierr);

    cout << "Min: " << l1min << "\t Max: " << l1max << "\t Err: " << ierr << endl;
}

コンパイルして実行すると、

$ g++ -c foo2.cc
$ gfortran -c wrapper.f90 
$ gfortran -c drc3jj.f 
$ g++ -o foo2 foo2.o wrapper.o drc3jj.o -lgfortran
$ ./foo2 2 8 2 8
Min: 10  Max: 10     Err: 0
0.218218
2.07738e-317
0
gpc-f103n084-$ ./foo2 2 8 2 7
Min: 9   Max: 10     Err: 0
-0.102598
-0.19518
0
于 2013-08-15T15:57:06.460 に答える