【问题标题】:Fortran functions with C interface result in undefined references when they try to call each other, why?具有 C 接口的 Fortran 函数在尝试相互调用时会导致未定义的引用,为什么?
【发布时间】:2014-01-14 20:25:21
【问题描述】:

我有一个旧的 fortran 代码,出于各种原因,我正在修改它以提供使用 c++ 的功能。

在这段代码中有两个函数,CALC 和 CALC2。这些函数需要从c++中调用,并且相互调用。

CALC 的示意图:

SUBROUTINE CALC(W,NW,DW,IDMX,JUMP,POINT) bind(C, name="CALC")
    use iso_c_binding
C   Some statements
    IF (LO(36)) CALL CALC2(W,NW,DW,IDMX, .TRUE., 14)
C   More statements
END

CALC2 的草图:

SUBROUTINE CALC2(W,NW,DW,IDMX,JUMP,POINT) bind(C, name="CALC2")
    use iso_c_binding
C   Some statements
    IF (LO(222)) CALL CALC(W,NW,DW,IDMX, .TRUE., 2)
C   More statements
END

我当前的 main.cpp:

extern "C" {
void CALC(double *w, double *nw, double *dw, int *idmx, int* jump, int* point);
void CALC2(double *w, double *nw, double *dw, int *idmx, int* jump, int* point);
}

int main()
{
    int length = 600000;
    int jump = 0;
    int point = 0;
    double *w = new double[length];
    CALC( w, w, w, &length, &jump, &point);
    CALC2( w, w, w, &length, &jump, &point);
    return 0;
}

运行我的 make 文件,一切都可以正确编译,但是在链接阶段我得到了这个:

g++ (big list of .o files) -lgfortran -o ecis
b1_calc.o: In function `CALC':
b1_calc.f:(.text+0x16b): undefined reference to `calc2_'
bq_calc.o: In function `CALC2':
bq_calc.f:(.text+0x1e52): undefined reference to `calc_'
bq_calc.f:(.text+0x1ec0): undefined reference to `calc_'
collect2: error: ld returned 1 exit status
make: *** [ecis] Error 1

为什么会这样,我该如何解决?

【问题讨论】:

  • 我不确定,但我知道 CALC 和 CALC2 各自调用大量子例程,每个子例程都在另一个文件中定义,没有问题,据我所知,唯一的区别是我对这两个子例程进行了修改,使它们可以从 C++ 中调用。

标签: c++ gcc fortran gfortran fortran-iso-c-binding


【解决方案1】:

[编辑,感谢 Vladimir F 和 eriktous 指出标准需要接口;因此指出我原来的(未经测试的)解决方案不太正确。]

绑定标签(bind(c) 的 name= 部分)是 C 处理器的标识符,因此它特别区分大小写并且在最终符号中不会有 gfortran 样式的装饰)。但是,如果没有可用的接口,gfortran 将在 CALC2 中创建对小写符号 calc_ 的引用(如链接器输出中所给出)。

因此,您需要让 gfortran/linker 知道正确的符号。您可以通过提供一个接口来做到这一点。另外,请注意,当使用 bind(c) 时,该标准需要一个显式接口 - 但即使不符合标准,您也可以让这个“工作”。

创建显式接口的一种简单方法是使用模块:将子例程放在一个文件中,module calcs/end module calcs 在顶部/底部。或者:

subroutine calc(...)
  ...
  interface
    subroutine calc2(...) bind(c, name='CALC2')
      ...
    end subroutine
  end interface
  call CALC2(...)
end subroutine

此外,无论如何,有接口是好的,因为它可以进行大量检查。

或者,将绑定标签更改为小写并附加下划线。这可能很有效,但超出了标准和特定于实现的范围。将函数放在单独的文件中会阻碍编译器检测到错误的机会。

【讨论】:

  • 啊,好吧,这很有道理,我现在就做出改变。非常感谢!
【解决方案2】:

从 Fortran 调用函数时,必须为函数提供显式接口。你正在互相调用它们(在 Fortran 中)。将它们放在模块中或使用接口块。

【讨论】:

  • 您能详细说明一下吗?我是 fortran 的新手,乍一看,我看到这些子例程调用了许多其他子例程,每个子例程都在自己的文件中,而且它们都没有接口块,为什么将它们绑定到 C 会使它们需要接口块?跨度>
  • @JamesMatta:简短回答:因为标准是这样说的。
猜你喜欢
  • 2018-12-31
  • 2016-07-15
  • 2011-11-21
  • 1970-01-01
  • 1970-01-01
  • 2016-09-18
  • 2020-01-19
  • 2016-06-14
  • 1970-01-01
相关资源
最近更新 更多