Boost C++ Libraries of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards


How can I find the existing PyObject that holds a C++ object?

"I am wrapping a function that always returns a pointer to an already-held C++ object."

One way to do that is to hijack the mechanisms used for wrapping a class with virtual functions. If you make a wrapper class with an initial PyObject* constructor argument and store that PyObject* as "self", you can get back to it by casting down to that wrapper type in a thin wrapper function. For example:

class X { X(int); virtual ~X(); ... };
X* f();  // known to return Xs that are managed by Python objects

// wrapping code

struct X_wrap : X
  X_wrap(PyObject* self, int v) : self(self), X(v) {}
  PyObject* self;

handle<> f_wrap()
  X_wrap* xw = dynamic_cast<X_wrap*>(f());
  assert(xw != 0);
  return handle<>(borrowed(xw->self));


def("f", f_wrap());
class_<X,X_wrap,boost::noncopyable>("X", init<int>())

Of course, if X has no virtual functions you'll have to use static_cast instead of dynamic_cast with no runtime check that it's valid. This approach also only works if the X object was constructed from Python, because Xs constructed from C++ are of course never X_wrap objects.

Another approach to this requires you to change your C++ code a bit; if that's an option for you it might be a better way to go. work we've been meaning to get to anyway. When a shared_ptr<X> is converted from Python, the shared_ptr actually manages a reference to the containing Python object. When a shared_ptr<X> is converted back to Python, the library checks to see if it's one of those "Python object managers" and if so just returns the original Python object. So you could just write object(p) to get the Python object back. To exploit this you'd have to be able to change the C++ code you're wrapping so that it deals with shared_ptr instead of raw pointers.

There are other approaches too. The functions that receive the Python object that you eventually want to return could be wrapped with a thin wrapper that records the correspondence between the object address and its containing Python object, and you could have your f_wrap function look in that mapping to get the Python object out.