Cracking open Python objects in C++

Greetings all!

This might be off-topic, as it straddles the line between scripting and source editing. But there’s more C++ in the question than Python, so it seemed appropriate to drop it here.

I am attempting to write an external python module to speed up some transformations I’m doing on a PNMImage. I’ve got the entire algorithm laid out in Python code, and it works correctly. But since I need to touch every pixel, it’s too slow; to me, this screams “Move the code into C++ for efficiency.”

I’m extremely new to writing Python modules, but I think I have a handle on the basic ideas (PyArg_ParseTuple, etc). The point where I’m stumped is how to take the PyObject reference to the PNMImage that is passed in to my module by Python and crack it open to get to the C++ layer of the object. Ideally, I’d like to access the object’s contents via the C++ methods provided by pnmImage.h:

static PyObject *
image_voodoo(PyObject *self, PyObject *args) {
  PyObject *myImgWrapper;

  if (!PyArg_ParseTuple(args, "O", &myImgWrapper))
       return NULL;
  PNMImage *myImg = <what I need to do to get the C++ reference >;
  double firstRed = myImg->get_red(0,0);  
  .. etc ..

… then the rest of the module would make this function accessible as mymodule.imageVoodoo(myPnmImage)

Thank you for your help!

The answer is different for Panda3D 1.0.5 than it is for Panda3D 1.1.0 and later. I’ll answer assuming you are writing for 1.1.0.

There is a file in the Panda3D sources (specifically, in dtool/src/interrogatedb) called py_panda.h. Within this file is the structure definition:

struct Dtool_PyInstDef
        void                 * _ptr_to_object;
        bool                   _memory_rules;
        unsigned long          _signiture;
        struct Dtool_PyTypedObject * _My_Type;

You can either include this header file, or duplicate this structure definition in your own sources (or at least the first two elements of the structure).

The key is that void *_ptr_to_object immediately follows PyObject_HEAD. Thus, you can retrieve the original C++ PNMImage object with code like this:

PNMImage *myImg = (PNMImage *)((Dtool_PyInstDef *)myImageWrapper)->_ptr_to_object;

Note that the above code performs no type checking; if you accidentally pass in, say, a string instead of a PNMImage, your program will certainly crash messily. The actual code generated for Panda does a bit more work than this simple cast, and will perform type checking properly; but it will be difficult to include that code in your own program without linking with Panda and possibly building within the Panda build environment.

Of course, building within the Panda build environment is not out of the question; the ppremake system in particular is designed to facilitate this. But I don’t know how far you meant to take this simple question. :slight_smile:


Excellent! This is exactly what I was wondering. Thank you for the help!

Building into the Panda framework may not be a bad idea; I’ll look into it once I’ve convinced myself this extension I’m writing will be functional in general :wink: