Jump to content


Exposing Classes To Python

  • Please log in to reply
3 replies to this topic

#1 reist


    Programming Department

  • Xenocide Programming Department
  • 130 posts

Posted 23 February 2006 - 08:11 PM

I exposed a big piece of Item/Inventory hirarchy plus other parts of code to python and here's a summary of what I learned about how it's supposed to be done:

Virtual functions are accepted as easily as non-virtual. Just write the regular .def() and it'll do. It only gets complicated if we want to derive new classes from that class and override the virtual functions. The boost::python documentation explains what to do in such a case perfectly.

For pure virtual functions, the class only has to be defined as having no constructor and as non-copyable:
class_<AbstractClass, boost::noncopyable>("AbstractClass", no_init)

If there are overloaded functions, they have to be passed as different function pointers. No way around it. For example:
   void (Item::*f1)(InventoryBase&) = &Item::insert;
    void (Item::*f2)(InventoryTroop&) = &Item::insert;
    void (Item::*f3)(InventoryCraft&) = &Item::insert;
    class_<Item, boost::noncopyable>("Item", no_init)
        .def("insert", f1)
        .def("insert", f2)
        .def("insert", f3)

Don't create functions that take iterators that would need to be exposed! It's normally impossible to pass an iterator from python to C++. In the other direction it's quite possible, as a pair<> of iterators to the start and end of a range (that's how iterators work in python).
By the way, it is possible to expose sequences (vector, list, deque...) and maps to python 100%. There are special (and very complex) templates in boost::python that let you do that. They don't work on multimaps!

BUT, as we don't use stl collections directly, we can only get limited usage in C++ and even more limited in python.
This is how it's possible to create an iterator over the whole collection inside a class that works in both C++ and python:
The class:
class CollectionWrapper {
    typedef SomeCollection<SomeType> Col;
    typedef Col::iterator iterator; // needed by iterator<>()
    iterator begin() {return col.begin(); }
    iterator end() {return col.end(); }
    Col col;
The _py.cpp file:
    .def("__iter__", iterator<CollectionWrapper>())

A note about map: for each class using a different underlying map, it's value_type has to be exposed too. For example:
class_< std::pair<String const, RackPtr> >("TInventoryEntry")
    .def_readonly("first", &std::pair<String const, RackPtr>::first)
    .def_readwrite("second", &std::pair<String const, RackPtr>::second)

Also, each shared_ptr that is passed to python has to be exposed too:
Somewhere before:
typedef TSharedPtr<Rack> RackPtr;
In the _py.cpp file:

Edited by reist, 23 February 2006 - 08:14 PM.

#2 rincewind


    Programming Department

  • Xenocide Programming Department
  • 541 posts

Posted 24 February 2006 - 08:11 AM

Good job reist.

Maybe put this in the wiki?

Posted Image

I love boost!!! The next best thing since the invention of C++.

#3 reist


    Programming Department

  • Xenocide Programming Department
  • 130 posts

Posted 24 February 2006 - 12:04 PM

No problem. Here's the direct link:

#4 red knight

red knight

    Xenocide Project Leader

  • Xenocide Inactive
  • 3,310 posts

Posted 25 February 2006 - 01:21 PM

Excelent work there reist.

Red Knight
Sourceforge Nick: flois - Federico Andres Lois
Visit my blog at: flois.blogspot.com

Posted Image

Pookie cover me, I am going in.