我准备了一个用于遗留Fortran库的C++接口(interface)。

旧版库中的某些子例程遵循丑陋但可用的状态代码约定来报告错误,并且我使用此类状态代码从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++接口(interface)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似乎没有发出可以捕获
  • 的信号

    最佳答案

    您可以通过拦截Fortran运行时对exit函数的调用来解决您的问题。见下文。 a.out是使用您的代码和您提供的编译行创建的。

    步骤1.找出要调用的函数。启动gdb

    $ gdb ./a.out
    GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6_4.1)
    [...]
    (gdb) break fsub
    Breakpoint 1 at 0x400888
    (gdb) run
    Starting program: a.out
    5
    
    Breakpoint 1, 0x00007ffff7dfc7e4 in fsub () from ./libfmodule.so
    (gdb) step
    Single stepping until exit from function fsub,
    which has no line number information.
    stop_string (string=0x7ffff7dfc8d8 "x >=5 : this kills the programfmodule.f90", len=30) at /usr/local/src/gcc-4.7.2/libgfortran/runtime/stop.c:67
    

    因此stop_string被调用。我们需要知道该功能对应于哪个符号。

    步骤2.找到stop_string函数的确切名称。它必须在共享库之一中。
    $ ldd ./a.out
        linux-vdso.so.1 =>  (0x00007fff54095000)
        libfmodule.so => ./libfmodule.so (0x00007fa31ab7d000)
        libstdc++.so.6 => /usr/local/gcc/4.7.2/lib64/libstdc++.so.6 (0x00007fa31a875000)
        libm.so.6 => /lib64/libm.so.6 (0x0000003da4000000)
        libgcc_s.so.1 => /usr/local/gcc/4.7.2/lib64/libgcc_s.so.1 (0x00007fa31a643000)
        libc.so.6 => /lib64/libc.so.6 (0x0000003da3c00000)
        libgfortran.so.3 => /usr/local/gcc/4.7.2/lib64/libgfortran.so.3 (0x00007fa31a32f000)
        libquadmath.so.0 => /usr/local/gcc/4.7.2/lib64/libquadmath.so.0 (0x00007fa31a0fa000)
        /lib64/ld-linux-x86-64.so.2 (0x0000003da3800000)
    

    我在fortran运行时中发现了它(毫不奇怪)。
    $ readelf -s /usr/local/gcc/4.7.2/lib64/libgfortran.so.3|grep stop_string
      1121: 000000000001b320    63 FUNC    GLOBAL DEFAULT   11 _gfortran_stop_string@@GFORTRAN_1.0
      2417: 000000000001b320    63 FUNC    GLOBAL DEFAULT   11 _gfortran_stop_string
    

    步骤3.编写一个将替换该函数的函数

    我在源代码中寻找函数的精确签名(/usr/local/src/gcc-4.7.2/libgfortran/runtime/stop.c请参见gdb session )
    $ cat my_exit.c
    #define _GNU_SOURCE
    #include <stdio.h>
    
    void _gfortran_stop_string (const char *string, int len)
    {
            printf("Let's keep on");
    }
    

    步骤4.编译导出该符号的共享库。
    gcc -Wall -fPIC -c -o my_exit.o my_exit.c
    gcc -shared -fPIC -Wl,-soname -Wl,libmy_exit.so -o libmy_exit.so my_exit.o
    

    步骤5.使用LD_PRELOAD运行程序,以便我们的新函数优先于运行时的函数
    $ LD_PRELOAD=./libmy_exit.so ./a.out
    1
       1.0000000000000000
    2
       2.0000000000000000
    3
       3.0000000000000000
    4
       4.0000000000000000
    5
    Let's keep on   5.0000000000000000
    6
    Let's keep on   6.0000000000000000
    7
    Let's keep on   7.0000000000000000
    

    妳去

    关于c++ - 从C++截取Fortran STOP,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19596375/

    10-14 11:05