使用 C_LOC 和 C_F_POINTER 的 Fortran 序列化
Fortran serialization using C_LOC and C_F_POINTER
我正在寻找 Fortran 库或在 Fortran 中将数据序列化到内存缓冲区的首选方法。
研究主题后,我找到了使用 EQUIVALENCE 语句和 TRANSFER 内部函数的示例。我写了代码来测试它们,它们都起作用了。在我的有限测试中,传递函数似乎比等价语句慢很多。但是,我发现一些参考文献指出一般不使用等价语句。
所以,我一直在尝试想出另一种有效序列化数据的方法。在准备好 Fortran 2003 规范后,我发现我可以一起使用 C_LOC 和 C_F_POINTER 将我的 "byte" 数组转换为所需的数据类型(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)
实际上,以这种方式使用 C_LOC 和 C_F_POINTER 可能有效,但形式上它不符合标准。传递给 C_F_POINTER 的 Fortran 指针的类型和类型参数必须与 C 地址指定的对象互操作,或者与最初传递给 [=20= 的对象的类型和类型参数相同](参见 F2008 15.2.3.3 中 FPTR 参数的说明)。根据您尝试序列化的内容,您可能还会发现对 C_LOC 参数的正式限制(在后来的标准中比 F2003 的限制逐渐减少)开始发挥作用。
(C 等价物需要使用 unsigned char 来实现这种技巧——这不一定与 int8_t 相同。)
EQUIVALENCE 集中的项目存在限制,使得该方法不普遍适用(请参阅 F2008 中的限制 C591 到 C594)。通过等价解释对象的内部表示形式也正式服从变量定义和未定义的规则 - 特别参见 F2008 16.6.6 第 1 项。
在 Fortran 中访问一个对象作为不同类型的表示的一致方法是通过 TRANSFER。请注意,使用可分配或指针组件对派生类型的内部表示进行序列化(这就是动态字段的意思吗?)可能没有用。
根据具体情况,将实时结果简单地存储在要存储的事物类型的数组中可能更简单、更可靠。
我正在寻找 Fortran 库或在 Fortran 中将数据序列化到内存缓冲区的首选方法。
研究主题后,我找到了使用 EQUIVALENCE 语句和 TRANSFER 内部函数的示例。我写了代码来测试它们,它们都起作用了。在我的有限测试中,传递函数似乎比等价语句慢很多。但是,我发现一些参考文献指出一般不使用等价语句。
所以,我一直在尝试想出另一种有效序列化数据的方法。在准备好 Fortran 2003 规范后,我发现我可以一起使用 C_LOC 和 C_F_POINTER 将我的 "byte" 数组转换为所需的数据类型(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)
实际上,以这种方式使用 C_LOC 和 C_F_POINTER 可能有效,但形式上它不符合标准。传递给 C_F_POINTER 的 Fortran 指针的类型和类型参数必须与 C 地址指定的对象互操作,或者与最初传递给 [=20= 的对象的类型和类型参数相同](参见 F2008 15.2.3.3 中 FPTR 参数的说明)。根据您尝试序列化的内容,您可能还会发现对 C_LOC 参数的正式限制(在后来的标准中比 F2003 的限制逐渐减少)开始发挥作用。
(C 等价物需要使用 unsigned char 来实现这种技巧——这不一定与 int8_t 相同。)
EQUIVALENCE 集中的项目存在限制,使得该方法不普遍适用(请参阅 F2008 中的限制 C591 到 C594)。通过等价解释对象的内部表示形式也正式服从变量定义和未定义的规则 - 特别参见 F2008 16.6.6 第 1 项。
在 Fortran 中访问一个对象作为不同类型的表示的一致方法是通过 TRANSFER。请注意,使用可分配或指针组件对派生类型的内部表示进行序列化(这就是动态字段的意思吗?)可能没有用。
根据具体情况,将实时结果简单地存储在要存储的事物类型的数组中可能更简单、更可靠。