【发布时间】:2021-01-12 22:13:45
【问题描述】:
非必要背景:我在使用 ctypes 指针时遇到了许多问题,到目前为止,我可以通过在 Python 端将所有内容剥离到 c_void_p 来解决这些问题,然后挖掘出我需要的东西在C中通过重铸和指针算术。然而,我终于在 Python 中遇到了我无法解决的问题,我希望这个 MWE 的明智答案能让我修复我所有的其他 hack。
MWE:在以下两种情况下,我成功地定义、填充、传递和返回一个结构,从/到 Python 到/从一个简单的 C 外部函数。在第一种情况下(ff1.py),一切都很好。在第二种情况下(ff2.py),函数返回并在 Python 中成功取消引用,然后 Python 崩溃。这两种情况的区别在于,从 Python 传出的结构体是作为变量从模块中导入,还是在传递给 C 之前从 Python 函数返回。
ff.h:
struct dummy
{
int dim1;
int dim2;
double * array_ptr;
};
void
foreign_func( struct dummy * ptr_to_dummy );
ff.c:
#include "ff.h"
__attribute__((visibility("default")))
void
foreign_func( struct dummy * d_ptr )
{
const int N = d_ptr->dim1;
const int M = d_ptr->dim2;
for( int i=0; i<N; i++ )
for( int j=0; j<M; j++ )
d_ptr->array_ptr[ i*M + j ] *= 3.14159;
return;
}
struct_wo_funcs.py:
import numpy as np
import ctypes
class dummy_struct_type( ctypes.Structure ):
_fields_ = [ ( 'dim1', ctypes.c_int ),
( 'dim2', ctypes.c_int ),
( 'array_ptr', ctypes.POINTER( ctypes.c_double ) ) ]
n = 12
m = 50
a = np.ones( (n,m), dtype=ctypes.c_double, order='C' )
dummy_struct_instance = \
dummy_struct_type( ctypes.c_int( n ),
ctypes.c_int( m ),
a.ctypes.data_as( ctypes.POINTER( ctypes.c_double ) ) )
struct_w_funcs.py:
import numpy as np
import ctypes
class dummy_struct_type( ctypes.Structure ):
_fields_ = [ ( 'dim1', ctypes.c_int ),
( 'dim2', ctypes.c_int ),
( 'array_ptr', ctypes.POINTER( ctypes.c_double ) ) ]
def make_instance( n, m ):
a = np.ones( (n,m), dtype=ctypes.c_double, order='C' )
return dummy_struct_type( ctypes.c_int( n ),
ctypes.c_int( m ),
a.ctypes.data_as( ctypes.POINTER( ctypes.c_double ) ) )
ff1.py:
import numpy as np
import ctypes
import os
from struct_wo_funcs import dummy_struct_type, dummy_struct_instance
fflib = ctypes.CDLL( os.path.abspath( 'ff_lib.so' ) )
fflib.foreign_func.argtypes = [ ctypes.POINTER( dummy_struct_type ) ]
fflib.foreign_func.restype = None
fflib.foreign_func( ctypes.byref( dummy_struct_instance ) )
print( dummy_struct_instance.array_ptr[19] )
ff2.py:
import numpy as np
import ctypes
import os
from struct_w_funcs import dummy_struct_type, make_instance
fflib = ctypes.CDLL( os.path.abspath( 'ff_lib.so' ) )
fflib.foreign_func.argtypes = [ ctypes.POINTER( dummy_struct_type ) ]
fflib.foreign_func.restype = None
dummy_struct_instance = make_instance( 12, 50 )
fflib.foreign_func( ctypes.byref( dummy_struct_instance ) )
print( dummy_struct_instance.array_ptr[19] )
我已经在 linux 上使用各种编译器进行了尝试。我还更改了导出并在 Windows 上使用 gcc/MinGW 和 cl/MSVC 编译,效果相同。 (公平地说,在 MSVC 开发人员提示符下运行不会导致彻底崩溃;相反,Windows 会退缩一会儿,然后恢复到提示符而不报告任何内容。)这是一个使用 gcc v7.5.0 和Python 3.6.9:
$ gcc -fPIC -c ff.c && gcc -fPIC -shared -o ff_lib.so ff.o
$ python3 ff1.py
3.14159
$ python3 ff2.py
3.14159
free(): corrupted unsorted chunks
Aborted (core dumped)
我已经 valgrinded 搞砸了,只能知道 Python 的内部出了点问题。我对gc 或其他Python 调试工具没有太多经验。所以,即使你不知道答案,我也希望得到一些关于如何进行的建议。最终,我希望能够使用 Python 中的函数来生成我传递出去的结构;也就是说,我希望ff2.py 不会崩溃。如果您的答案只是另一种解决方法(例如 numpy 声明和 byrefs 的某种神奇组合等),那对我来说已经足够了。据我所知,这是 Python 而不是 C 的问题,但如果您可以通过更改 FFI 的 C 端使其工作,我很想看看如何。
【问题讨论】: