英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论