python - CFUNCTYPE 的增量引用计数器

标签 python c ctypes

我有一个用 C 语言编写的库。库中的一个例程接受一个函数指针,将其保存到内存中供以后使用,然后返回。我正在尝试传递 Python 回调函数。

我看到的问题是稍后调用此函数有时会导致段错误。这是我正在玩的一些玩具代码。 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 的函数正在被垃圾收集。我用打印语句稍微修改了例程:

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)

毫不奇怪,我得到了 2 个关于 funcy 的引用。我尝试将它设置为一个全局变量,以防止垃圾回收,这似乎可行(它确实增加了引用计数),但肯定不是很漂亮。

我认为更好的解决方案是手动增加引用计数。但我根本无法让它正常工作。我再次将例程更改为:

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)

使用对 Py_IncRef 的调用或对递增引用计数器的 C 代码的调用。但是,无论我使用哪种方法来递增计数器,我都会遇到段错误,或者计数器不会递增(保持在 2)。

回调函数的引用计数器不能递增有什么原因吗?也许我犯了一个错误?感谢您的帮助。

最佳答案

cback 是一个返回包装函数的函数。还有什么方法可以这样?装饰器:

# global scope, the callback type
cback = ctypes.CFUNCTYPE(None,ctypes.POINTER(problem))

# decorate the callback
@cback
def change_struct_data(s):
    s.contents.val = 99

现在 change_struct_data 实际上是一个实例化的 CFUNCTYPE 对象,不会超出范围。您不能再直接从 Python 调用它,因为它已被修饰为从 C 调用,但这应该不是问题。

关于python - CFUNCTYPE 的增量引用计数器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27232627/

相关文章:

python - c_void_p 值无效*

python - pandas groupby 并更新最小值

python - Keras/Tensorflow CNN 输入形状

铛生成文件 "ar: no archive members specified"

c - OpenGL 视口(viewport)不工作

Python `ctypes` - 如何将 C 函数返回的缓冲区复制到字节数组中

python - 无法从类传递调整大小的 QLabel 几何

python - GroupBy Pandas 计算连续的零

c - 使用 getchar();定义多个变量

python - 帮助我理解为什么我对 Python 的 ctypes 模块的简单使用失败了