【问题标题】:gfortran: pass logical argument to Fortran function from Cgfortran:将逻辑参数从 C 传递给 Fortran 函数
【发布时间】:2021-11-02 18:50:21
【问题描述】:

在调用采用 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 prototype gfortran


    【解决方案1】:

    为了实现真正的现代 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;
    }
    

    当然只有当它确实只是一个输入参数时才会更改为pass-by-value。

    【讨论】:

    • 我没有尝试编译和运行代码,但是你可能需要有logical, value :: x并在c代码中添加bind(c,name="proc1")'. You then can simply do proc1(x)`。
    • 感谢弗拉基米尔的回答。不幸的是,我们正在与旧的 Fortran 代码(主要是 F77)进行交互。事实上,它是 ARPACK 的一部分。您是否知道不需要对 Fortran 源进行大量修改的解决方案?
    • @Szabolcs 不,但您始终可以编写一个中间 Fortran 过程来调用旧的 Fortran 过程。
    • @steve 我确实提到了bind(C),但我想演示数据类型中所需的最少更改。
    • @VladimirF,是的,我知道您试图保持简短。我的评论只是为了补充您的评论,因为新使用 bind(c) 的人遇到的最大问题之一是省略了 value 属性。
    【解决方案2】:

    正如 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 的特殊整数变量,即使它们占用更多空间(由它们的 kind 参数指定)。所以这种类型不匹配很可能就是它所抱怨的。不幸的是,除了上述通过 ISO_C_BINDING 的正确解决方案之外,没有解决方案。

    【讨论】:

    • 您是否检查过它不会产生警告?因为 Szabolcs 写道他们尝试过int
    • @VladimirF 我添加了一个部分来解释为什么它不适用于 LTO。
    • 谢谢,这解释了为什么没有解决方案可以消除警告。但它也表明使用正确大小的整数类型是安全的,确保值仅为 0 或 1,并忽略警告。 (遗憾的是,我们需要消除警告,但至少我们现在清楚选项。)
    猜你喜欢
    • 2016-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-26
    • 2014-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多