英文:
Python and fortran with ctypes: lists with objects
问题
当我调用lists_change
函数时,出现了错误“OSError: exception: access violation reading 0x0000000000000005”。请告诉我如何修复它以及代码是否总体上编写正确?
当你调用lists_change
函数时出现这个错误,通常表示在Fortran代码中存在内存访问问题。错误消息 "access violation reading 0x0000000000000005" 指示尝试读取无效内存地址。
这个问题可能与你在Fortran代码中使用指针时出现的问题有关。在你的Fortran代码中,你使用了C指针传递Python中的数组,但出现了内存访问错误。为了修复这个问题,你可以尝试以下几种方法:
-
确保Python和Fortran中的数据结构匹配:确保Python中的
StateVector
和Fortran中的state_vector
数据结构匹配,包括大小和成员的顺序。 -
检查内存分配和释放:确保在Fortran代码中正确分配和释放内存。你已经使用了
C_F_POINTER
来分配内存,但你需要确保它与Python中的数组一致,并在使用完后释放内存。 -
检查函数调用参数:确保你正确传递了参数给
lists_change
函数。特别注意检查传递给length
参数的值是否正确,它应该等于数组的大小。 -
调试Fortran代码:使用调试工具来检查Fortran代码中的问题。你可以使用调试器来跟踪代码执行并查找错误。
总之,这个问题通常是由内存访问问题引起的,因此你需要仔细检查代码中的指针操作和内存分配/释放,以确保数据在Python和Fortran之间正确传递。如果问题仍然存在,你可能需要进一步调试代码以找出问题的根本原因。
英文:
I need to write a library in fortran that will take an array of objects from Python, process it and return it back.
I've tried a lot of options, the last one looks like this:
Code Python:
import ctypes as ct
import os
import numpy as np
here = os.path.dirname(__file__)
lib_file = os.path.abspath(os.path.join(here, '..', 'fortran', 'svector_test_mod.dll'))
fortlib = ct.CDLL(lib_file, winmode=0)
class StateVector(ct.Structure):
_fields_ = [
('x', ct.c_longdouble),
('y', ct.c_longdouble),
('z', ct.c_longdouble),
('vx', ct.c_longdouble),
('vy', ct.c_longdouble),
('vz', ct.c_longdouble),
('t', ct.c_longdouble),
]
size = 5
array = (StateVector * size)()
for i in range(size):
array[i].x = ct.c_longdouble(2.5)
array[i].y = ct.c_longdouble(2.5)
array[i].z = ct.c_longdouble(2.5)
array[i].vx = ct.c_longdouble(2.5)
array[i].vy = ct.c_longdouble(2.5)
array[i].vz = ct.c_longdouble(2.5)
array[i].t = ct.c_longdouble(2.5)
np_objects = np.ctypeslib.as_array(array)
arr_ptr = np_objects.ctypes.data_as(ct.POINTER(ct.c_void_p))
fortlib.lists_change(ct.byref(arr_ptr), size)
Code Fortran:
module state_vector_mod
use, intrinsic :: iso_c_binding, only: C_LONG_DOUBLE, c_int, C_PTR, C_F_POINTER
implicit none
type, bind(C) :: state_vector
real(C_LONG_DOUBLE) :: x
real(C_LONG_DOUBLE) :: y
real(C_LONG_DOUBLE) :: z
real(C_LONG_DOUBLE) :: vx
real(C_LONG_DOUBLE) :: vy
real(C_LONG_DOUBLE) :: vz
real(C_LONG_DOUBLE) :: t
end type state_vector
contains
subroutine make_sv(x, y, z, vx, vy, vz, t, sv) bind(c, name='make_sv')
real(C_LONG_DOUBLE), intent(in) :: x
real(C_LONG_DOUBLE), intent(in) :: y
real(C_LONG_DOUBLE), intent(in) :: z
real(C_LONG_DOUBLE), intent(in) :: vx
real(C_LONG_DOUBLE), intent(in) :: vy
real(C_LONG_DOUBLE), intent(in) :: vz
real(C_LONG_DOUBLE), intent(in) :: t
type(state_vector), intent(out), target :: sv
sv = state_vector(x, y, z, vx, vy, vz, t)
end subroutine make_sv
subroutine lists_change(in_list, length) bind(C, name='lists_change')
type(C_PTR), intent(inout) :: in_list
!type(state_vector), dimension(length), intent(out) :: out_list
type(state_vector), dimension(:), pointer :: my_array
integer(c_int), intent(in) :: length
integer(c_int) :: i
call C_F_POINTER(in_list, my_array, [length])
DO i = 1, length
my_array(i)%x = my_array(i)%x * 2
my_array(i)%y = my_array(i)%y * 2
my_array(i)%z = my_array(i)%z * 2
my_array(i)%vx = my_array(i)%vx * 2
my_array(i)%vy = my_array(i)%vy * 2
my_array(i)%vz = my_array(i)%vz * 2
my_array(i)%t = my_array(i)%t * 2
END DO
!out_list = my_array
deallocate(my_array)
end subroutine lists_change
end module state_vector_mod
But when I call the lists_change
function, an error appears "OSError: exception: access violation reading 0x0000000000000005"
Please tell me how you can fix it and whether the code is written correctly in general?
答案1
得分: 1
错误消息中的地址很奇怪。它指向一个整数值为5的地址值。变量size
具有此值。我们可以得出结论,参数length
integer(c_int), intent(in) :: length
实际上应该具有value
属性
integer(c_int), value :: length
或者它可以通过Python的引用方式(ct.byref
)来调用。
关于一般性问题,francescalus是对的,in_list
数组指针被修改,因此可能应该以值的方式传递,这样你就可以摆脱ct.byref
或者使用intent(in)
。或者你也可以只是通过引用传递数组本身,避免进行强制转换和解引用指针。
type(state_vector), intent(inout) :: my_array(length)
或者如果你引入一个单独的out_list
参数并将其注释掉,那么需要进行一些明显的修改。
英文:
The address in the error message is peculiar. It points to an integer value of 5 being used as an address value. The variable size
has this value. We can conclude that the argument length
integer(c_int), intent(in) :: length
should actually have the value
attribute
integer(c_int), value :: length
or that it should be called from Python by reference (ct.byref
).
Regarding general points, francescalus is right that the in_list
array pointer is modified and should likely be passed as value so you can get rid of the ct.byref
or with intent(in)
. Or you can also just pass the array itself by reference and avoid casting de-referencing the pointer.
type(state_vector), intent(inout) :: my_array(length)
or with some obvious modifications if you introduce a separate out_list
argument that is now commented out.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论