gfortran:将逻辑参数从 C 传递给 Fortran 函数

gfortran: pass logical argument to Fortran function from C

调用带有 logical 个参数的 Fortran 函数时,我应该在 C 中使用什么参数类型,特别是 gfortran? gfortran 的文档在哪里?

这是一个没有警告就无法编译的示例程序:

one.f 的内容:

      subroutine proc1(x)
      logical x
      end

main.c 的内容:

void proc1_(_Bool *x);

int main() {
    _Bool x;

    proc1_(&x);

    return 0;
}

如果我按如下方式使用 GCC 进行编译,并启用 LTO,我会收到有关函数原型不匹配的警告:

gfortran -flto -c one.f
gcc -flto -c main.c
gcc -flto main.o one.o

我收到的警告:

main.c:2:6: warning: type of 'proc1_' does not match original declaration [-Wlto-type-mismatch]
    2 | void proc1_(_Bool *x);
      |      ^
one.f:2:22: note: 'proc1' was previously declared here
    2 |       subroutine proc1(x)
      |                      ^
one.f:2:22: note: code may be misoptimized unless '-fno-strict-aliasing' is used

请注意,启用 LTO 允许链接器验证原型之间的参数类型是否匹配。不幸的是,使用 LTO 不是我们的选择。 CRAN 要求提交的代码在启用 LTO 的情况下编译时不会出现这些警告。

我只在尝试使用 logical 参数时才发现问题。 realintegercharacter 都可以。

可以要求 gfortran 生成 C 原型,这是它给我的输出:

gfortran -flto -fc-prototypes-external -c one.f
void proc1_ (int_fast32_t *x);

在 C 原型中使用 int_fast32_t 也不起作用。我试过的类型都没有,int_Bool 都没有。通常,当原型之间存在类型不匹配时,错误消息会提到类型应该是什么——但在这种情况下不会。

如何找到要使用的正确类型?

对于真正的现代 C-Fortran 互操作性,您应该使用 iso_c_binding 模块提供的类型(种类)并制作您的 Fortran 过程 bind(c)。这样你就可以使用 logical(c_bool).

在旧式中,最好的办法是使用整数并传递一个 int,并且只在 Fortran 中更正从 integerlogical。老C没有bool,是后来加的

变化最小:

      subroutine proc1(x)
      use iso_c_binding
      logical(c_bool) x
      end
#include <stdbool.h>
void proc1_(bool *x);

int main() {
    bool x;

    proc1_(&x);

    return 0;
}
> gfortran -flto -c one.f
> gcc -flto -c main.c
> gcc -flto main.o one.o

在我的 Linux 和 GCC 7 和 10 上没有发出警告。

或进一步修改后:

      subroutine proc1(x) bind(C, name="proc1")
      use iso_c_binding
      logical(c_bool), value :: x
      end
#include <stdbool.h>
void proc1(bool x);

int main() {
    bool x;

    proc1(x);

    return 0;
}

当然只有当它确实只是一个输入参数时才改变为按值传递。

正如 Vladimir F 在回答中所解释的那样,正确且保证可移植的解决方案是创建一个使用 ISO_C_BINDING 的 Fortran 包装器例程。这个包装器也可以借此机会制作一个更惯用的 C 接口,例如使用 value 说明符按值传递标量。

但是,对于在没有 LTO 的情况下适用于 GFortran 的快速而肮脏的解决方案(并且在某种程度上可能适用于其他编译器,但不能保证),请参阅 https://gcc.gnu.org/onlinedocs/gfortran/Internal-representation-of-LOGICAL-variables.html#Internal-representation-of-LOGICAL-variables 。也就是说,您可以传递一个适当大小的 C 整数变量,其中包含 1 表示真,0 表示假。这里适当的大小意味着除非你用 -fdefault-integer-8 或这样的编译选项编译你的 Fortran 代码,否则 GFortran 默认类型逻辑将是 4 个字节,所以一个普通的 C int 应该是好的(或 int32_t 如果你真的想确定的话,尽管我认为 GFortran 不支持任何 C int 不是 32 位的目标。

这不适用于 LTO 的原因是,虽然上述方法有效,但在 GCC 的内部,Fortran LOGICAL 变量几乎与整数相同,但不完全相同。所以在实践中,它们是具有最大值 1 和最小值 0 的特殊整数变量,即使它们占用更多 space(由它们的 kind 参数指定)。所以这种类型不匹配很可能就是它所抱怨的。不幸的是,除了上述通过 ISO_C_BINDING.

的正确解决方案外,没有解决方案