0

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

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

这应该很“容易”,但我所做的搜索都没有产生足够的片段让我创建一个工作程序。

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

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

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

添加最后一个参数是为了让 Fortran 不必担心尾随的 null(我在 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

帮助?

4

2 回答 2

1

除了 MSB 对参数计数的评论之外,您的接口块语法已失效,并且您需要将一些项目引入接口主体的范围。

请注意,*由于 write 语句中的单元是 OUTPUT_UNIT 的同义词(并且更为典型)(并且 PRINT 语句也写入该单元 - 如果您想要最少的代码)。我还建议在 str 输出项周围使用 TRIM() 。同样, LEN_TRIM(str) 可能是作为第三个参数传递给的合理长度send_to_port

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

您的代码包含对fdateand的引用sleep,它们不是标准的内在函数。请注意,这些行为在您的编译器之间可能会有所不同(我认为您没问题 - 但您应该检查一下。其中第一个可能会被 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
于 2014-09-03T04:47:04.433 回答
0

(是的,反对 fortran-iso-c-binding question 标签......)

如果你不能使 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)

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

于 2014-09-04T17:37:01.110 回答