Boost Python Conceptual Problem

Get answers to all your basic programming questions. No Ogre questions, please!
Post Reply
DarkAnt
Gnoblar
Posts: 24
Joined: Tue May 26, 2009 2:11 pm
x 1

Boost Python Conceptual Problem

Post by DarkAnt »

I'm having a conceptual issue with the way C++ interfaces with Python (boost::python). I'm trying to convert a C++ object to a Python object, but my attempt causes the program to crash. I think I'm doing a lot of things wrong. Ultimately I'd like to be able to pass C++ objects to a python function that modifies their values.

Code: Select all

#include <boost/python.hpp>
#include <string>
struct Unit
{
	int health;
	std::string name;
	std::string type;
	std::pair<int,int> coord;
};

struct Unit_to_python
{
	static PyObject* convert(Unit const& unit)
	{
		return boost::python::incref(boost::python::object(unit).ptr());
	}
};

/**** In the lib ****/
// I believe this bit is only used if you create the object in Python
BOOST_PYTHON_MODULE(game)
{
	boost::python::class_<Unit>("Unit")
		.def_readwrite("health", &Unit::health)
		.def_readwrite("name", &Unit::name)
		.def_readwrite("type", &Unit::type)
		.def_readwrite("coord", &Unit::coord)
	;
}

int main(int argc, char** argv)
{
	Py_Initialize();
	boost::python::to_python_converter<Unit, Unit_to_python>();
	Unit unit1;
	unit1.health = 100;
	unit1.name = "Tank";
	unit1.type = "Armor";
	boost::python::object foo(unit1); // crash: stack overflow
	return 0;
}
DarkAnt
Gnoblar
Posts: 24
Joined: Tue May 26, 2009 2:11 pm
x 1

Re: Boost Python Conceptual Problem

Post by DarkAnt »

Ok, I had some wonderful help over at the Cplusplus-sig mailing list and it turns out my problem was quite simple to solve(read type).

First, when you create a set of wrappers you need to append them to the init tab. The BOOST_PYTHON_MODULE macro will create you the function needed to append all of the wrappers to the init tab. The name of this function is init[module_name]. Thus invoking BOOST_PYTHON_MODULE(game) creates the routine initgame.
Second, you need to import the module you created after you initialize the python runtime. Also of note, the wrappers created in BOOST_PYTHON_MODULE will generate to/from python converters for all primitive and stl types (I think) so there's no need to define them again like I did.

Here is the corrected code. Please note that the python object foo is a copy, not a reference:

Code: Select all

#include <boost/python.hpp>
#include <string>
struct Unit
{
   int health;
   std::string name;
   std::string type;
   std::pair<int,int> coord;
};

BOOST_PYTHON_MODULE(game)
{
   boost::python::class_<Unit>("Unit")
      .def_readwrite("health", &Unit::health)
      .def_readwrite("name", &Unit::name)
      .def_readwrite("type", &Unit::type)
      .def_readwrite("coord", &Unit::coord)
   ;
}

int main(int argc, char** argv)
{
   PyImport_AppendInittab("game", &initgame);
   Py_Initialize();
   boost::python::object module = boost::python::import("game");
   Unit unit1;
   unit1.health = 100;
   unit1.name = "Tank";
   unit1.type = "Armor";
   boost::python::object foo(unit1); // this is a python object that has copied the contents of unit1 (not a reference)
   return 0;
}
Ok, that's great! We can convert a C++ object to a python object. However, this was not entirely what I wanted to do so anyone reading this gets some extra fun bonus information :)
As stated in my first post I wanted to pass C++ objects to a python function that modifies their values. This is my python function:

Code: Select all

def hit(unit):
	unit.health -= 10
This code would call that python function hit:

Code: Select all

int main(int argc, char** argv)
{
	PyImport_AppendInittab("game", &initgame);
	Py_Initialize();
	boost::python::object module = boost::python::import("game");
	
	Unit unit1;
	unit1.health = 100;
	unit1.name = "Tank";
	unit1.type = "Armor";

	try{
		boost::python::object hit = boost::python::import("pyfile").attr("hit"); // gets the hit function from my python file pyfile.py
		boost::python::object ret = hit(unit1);
	}
	catch(...){	
		PyErr_Print();
	}
	std::cout << "unit1: " << unit1.health << ", " << unit1.name << ", " << unit1.type << std::endl;
	return 0;
}
And this code prints out:

Code: Select all

unit1: 100, Tank, Armor
Wait... The health should be 90 now. What happened?
When you call the python function hit, it implicitly converts all of the arguments into python objects by copying the data. Thus unit1's data is copied into a boost::python object and this copy is used in the hit function. The hit function goes out of scope and the copy is garbage collected. The original is never modified.
Ok, so not what I want. What I want is easily achieved using boost ref. So if we look at that try catch block again:

Code: Select all

	try{
		boost::python::object hit = boost::python::import("pyfile").attr("hit");
		boost::python::object ret = hit(boost::ref(unit1)); // this time with boost::ref giving us our desired outcome
	}
	catch(...){	
		PyErr_Print();
	}
The output of the program is now:

Code: Select all

unit1: 90, Tank, Armor
Hooray! You made it to the end. I just started using Boost::Python so there's probably a few errors in what I said above. Also, a note about compiling.
Visual Studios: Don't forget to put -DBOOST_PYTHON_STATIC_LIB into your command line options (configuration properties)
Linux: Compile with -I/usr/include/python2.6 -lpython2.6 -lboost_python

Also also, I never got it running on Windows, but the Py++ is an awesome code generator for boost::python. The boost-ogre guys used it in their project.
TheSHEEEP
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 972
Joined: Mon Jun 02, 2008 6:52 pm
Location: Berlin
x 65

Re: Boost Python Conceptual Problem

Post by TheSHEEEP »

Nice post!

Will certainly help some people trying to bind python with.. with whatever C++, actually ;)
My site! - Have a look :)
Also on Twitter - extra fluffy
DarkAnt
Gnoblar
Posts: 24
Joined: Tue May 26, 2009 2:11 pm
x 1

Re: Boost Python Conceptual Problem

Post by DarkAnt »

Thanks :)
I was hoping it would be useful to someone other than myself. Maybe I'll write a more comprehensive tutorial after I'm more experienced with the library. I'd like to know a lot more about the call policies provided by boost::python.
Post Reply