2016-05-03 53 views
2

我正在写一个包装外部C库的Python C扩展。在原有的库有(讨论的缘故T型)结构,所以我的扩展类看起来是这样的:如何从Python C扩展中访问子类的对象结构体字段?

typedef struct { 
    PyObject_HEAD 
    T *cdata; 
} TWrapperBase; 

我还需要从时间查找指针在Python中的时间,所以我暴露了一个只读字段_cdata,这是一个cdata指针作为unsigned long long(是的,我知道它不是很便携,但现在它已经超出了范围)。

然后,我希望能够增加在Python一些方法,但我不能只将它们附加在C声明的类,所以我继承它并添加我的新方法:

class TWrapper(TWrapperBase): 
    ... 

现在,在我的C扩展代码中,我需要一个访问cdata字段的方法,所以我可以将它传递给库函数。我知道self不会是TWrapperBase的实例,而是TWrapper(此Python版本)。 这样做的正确方法是什么?

static PyObject * doStuff(PyObject *self) 
{ 
    T *cdata_ptr; 
    // How to get a pointer to cdata? 
    // 
    // This looks very unsafe to me, do I have any guarantee of 
    // the subclass memory layout? 
    // 1. cdata_ptr = ((TWrapperBase*)self)->cdata 
    // 
    // This is probably safe, but it seems to be a bit of a hassle 
    // to query it with a string key 
    // 2. cdata_ptr = PyLong_AsVoidPtr(PyObject_GetAttrString(self, "_cdata")) 
    do_important_library_stuff(cdata_ptr); 
    Py_INCREF(self); 
    return self; 
} 

谢谢!

+0

你为什么在做'Py_INCREF(self);'*之后*你做图书馆的东西? –

+0

这就是为什么选项1对你来说看起来不安全? –

+0

我是'INCREF'ing它只是因为它是一个返回类型和返回的对象是被盗的引用。 – wesolyromek

回答

2
// This looks very unsafe to me, do I have any guarantee of 
    // the subclass memory layout? 
    // 1. cdata_ptr = ((TWrapperBase*)self)->cdata 

是的,那是有效的。你可以看看Python's built-in types的所有实现,并看到它们几乎完全相同,通常不检查它们是否在子类实例上运行。

+0

真的吗?那么它是否总是这样呢,一个子类中所需的字段和其他东西在它的基类的数据之后被放置在内存中?这是一些标准的一部分,还是Python总是如何实现的? – wesolyromek

+0

@wesolyromek:这是CPython实现的工作原理。请注意,在Python中定义的“fields”通常是'__dict__'条目,而不是您在C中定义的结构字段类型,所以Python级别的子类通常不会对其超类的内存布局做很多工作。 (如果你使用'__slots__',那就改变了一切。) – user2357112

+0

所以(只要我不使用'__slots__')它就像这样工作:所有对象的布局都以它们的C基类所拥有的开始,然后只有一些这些字典和其他字段的指针被修改为获取子类,对吧? – wesolyromek