[C++-sig] BPL and embedding python

Dave Hawkes daveh at cadlink.com
Tue Jun 11 05:45:32 CEST 2002


I've noticed that more and more people are attempting to embed python in
applications and documentation  on how to do this effectively is quite
scarce. One of the strengths of boost is the ability to create a c++ class
instance and override some of its members in python, but use the actual
class instance from within c++. In fact you can create a class instance,
modify some of its members using on the fly generated python code and then
pass the c++ instance back to yor c++ code.

I've done this with with boost V1 along these lines:

(Make sure your boost extension module is already imported.)

PyObject  *pPyModule = PyImport_AddModule(const_cast<char *>("MyModule"));
PyObject  *pPyDict = PyModule_GetDict(pPyModule);
PyObject  *pPyClass = PyDict_GetItemString(pPyDict, const_cast<char
*>("MyClass"));
if(pPyClass == NULL) {

    // The python code overiding the desired members of your c++/python
class.
    PyRun_String("python...code", Py_file_input, pPyDict, pPyDict);
    pPyClass = PyDict_GetItemString(*ppPyDict, const_cast<char
*>("MyClass"));
}

// pPyClass will now contain the python object corresponding to our c++
class
// Next we need instantiate this for our c++ code

// just in case
if(pPyClass) {
    // Remember the Python GIL, I just use my own class here to deal with
this
    // note that as this call is initiated from c++ rather than a callback
we have to be careful
    //  ...especially if you are using pythonwin
    CReEnterLeavePython _celp;
    // create an instance of our class
    // OK callback is a misnomer in this case!
    PyObject *pPyInstance = python::callback<PyObject *>::call(pPyClass);
    if(pPyInstance) {
        MyClass *pInstance = python::from_python(pPyInstance,
python::type<MyClass *>());
        if(pInstance) {
            // return our instance here
            // ... use our pInstance as a c++ class, ie
            return pInstance;
        } else {
            Py_DECREF(pPyInstance);
        }
    }
}

The only real problem here is that you cannot do this:

delete pInstance;

where pInstance is created as above. Instead we must decrement its PyObject
reference when it is no longer needed, ie:

{
    CReEnterLeavePython _celp;
    Py_DECREF(pPyInstance);
}

The best way of dealing with this is to remember the self passed to the
constructor in python and then create a del method such as:

class MyClass
{
public:
    virtual PyObject *GetPyObject()
    {
        return NULL;
    }
    void del()
    {
        if(GetPyObject()) {
            CReEnterLeavePython _celp;
            Py_DECREF(GetPyObject());
        } else
            assert(false);
    }
}
protected:
    MyClass() {}
    virtual ~MyClass();
private:
     PyObject *self;
};

class MyClass_callback : public MyClass
{
public:
    MyClass_callback(PyObject *self_) : MyClass(), self(self_) {}
    ~MyClass_callback() {}
    virtual PyObject *GetPyObject()
    {
        return self;
    }
private:
     PyObject *self;
};

We would have to use something like:

 class_builder<MyClass, MyClass_callback> MyClass_class(module, "MyClass");

to declare this class to python using boost.



Remember this is NOT complete code and just an outline of how to proceed.

The fly in the ointment is not being able to directly delete the class
instances (Dave, do you have any ideas ?)


Dave Hawkes










More information about the Cplusplus-sig mailing list