Monday, February 2, 2009

Embedding Boost Python

Just spent 6 hours trying to solve an issue with class wrapping using Boost with embedded code and pointers.

Imagine your code looks something like this where you are tying to define a global variable that will be availiable to a python program. The global variable is actually an instance pointer to a C++ class.


BOOST_PYTHON_MODULE(TestModule)
{
class_("MyClass", no_init)
.def(self_ns::str(self))
;
};

PyImport_AppendInittab( "TestModule", initTestModule );
Py_Initialize();

MyClass *myInstance = new MyClass();

object main_module((handle<>(borrowed(PyImport_AddModule("__main__")))));
object main_namespace((main_module.attr("__dict__")));
main_namespace["myInstance"] = myInstance;
object ignored = exec_file("test.py", main_namespace, main_namespace);


Now, whenever you try to run it, you get an error:

TypeError: No to_python (by-value) converter found for C++ type: class MyClass


The culprit is the statement main_namespace["myInstance"] = myInstance;. Basically it's saying there is no convertor. Seems odd no? I mean you explicitly defined it previously with the class wrapper, surely it should work?. Well.. that's true.. but you need one extra line:


exec("import TestModule", main_namespace, main_namespace);


Inserted just before you assign the variable myInstance into the main namespace.
The reason for this seemingly inexplicable behaviour comes from the Python documention:

int PyImport_AppendInittab (char *name, void (*initfunc)(void))
Add a single module to the existing table of built-in modules. This is a convenience wrapper around PyImport_ExtendInittab(), returning -1 if the table could not be extended. The new module can be imported by the name name, and uses the function initfunc as the initialization function called on the first attempted import. This should be called before Py_Initialize().

So, the code to register the convertor isn't called until the module is imported, and if you try to assign the variable directly to the dictionary it will fail until it IS imported.