当尝试捕获由 fortran 子例程调用的 c++ 函数引发的异常时,我有不同的(但不令人满意)行为,该子例程本身由 c++ main 调用。
testf.f
源文件中的fortran子程序是:
subroutine testf(simul)
external simul
call simul(0)
end
C++ 部分(主要和函数)位于
main.cpp
源文件:
#include <iostream>
using namespace std;
typedef void (*fun_t)(int *ind);
extern "C" void testf_(fun_t);
void cppsimul(int *i);
int main() {
cout << "in main\n";
try
{
testf_(cppsimul);
}
catch (int e)
{
cout << "Caught exception\n";
}
return 0;
}
void cppsimul(int *i)
{
cout << "in cppsimul\n";
throw 0;
}
在 Linux/x86_64 下使用纯 gnu 工具链(g++ (Ubuntu 9.4.0-1ubuntu1~20.04.3) 9.4.0)编译时,得到以下结果:
$ gfortran -c testf.f
$ g++ main.cpp testf.o -o main
$ ./main
in main
in cppsimul
Caught exception
事实上
-fexceptions
开关似乎没有必要。
在 x86_64 mac 上使用 conda 24.7.1 工具链(clang 18.1.4、gfortran 13.2.0)编译时,我得到:
$ gfortran -c testf.f
$ clang++ main.cpp testf.o -o main
$ ./main
in main
in cppsimul
libc++abi: terminating due to uncaught exception of type int
zsh: abort ./main
Caught exception
在arm64 mac上使用conda 24.7.1工具链(clang 18.1.4,gfortran 13.2.0)编译时,我得到:
$ gfortran -c testf.f
$ clang++ main.cpp testf.o -o main
$ ./main
in main
in cppsimul
zsh: trace trap ./main
当在 lldb 中运行时,我有以下跟踪:
in main
in cppsimul
Process 88839 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x18e794944)
frame #0: 0x000000018e794944 libunwind.dylib`libunwind::UnwindCursor<libunwind::LocalAddressSpace, libunwind::Registers_arm64>::step(bool) + 504
libunwind.dylib`libunwind::UnwindCursor<libunwind::LocalAddressSpace, libunwind::Registers_arm64>::step:
-> 0x18e794944 <+504>: brk #0xc471
0x18e794948 <+508>: pacib x16, x8
0x18e79494c <+512>: b 0x18e794a84 ; <+824>
0x18e794950 <+516>: mov x0, #0x0
Target 0: (main) stopped.
对于这两种架构,如果我将
clang++
替换为 /usr/bin/clang++
,我都会得到正常的输出(参考上面的 Linux),但对于更大的项目来说这样做是没有意义的(Apple /usr/bin/clang++ 的版本为 15.0) .0,来自命令行工具 15.1),因为 conda 包的所有依赖项都具有与工具链兼容的版本(clang 18.1.4)。
conda 工具链可能存在什么问题?系统的
/usr/lib/libc++
和 conda libc++ 之间可能不匹配,但怎么可能呢,因为 conda 包的构建应该彼此一致并与系统库一致。
如果专家能给出一些解释,我将不胜感激。
Fortran 和 C 不支持异常。 C++ 函数抛出异常会导致未定义的行为。
用 C++ 创建此函数的包装器,返回错误代码并在内部捕获异常。
这就是 C++ 和 C/Fortran 可以合作的方式。
某些编译器可以以非标准方式处理它的事实并不意味着它是通用或受支持的。