从fortran调用C(ifort,gfortran)

时间:2014-09-02 22:55:22

标签: c string fortran call fortran-iso-c-binding

我是一名C程序员,他必须通过添加对C函数的单个调用来更新庞大的Fortran 2003程序。

首先,我需要编写一个最小的Fortran包装器(在现代的,自由形式的Fortran中,没有喊叫),它将使用包含循环计数器的字符串正确调用C函数(如果可能的话,还有日期/时间) ,从一个循环中。

这应该是“简单的”,但我所做的搜索都没有为我创建足够的片段来创建一个有效的程序。

我在64位Linux下使用最近的64位版本的gfortran和Intel ifort编译器,测试代码需要使用两个编译器进行编译。

这是C定义,在send_to_port.c文件中:

int send_to_port(int port, char *data, unsigned int length);

添加了最后一个参数以允许Fortran不必担心尾随空值(我在C中处理它:data [length] ='\ 0';)。我理解长度参数是由Fortran“自动”添加的,因此Fortran调用只有两个参数,即整数端口号和要发送的字符串。

我希望使用以下gfortran行编译代码,再加上ifort的等效代码:

gfortran -ffree-form test.f -o test send_to_port.o

我正在寻找最少的代码:我认为应该是10-20行,但我不知道Fortran。这是我当前的test.f编辑缓冲区(无法编译):

use iso_c_binding
use iso_fortran_env, stdout => output_unit
implicit none

! Fortran interface to C routine:
!   int send_to_port(int port, char *data, unsigned int length);
interface
  integter(c_int) function send_to_port(port, data) bind(C)
  integer(c_int), value :: port
  character(kind=c_char) :: data(*)
end interface

integer(c_int) retval, cnt, port
character(1024) str

cnt = 0
port = 5900

do  ! Infinite loop (^C to exit)
  call fdate(date)
  cnt = cnt + 1
  write(str, "(A,A,I8)") date, ": Iteration = ", cnt
  write(stdout, *) str  ! Show what's about to be sent
  retval = send_to_port(port, str)  ! Send it
  write(stdout, *) retval  ! Show result
  call sleep(1)
end do

end

帮助?

2 个答案:

答案 0 :(得分:1)

除了M. S. B.关于参数计数的注释之外,接口块的语法已经完成,并且您需要将一些项引入接口体的范围。

请注意,*作为write语句中的单位是OUTPUT_UNIT的同义词(更常见)(如果您想要最少的代码,PRINT语句也会写入该单元)。我还建议在str输出项周围使用TRIM()。类似地,LEN_TRIM(str)可能是一个合理的长度,作为send_to_port的第三个参数传递。

不要使用.f作为自由格式Fortran源代码的扩展名 - 请改用.f90。

您的代码包含对fdatesleep的引用,这些引用不是标准的内在函数。请注意,这些行为可能会在您的编译器之间有所不同(我认为您没问题 - 但您应该检查。第一个可能被DATE_AND_TIME内在替换,以及一些适当的文本格式代码,以获得更便携的解决方案。

use, intrinsic :: iso_c_binding, only: c_int, c_char
implicit none

interface
  function send_to_port(port, data, length) bind(C)
    use, intrinsic :: iso_c_binding, only: c_int, c_char
    implicit none
    integer(c_int), value :: port
    character(kind=c_char) :: data(*)
    integer(c_int), value :: length
    integer(c_int) :: send_to_port
  end function send_to_port
end interface

integer(c_int) :: retval, port
integer :: cnt    
character(len=1024,kind=c_char) :: str
character(30) :: date

cnt = 0
port = 5900_c_int

do  ! Infinite loop (^C to exit)
  call fdate(date)
  cnt = cnt + 1
  write(str, "(A,A,I8)") trim(date), ": Iteration = ", cnt
  print *, trim(str)  ! Show what's about to be sent
  retval = send_to_port(port, str, len_trim(str))  ! Send it
  print *, retval  ! Show result
  call sleep(1)
end do
end

答案 1 :(得分:0)

(是的,违反了fortran-iso-c-binding问号...)

如果你不能使iso-c绑定工作...根据编译器版本,我遇到了一些问题,我更喜欢在混合C和FORTRAN时使用金属:  避免接口只是创建一个"包装器"为你的C函数。

包装指南大致如下:

  • 函数名必须以_结尾(在Linux上。在Windows上,函数名必须是ALL_CAPS,没有尾随_)

  • 如果在C ++程序中编译,请定义为extern" C"

  • 所有参数都是指针

  • 在内存中,多维数组索引相反[i] [j]是[j] [i]

所以C代码是:

extern "C" 
void send_to_port_fort_(int* port, char *data, int* length, int *result)
{
  *result = send_to_port(*port,data,*length);
}

然后来自fortran

call send_to_port_fort(port,data,size(data),retval)

由于没有接口语句,因此没有或参数大小/类型检查也没有转换