没有为指向结构的指针调用 ctypes.Structure 子类的 ctypes __del__

Posted

技术标签:

【中文标题】没有为指向结构的指针调用 ctypes.Structure 子类的 ctypes __del__【英文标题】:ctypes __del__ of ctypes.Structure subclass not invoked for pointers to structures 【发布时间】:2020-01-26 19:55:13 【问题描述】:

我包装的 C 库需要分配和销毁 C 函数内部结构的内存,并且总是传递指针,而不是结构。

我有一个带有__del__ 方法的ctypes.Structure 子类。在实例上调用 del 时不会调用此方法,该实例是指向从 C 中的函数返回的结构的指针 (Foo_init)。有趣的是,当调用 del 时调用它,如果该实例不是指针,而是从不同 C 函数 (Foo_init_value) 返回的实际值。

如何获得 __del__ 方法来调用指向从 C 中的函数返回的结构的指针?

在 Python 中:

import ctypes
import gc
libfoo = ctypes.CDLL("libfoo.so")

class Foo(ctypes.Structure):
    _fields_ = [
        ('a', ctypes.c_int),
        ('b', ctypes.POINTER(ctypes.c_char)),
    ]
    def __del__(self):
        print('deleting')
        libfoo.Foo_destroy(self)

# Get a pointer to Foo
libfoo.Foo_init.restype = ctypes.POINTER(Foo)
libfoo.Foo_init.argtypes = None

# Get actual Foo, not pointer
libfoo.Foo_init_value.restype = Foo
libfoo.Foo_init_value.argtypes = None

libfoo.Foo_destroy.restype = ctypes.c_int
libfoo.Foo_destroy.argtypes = [ctypes.POINTER(Foo)]

# Get a pointer to Foo
foo = libfoo.Foo_init()
# Nothing is printed here:
del foo
# Nothing is printed here
gc.collect()
# Nothing is printed here
[f for f in gc.get_objects() if isinstance(f, Foo)]

# Get an actual Foo, not pointer
foo = libfoo.Foo_init_value()
# This works, prints 'deleting', but crashes with freeing an invalid pointer
# presumably because __del__ is passing the struct, not pointer to struct
del foo

这是我的琐碎 C 库:

#include <stdlib.h>

typedef struct Foo 
    int a;
    char *b;
 Foo;

/* Allocate and return pointer to Foo */
/* __del__ does not work for this one */
Foo *Foo_init(void)

    Foo *foo = (Foo *) malloc(sizeof(Foo));
    foo->a = 0;
    foo->b = NULL;
    return foo;


/* Allocate and return value of Foo, not a pointer*/
/* __del__ works on this */
Foo Foo_init_value(void)

    Foo *foo = Foo_init();
    return *foo;


int *Foo_destroy(Foo *foo)

    if (foo->b) 
         free(foo->b);
         foo->b = NULL;
    
    free(foo);
    return 0;

编译:

gcc -c -Wall -Werror -fpic foo.c
gcc -shared -o libfoo.so foo.o

【问题讨论】:

什么已经解决,什么可以改变?关于.dll,函数签名可以修改吗? @CristiFati 函数签名无法更改;它们来自供应商提供的专有/旧版 API。 【参考方案1】:

这可行,但我认为有更好的方法:

Foop = ctypes.POINTER(Foo)

def foo_del(self):
    print('deleting')
    libfoo.Foo_destroy(self)

Foop.__del__ = foo_del

foo = libfoo.Foo_init()
# prints 'deleting'
del foo

【讨论】:

以上是关于没有为指向结构的指针调用 ctypes.Structure 子类的 ctypes __del__的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 实体中的可转换属性可以是指向 c 结构的指针吗?我的变压器没有被调用

使用指向第一个成员的指针调用 free 是不是有效?

使用 ctypes 传递结构指针

python 调用C代码获取数据,C代码要求1个结构参数, 其中有项目是指向缓冲区的指针,如何实现参数赋值?

程序的错误输出,使用指向结构数组的指针

ES6的Iterator,jquery Fn