使用C_LOC和C_F_POINTER进行Fortran序列化

时间:2015-05-06 04:49:04

标签: serialization fortran fortran2003

我正在寻找Fortran库或将数据序列化到Fortran内存缓冲区的首选方法。

在研究了这个主题之后,我找到了使用EQUIVALENCE语句和TRANSFER内部函数的例子。我编写了代码来测试它们,但它们都有效。在我的有限测试中,传递函数似乎比等价语句慢得多。但是,我发现有几条引用说明一般不使用等价语句。

所以,我一直试图想出另一种有效序列化数据的方法。在准备好Fortran 2003规范之后,我发现我可以一起使用C_LOC和C_F_POINTER来构建我的"字节"数组到所需的数据类型(int,real等)。初步测试显示它正在工作并且比传递功能更快。下面列出了一个示例程序。我想知道这是否有效地使用了C_LOC和C_F_POINTER函数。

谢谢!

  program main
    use iso_c_binding
    implicit none


    real(c_float)             :: a, b, c
    integer(c_int8_t), target :: buf(12)

    a = 12345.6789_c_float
    b = 4567.89123_c_float
    c = 9079.66788_c_float

    call pack_float( a, c_loc(buf(1)) )
    call pack_float( b, c_loc(buf(5)) )
    call pack_float( c, c_loc(buf(9)) )

    print '(A,12I5)', 'Bin: ', buf


  contains


    subroutine pack_float( src, dest )
      implicit none
      real(c_float), intent(in) :: src
      type(c_ptr), intent(in)   :: dest
      real(c_float), pointer    :: p
      call c_f_pointer(dest, p)
      p = src
    end subroutine


  end program

输出:

Bin:   -73  -26   64   70   33  -65 -114   69  -84  -34   13   70

我还用Python编写了这个代码来仔细检查上面的答案。代码和输出如下所示。

import struct

a = struct.pack( '3f', 12345.6789, 4567.89123, 9079.66788)
b = struct.unpack('12b', a)
print b

输出:

(-73, -26, 64, 70, 33, -65, -114, 69, -84, -34, 13, 70)

1 个答案:

答案 0 :(得分:1)

实际上,以这种方式使用C_LOC和C_F_POINTER可能会起作用,但正式地说它不符合标准。传递给C_F_POINTER的Fortran指针的类型和类型参数必须可以与C地址指定的对象互操作,或者与最初传递给C_LOC的对象的类型和类型参数相同(请参阅FPTR的说明) F2008中的论点15.2.3.3)。根据您尝试序列化的内容,您可能还会发现对C_LOC参数的正式限制(在后来的标准中比F2003的限制性越来越小)开始发挥作用。

(C等价物需要使用unsigned char来实现这种技巧 - 这与int8_t不一定相同。)

EQUIVALENCE集中的项目存在约束,使得该方法通常不适用(参见F2008中的约束C591至C594)。通过等价来解释对象的内部表示也正式受制于变量定义和未定义的规则 - 特别参见F2008 16.6.6第1项。

在Fortran中访问一个对象作为不同类型的表示的一致方式是通过TRANSFER。请注意,使用可分配或指针组件(您的动态字段的含义?)对派生类型的内部表示进行序列化可能没有用。

根据具体情况,只需将实时结果存储在要存储的事物类型的数组中,就可能更简单,更健壮。