wesolyromek
wesolyromek

Reputation: 650

How to access object struct fields of subclasses from a Python C extension?

I'm writing a Python C extension that wraps an external C library. In the original library there are structs (of type T for the sake of the discussion), so my extension class looks like this:

typedef struct {
  PyObject_HEAD
  T *cdata;
} TWrapperBase;

I also need to look up pointers in Python from time to time, so I exposed a read-only field _cdata that is a cdata pointer as unsigned long long (yes, I know it's not very portable, but it's out of scope now).

Then, I want to be able to add some more methods in Python, but I can't just append them to a class declared in C, so I subclass it and add my new methods:

class TWrapper(TWrapperBase):
    ...

Now, in my C extension code I need a way of accesing cdata field, so I can pass it to library functions. I know that self won't be an instance of TWrapperBase, but rather TWrapper (this Python version). What is the proper way to do this?

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;
}

Thanks!

Upvotes: 3

Views: 669

Answers (1)

user2357112
user2357112

Reputation: 281152

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

Yeah, that works. You can look at all the implementations of Python's built-in types and see that they do pretty much the same thing, usually without checking whether they're operating on a subclass instance.

Upvotes: 3

Related Questions