【问题标题】:How do I concatenate a NULL character to a character array in Fortran to call a c function?如何将 NULL 字符连接到 Fortran 中的字符数组以调用 c 函数?
【发布时间】:2021-09-28 08:53:10
【问题描述】:

我有以下测试功能:

#include <stdlib.h>
#include <stdio.h>

void print_string(char *text);

void print_string(char *text) {
    printf("---\n%s\n---\n", text);
}

以下模块使用 ISO c 绑定封装了 Fortran 子例程的调用:

module test_c_lib

        use iso_c_binding
        implicit none
                
contains
        
    subroutine test(text)
    
            use iso_c_binding
            
            character,intent(in)       :: text(:)
            
            ! Interface to C function
            interface
                    subroutine c_print_string(t) bind(C, name="print_string")
                            import
                            character(kind=c_char) :: t(:)
                    end subroutine
            end interface
            
            ! Call C function
            print *,"AAAAA"
            print *,text
            print *,"AAAAA"         
            
            call c_print_string(text // C_NULL_CHAR)
            
    end subroutine
    
end module

使用 ISO C BINDINGS 在 Fortran 中定义子例程的方法摘自此文档 link。我进一步封装了一点f2py不支持iso c绑定。

我通过makefile编译所有内容:

$ cat makefile 
f_mod.so:       f_mod.f90 c_lib.o
                f2py -c f_mod.f90 c_lib.o -m f_mod

c_lib.o:        c_lib.c
                gcc -c -fpic c_lib.c -o c_lib.o

它编译但是:

  1. 我收到以下警告:
      150 |                 call c_print_string(text // C_NULL_CHAR)
          |                                    1
    Warning: Character length mismatch (2/1) between actual argument and assumed-shape dummy argument 't' at (1) [-Wargument-mismatch]
  1. 通过import fmod; f_mod.test_c_lib.test("Foo") 调用时得到以下输出:
     AAAAA
     Foo
     AAAAA
    ---
    8v$
    ---

所以f2py 正在工作,但是当将text // C_NULL_CHAR 作为参数传递时,它似乎不起作用,因为我从 C 函数输出中得到了垃圾。

【问题讨论】:

  • 在风格说明上,已经看到了您最近提出的一些问题:假设默认的内部类型参数是可互操作的参数可能是不好的做法。在这里,您假设默认的种类字符与可互操作的字符相同。您可能最好在整个过程中明确地使用可互操作的类型,或者明确地转换它们。

标签: fortran character f2py fortran-iso-c-binding


【解决方案1】:

你有两个错误:

要使字符伪参数与char * C 参数互操作,t 应该是assumed size array

character(kind=c_char) :: t(*)   ! Assumed size, not assumed shape

您还可以使用CFI_cdesc_t C 类型来让t 具有假定的形状数组或假定的长度标量,但这要先进得多。

即使使t 假定大小,您也没有工作程序,因为下一个问题:// 的基本性质。

由于text 是一个(假定的形状)数组,连接text // C_NULL_CHAR 是在元素上完成的,给出长度为2 的数组1text 的每个元素与C 空字符连接.然后,C 函数会看到类似于 [text(1), C_NULL_CHAR, text(2), C_NULL_CHAR, ...] 的输入。

要添加一个长度为 1 且带有 C_NULL_CHAR 的字符数组,您需要使用数组构造函数:

call c_print_string([text,C_NULL_CHAR])

1 参数长度为 2 是“字符长度不匹配”警告的原因。

【讨论】:

  • 对这两个问题的超清晰解释,不仅修复了它们,而且清楚地阐明了为什么我同时收到警告和错误。一旦我修复了假定的大小与假定的形状问题,使用// 正在制作C 打印F 而不是Foo,这与解释一致。我也不知道(*)(:) 之间有什么区别,现在很清楚了。
  • 我添加了一个链接以帮助其他人了解这两种数组类型之间的区别。
【解决方案2】:

@francescalus 的回答是正确的。但是由于我在看到他的答案之前解决了它,所以我在这里发布了功能齐全的代码:

module test_c_lib

        use iso_c_binding
        implicit none

contains

    subroutine test(text)

        use iso_c_binding

        character(*),intent(in) :: text

        ! Interface to C function
        interface
                subroutine c_print_string(t) bind(C, name="print_string")
                        import
                        character(kind=c_char) :: t(*)
                end subroutine
        end interface

        ! Call C function
        print *,"Inside Fortran"
        print *,text
        print *,"Inside Fortran"

        call c_print_string(text // C_NULL_CHAR)

    end subroutine

end module test_c_lib

program test_prog
    use test_c_lib
    call test(text = "Hello C World")
end program test_prog

注意subroutine test(text)的接口和C函数接口print_string()的变化。通过以下命令编译代码,

icl print_string.c -c
ifort test_c_lib.f90 -c
ifort *.obj /exe:main.exe

目前我不确定这是英特尔编译器中的一个错误,还是符合标准的行为不需要@francecalus 的最后一次更正来转换 text // C_NULL_CHAR[text,C_NULL_CHAR]。当然ifort 不需要它。这是输出:

> main.exe
 Inside Fortran
 Hello C World
 Inside Fortran
---
Hello C World
---

【讨论】:

  • 关于串联,您还将虚拟参数text 更改为假定长度的标量,而不是假定大小或假定形状的数组。这是一个重大变化,意味着text // C_NULL_CHAR 是两个标量的串联,而不是一个数组和一个标量。
  • 但是,我仍然对假定长度字符是否以及如何与 C 互操作感到困惑。它可以互操作吗?或者只是幸运地看到这段代码正常工作?通常,我会假设在传递给 C 之前需要转换为字符向量。
  • 这不是“可互操作”的问题。标量字符实际参数text 可以是使用序列关联与数组字符虚拟参数t 关联的参数。 (无论过程是由 Fortran 还是其他方式定义的,都是如此。)
  • 提供的示例在我的环境(f2py、gfortran、FreeBSD)中不起作用,无论如何我都遵循了 francescalus 的两个建议并且它们有效,而且我理解解释。还需要[text, C_NULL_CHAR] 而不是text // C_NULL_CHAR
  • 这可能是您的 gfortran 的一个错误。您使用的是哪个版本? GFortran 10 编译运行愉快,没有问题。
猜你喜欢
  • 2016-11-07
  • 1970-01-01
  • 1970-01-01
  • 2021-09-05
  • 2020-04-02
  • 2020-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多