5

レガシー Fortran ライブラリへの C++ インターフェイスを用意しました。

レガシー ライブラリの一部のサブルーチンは、醜いが使用可能なステータス コード規則に従ってエラーを報告します。私はそのようなステータス コードを使用して、C++ コードから読み取り可能な例外をスローします。うまく機能します。

一方で、レガシー ライブラリがSTOP(プログラムを終了させる) 呼び出しを行う場合があります。そして、状態が回復可能であっても、それを行うことがよくあります。

これを C++ 内からキャプチャしたいのですが、これまでのところ成功していません。STOP

次のコードは単純ですが、当面の問題を正確に表しています。

Fortran レガシー ライブラリfmodule.f90:

module fmodule
  use iso_c_binding
  contains
    subroutine fsub(x) bind(c, name="fsub")
      real(c_double) x
      if(x>=5) then 
         stop 'x >=5 : this kills the program'
      else
         print*, x
      end if
    end subroutine fsub    
end module fmodule

C++ インターフェイスmain.cpp:

#include<iostream>

// prototype for the external Fortran subroutine
extern "C" {
  void fsub(double& x);  
}

int main() {  
  double x;
  while(std::cin >> x) {
    fsub(x);
  }
  return 0;
}

コンパイル行 (GCC 4.8.1 / OS X 10.7.4;$コマンドプロンプトを示します):

$ gfortran -o libfmodule.so fmodule.f90 -shared  -fPIC -Wall
$ g++ main.cpp -L. -lfmodule -std=c++11

実行:

$ ./a.out 
1
   1.0000000000000000     
2
   2.0000000000000000     
3
   3.0000000000000000     
4
   4.0000000000000000     
5
STOP x >=5 : this kills the program

どうすればその番号をキャプチャしてSTOP、別の番号をリクエストできるでしょうか。Fortran コードには触れたくないことに注意してください。

私が試したこと:

  • std::atexit: 一度入力すると「戻る」ことはできません
  • std::signal:STOPキャプチャできるシグナルをスローしていないようです
4

4 に答える 4

5

あなたが望むものはとにかく移植性のないコードになるので、あいまいなロングジャンプメカニズムを使用して終了メカニズムを覆すだけではどうですか:

#include<iostream>
#include<csetjmp>
#include<cstdlib>

// prototype for the external Fortran subroutine
extern "C" {
  void fsub(double* x);  
}

volatile bool please_dont_exit = false;
std::jmp_buf jenv;

static void my_exit_handler() {
  if (please_dont_exit) {
    std::cout << "But not yet!\n";
    // Re-register ourself
    std::atexit(my_exit_handler);
    longjmp(jenv, 1);
  }
}

void wrapped_fsub(double& x) {
  please_dont_stop = true;
  if (!setjmp(jenv)) {
    fsub(&x);
  }
  please_dont_stop = false;
}

int main() {
  std::atexit(my_exit_handler);  
  double x;
  while(std::cin >> x) {
    wrapped_fsub(x);
  }
  return 0;
}

呼び出しは、呼び出しlongjmpのある行の真ん中にジャンプし、 の 2 番目の引数として渡された値を返します。それ以外の場合は 0 を返します。出力例 (OS X 10.7.4、GCC 4.7.1):setjmpsetjmplongjmpsetjmp

$ ./a.out 
2
   2.0000000000000000     
6
STOP x >=5 : this kills the program
But not yet!
7
STOP x >=5 : this kills the program
But not yet!
4
   4.0000000000000000
^D     
$

ライブラリのプリロードは必要ありません (Linux よりも OS X の方が少し複雑です)。ただし、警告の言葉 - 終了ハンドラーは、登録の逆の順序で呼び出されます。の後に他の終了ハンドラーが登録されないように注意する必要がありますmy_exit_handler

于 2013-10-26T14:47:12.370 に答える
1

_gfortran_stop_stringカスタム関数とを使用する 2 つの回答を組み合わせると、カスタムlongjmp関数内で例外を発生させるのは似ていると思い、メイン コードでキャッチします。だからこれが出てきた:

main.cpp:

#include<iostream>

// prototype for the external Fortran subroutine
extern "C" {
  void fsub(double& x);  
}

int main() {  
  double x;
  while(std::cin >> x) {
    try { fsub(x); }
    catch (int rc) { std::cout << "Fortran stopped with rc = " << rc <<std::endl; }
  }
  return 0;
}

catch.cpp:

extern "C" {
    void _gfortran_stop_string (const char*, int);
}

void _gfortran_stop_string (const char *string, int len)
{
        throw 666;
}

次に、コンパイルします。

gfortran -c fmodule.f90
g++ -c catch.cpp
g++ main.cpp fmodule.o catch.o -lgfortran

ランニング:

./a.out
2
   2.0000000000000000     
3
   3.0000000000000000     
5
Fortran stopped with rc = 666
6
Fortran stopped with rc = 666
2
   2.0000000000000000     
3
   3.0000000000000000     
^D

だから、うまくいくようです:)

于 2013-10-28T13:27:15.987 に答える
0

Fortran コードを呼び出す前にプロセスをフォークし、Fortran の実行後に 0 を終了することをお勧めします (編集: STOP がゼロで終了する場合は、センチネル終了コードが必要になります。そうすれば、すべての fortran 呼び出しが同じ方法で終了します: 停止した場合と同じです。または、「STOP」でエラーが確実に発生する場合は、Fortran コードが停止したときに例外をスローし、Fortran の実行が正常に「完了」したときに別のメッセージを送信します。

以下は、fortran の「STOP」がエラーであると仮定したコードからインスパイアされた例です。

 int main() {  
   double x;
   pid_t pid;
   int   exit_code_normal = //some value that is different from all STOP exit code values
   while(std::cin >> x) {
     pid = fork();
     if(pid < 0) {
       // error with the fork handle appropriately
     } else if(pid == 0) {
       fsub(x);
       exit(exit_code_normal);
     } else {
       wait(&status);
       if(status != exit_code_normal)
          // throw your error message.
     }
   }
   return 0;
 }

終了コードは、変数ではなく定数である可能性があります。あまり重要ではないと思います。

コメントに続いて、実行の結果がプロセスのメモリにある場合 (たとえば、ファイルに書き込むのではなく) 失われることがあります。もしそうなら、私は3つの可能性を考えることができます:

  • Fortran コードは、呼び出し中に大量のメモリを台無しにします。実行を STOP を超えて継続させることは、そもそも良い考えではありません。
  • Fortran コードは単に何らかの値を返します (私の fortran があまり錆びていない場合は、その引数を介して)。これは、共有メモリ空間を介して親に簡単に中継できます。
  • fortran サブルーチンの実行は外部システム (例: ファイルへの書き込み) で行われ、戻り値は期待されません。

3番目のケースでは、上記の私の解決策はそのまま機能します。主な理由として、他の提案された解決策よりもそれを好みます: 1) ビルド プロセスが適切に維持されていることを確認する必要がない 2) fortran "STOP" が期待どおりに動作する 3) 非常に少ないコード行とすべての " fortran STOP 回避策" ロジックが 1 か所にまとめられています。したがって、長期的なメンテナンスに関しては、私はそれをはるかに好みます。

2 番目のケースでは、上記のコードを少し変更する必要がありますが、追加の複雑さを最小限に抑えて、上で列挙した利点を保持しています。

最初のケースでは、何があっても fortran コードをいじる必要があります。

于 2013-10-25T18:17:23.727 に答える