我有一个用C语言编写的库。库中的一个例程需要一个函数指针,将其保存到内存中供以后使用,然后返回。我正在尝试传递一个Python回调函数。CFUNCTYPE的增量引用计数器
我看到的问题是稍后调用此函数有时会导致分段错误。这是我正在玩的一些玩具代码。的Python代码:
import ctypes
import sys
class problem(ctypes.Structure):
_fields_ = [("val",ctypes.c_int),("f",ctypes.POINTER(ctypes.c_void_p))]
def change_struct_data(s):
s.contents.val = 99
class UsefulInfo:
def __init__(self,library_name):
self.lib = ctypes.pydll.LoadLibrary(library_name)
self.lib.create_data.restype = ctypes.POINTER(problem)
self.data = self.lib.create_data(12)
def setcallback(self,f):
cback = ctypes.CFUNCTYPE(None,ctypes.POINTER(problem))
funcy = cback(f)
self.lib.pass_func(self.data,funcy)
def makecall(self):
self.data.contents.val = 0
self.lib.use_callback(self.data)
print 'val is now',self.data.contents.val
test = UsefulInfo('./libfoo.so')
test.setcallback(change_struct_data)
test.makecall()
C代码:
//gcc test.c -I/usr/include/python2.7/ -L/usr/lib/python2.7/ -lpython2.7 -lm -fPIC -c -g
//gcc -shared -Wl,-soname,libfoo.so -o libfoo.so test.o
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct problem
{
int val;
void (*f)(struct problem*);
} problem;
int C_inc_ref(PyObject* obj)
{
Py_INCREF(obj);
return 0;
}
void pass_func(problem* prob,void (*funcy)(problem*))
{
prob->f = funcy;
}
void use_callback(problem* prob)
{
prob->f(prob);
}
problem* create_data(int n)
{
problem* prob = (problem*) malloc(sizeof(problem));
prob->val = n;
prob->f = NULL;
return prob;
}
左思右想,我怀疑函数转换在日常setcallback
到CFUNCTYPE被垃圾收集。我修改了程序略有print语句:
def setcallback(self,f):
cback = ctypes.CFUNCTYPE(None,ctypes.POINTER(problem))
funcy = cback(f)
self.lib.pass_func(self.data,funcy)
print 'ref count is',sys.getrefcount(funcy)
不足为奇的是,我得到funcy
2个引用。我试着将它设置为一个全局变量,以防止垃圾收集,这似乎工作(它确实增加了引用计数),但肯定不是很漂亮。
我认为更好的解决方案是手动增加引用计数。但我根本无法让这个工作正常。使用要么Py_IncRef呼叫或C代码的呼叫,其递增基准计数器
def setcallback(self,f):
cback = ctypes.CFUNCTYPE(None,ctypes.POINTER(problem))
funcy = cback(f)
self.lib.pass_func(self.data,funcy)
#self.lib.C_inc_ref(funcy)
ctypes.pythonapi.Py_IncRef(funcy)
print 'ref count is',sys.getrefcount(funcy)
:我再次改变例程。但是,无论使用哪种方法增加计数器,我要么出现分段错误,要么计数器不增加(保持2)。
有什么理由不能增加回调函数的引用计数器?也许我犯了一个错误?任何帮助表示赞赏。
这篇文章对你有帮助吗? http://stackoverflow.com/a/7261524/1470749 – 101 2014-12-01 22:24:10
你为什么使用'pydll'?在调用过程中,'PyDLL'类被标记为保存GIL,如果C库使用解释器,则只需要该类。 – eryksun 2014-12-06 00:16:51