Boost Python Conceptual Problem

Get answers to all your basic programming questions. No Ogre questions, please!

Boost Python Conceptual Problem

Postby DarkAnt » Mon Apr 02, 2012 3:44 pm

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
Kudos: 1
Joined: 26 May 2009

Re: Boost Python Conceptual Problem

Postby DarkAnt » Wed Apr 04, 2012 3:09 am

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.

For this message the author DarkAnt has received kudos
DarkAnt
Gnoblar
 
Posts: 24
Kudos: 1
Joined: 26 May 2009

Re: Boost Python Conceptual Problem

Postby TheSHEEEP » Thu Apr 19, 2012 11:39 am

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
TheSHEEEP
OGRE Team Member
OGRE Team Member
 
Posts: 933
Kudos: 56
Joined: 02 Jun 2008
Location: Berlin

Re: Boost Python Conceptual Problem

Postby DarkAnt » Sat Apr 21, 2012 9:02 pm

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.
DarkAnt
Gnoblar
 
Posts: 24
Kudos: 1
Joined: 26 May 2009


Return to Back to Basics

Who is online

Users browsing this forum: No registered users and 0 guests