Jump to content
XCOMUFO & Xenocide

Exposing Classes To Python


reist

Recommended Posts

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 {
public:
   typedef SomeCollection<SomeType> Col;
   typedef Col::iterator iterator; // needed by iterator<>()
   iterator begin() {return col.begin(); }
   iterator end() {return col.end(); }
private:
   Col col;
};
The _py.cpp file:
class_<CollectionWrapper>("CollectionWrapper")
   .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:
register_ptr_to_python<RackPtr>();

Edited by reist
Link to comment
Share on other sites

×
×
  • Create New...