| Home | Libraries | People | FAQ | More

## Exposing Classes

Now let's expose a C++ class to Python.

Consider a C++ class/struct that we want to expose to Python:

    struct World
    {
        void set(std::string msg) { this->msg = msg; }
        std::string greet() { return msg; }
        std::string msg;
    };

We can expose this to Python by writing a corresponding Boost.Python C++ Wrapper:

    #include <boost/python.hpp>
    using namespace boost::python;

    BOOST_PYTHON_MODULE(hello)
    {
        class_<World>("World")
            .def("greet", &World::greet)
            .def("set", &World::set)
        ;
    }

Here, we wrote a C++ class wrapper that exposes the member functions `greet` and `set`. Now, after building our module as a shared library, we may use our class `World` in Python. Here's a sample Python session:

    >>> import hello
    >>> planet = hello.World()
    >>> planet.set('howdy')
    >>> planet.greet()
    'howdy'

### Constructors

Our previous example didn't have any explicit constructors. Since `World` is declared as a plain struct, it has an implicit default constructor. Boost.Python exposes the default constructor by default, which is why we were able to write

    >>> planet = hello.World()

We may wish to wrap a class with a non-default constructor. Let us build on our previous example:

    struct World
    {
        World(std::string msg): msg(msg) {} // added constructor
        void set(std::string msg) { this->msg = msg; }
        std::string greet() { return msg; }
        std::string msg;
    };

This time `World` has no default constructor; our previous wrapping code would fail to compile when the library tried to expose it. We have to tell `class_<World>` about the constructor we want to expose instead.

    #include <boost/python.hpp>
    using namespace boost::python;

    BOOST_PYTHON_MODULE(hello)
    {
        class_<World>("World", init<std::string>())
            .def("greet", &World::greet)
            .def("set", &World::set)
        ;
    }

`init<std::string>()` exposes the constructor taking in a `std::string` (in Python, constructors are spelled "`"_init_"`").

We can expose additional constructors by passing more `init<...>`s to the `def()` member function. Say for example we have another World constructor taking in two doubles:

    class_<World>("World", init<std::string>())
        .def(init<double, double>())
        .def("greet", &World::greet)
        .def("set", &World::set)
    ;

On the other hand, if we do not wish to expose any constructors at all, we may use `no_init` instead:

    class_<Abstract>("Abstract", no_init)

This actually adds an `_init_` method which always raises a Python RuntimeError exception.

### Class Data Members

Data members may also be exposed to Python so that they can be accessed as attributes of the corresponding Python class. Each data member that we wish to be exposed may be regarded as read-only or read-write. Consider this class `Var`:

    struct Var
    {
        Var(std::string name) : name(name), value() {}
        std::string const name;
        float value;
    };

Our C++ `Var` class and its data members can be exposed to Python:

    class_<Var>("Var", init<std::string>())
        .def_readonly("name", &Var::name)
        .def_readwrite("value", &Var::value);

Then, in Python, assuming we have placed our Var class inside the namespace hello as we did before:

    >>> x = hello.Var('pi')
    >>> x.value = 3.14
    >>> print x.name, 'is around', x.value
    pi is around 3.14

Note that `name` is exposed as read-only while `value` is exposed as read-write.

        >>> x.name = 'e' # can't change name
        Traceback (most recent call last):
          File "<stdin>", line 1, in ?
        AttributeError: can't set attribute

### Class Properties

In C++, classes with public data members are usually frowned upon. Well designed classes that take advantage of encapsulation hide the class' data members. The only way to access the class' data is through access (getter/setter) functions. Access functions expose class properties. Here's an example:

    struct Num
    {
        Num();
        float get() const;
        void set(float value);
        ...
    };

However, in Python attribute access is fine; it doesn't neccessarily break encapsulation to let users handle attributes directly, because the attributes can just be a different syntax for a method call. Wrapping our `Num` class using Boost.Python:

    class_<Num>("Num")
        .add_property("rovalue", &Num::get)
        .add_property("value", &Num::get, &Num::set);

And at last, in Python:

    >>> x = Num()
    >>> x.value = 3.14
    >>> x.value, x.rovalue
    (3.14, 3.14)
    >>> x.rovalue = 2.17 # error!

Take note that the class property `rovalue` is exposed as read-only since the `rovalue` setter member function is not passed in:

    .add_property("rovalue", &Num::get)

### Inheritance

In the previous examples, we dealt with classes that are not polymorphic. This is not often the case. Much of the time, we will be wrapping polymorphic classes and class hierarchies related by inheritance. We will often have to write Boost.Python wrappers for classes that are derived from abstract base classes.

Consider this trivial inheritance structure:

    struct Base { virtual ~Base(); };
    struct Derived : Base {};

And a set of C++ functions operating on `Base` and `Derived` object instances:

    void b(Base*);
    void d(Derived*);
    Base* factory() { return new Derived; }

We've seen how we can wrap the base class `Base`:

    class_<Base>("Base")
        /*...*/
        ;

Now we can inform Boost.Python of the inheritance relationship between `Derived` and its base class `Base`. Thus:

    class_<Derived, bases<Base> >("Derived")
        /*...*/
        ;

Doing so, we get some things for free:

1. Derived automatically inherits all of Base's Python methods (wrapped C++ member functions)
2. If Base is polymorphic, `Derived` objects which have been passed to Python via a pointer or reference to `Base` can be passed where a pointer or reference to `Derived` is expected.

Now, we shall expose the C++ free functions `b` and `d` and `factory`:

    def("b", b);
    def("d", d);
    def("factory", factory);

Note that free function `factory` is being used to generate new instances of class `Derived`. In such cases, we use `return_value_policy<manage_new_object>` to instruct Python to adopt the pointer to `Base` and hold the instance in a new Python `Base` object until the the Python object is destroyed. We shall see more of Boost.Python call policies later.

    // Tell Python to take ownership of factory's result
    def("factory", factory,
        return_value_policy<manage_new_object>());

### Class Virtual Functions

In this section, we shall learn how to make functions behave polymorphically through virtual functions. Continuing our example, let us add a virtual function to our `Base` class:

    struct Base
    {
        virtual ~Base() {}
        virtual int f() = 0;
    };

One of the goals of Boost.Python is to be minimally intrusive on an existing C++ design. In principle, it should be possible to expose the interface for a 3rd party library without changing it. It is not ideal to add anything to our class `Base`. Yet, when you have a virtual function that's going to be overridden in Python and called polymorphically from C++, we'll need to add some scaffoldings to make things work properly. What we'll do is write a class wrapper that derives from `Base` that will unintrusively hook into the virtual functions so that a Python override may be called:

    struct BaseWrap : Base, wrapper<Base>
    {
        int f()
        {
            return this->get_override("f")();
        }
    };

Notice too that in addition to inheriting from `Base`, we also multiply- inherited `wrapper<Base>` (See Wrapper). The `wrapper` template makes the job of wrapping classes that are meant to overridden in Python, easier.

MSVC6/7 Workaround If you are using Microsoft Visual C++ 6 or 7, you have to write `f` as: `return call<int>(this->get_override("f").ptr());`.

BaseWrap's overridden virtual member function `f` in effect calls the corresponding method of the Python object through `get_override`.

Finally, exposing `Base`:

    class_<BaseWrap, boost::noncopyable>("Base")
        .def("f", pure_virtual(&Base::f))
        ;

`pure_virtual` signals Boost.Python that the function `f` is a pure virtual function.

member function and methods Python, like many object oriented languages uses the term methods. Methods correspond roughly to C++'s member functions

### Virtual Functions with Default Implementations

We've seen in the previous section how classes with pure virtual functions are wrapped using Boost.Python's class wrapper facilities. If we wish to wrap non-pure-virtual functions instead, the mechanism is a bit different.

Recall that in the previous section, we wrapped a class with a pure virtual function that we then implemented in C++, or Python classes derived from it. Our base class:

    struct Base
    {
        virtual int f() = 0;
    };

had a pure virtual function `f`. If, however, its member function `f` was not declared as pure virtual:

    struct Base
    {
        virtual ~Base() {}
        virtual int f() { return 0; }
    };

We wrap it this way:

    struct BaseWrap : Base, wrapper<Base>
    {
        int f()
        {
            if (override f = this->get_override("f"))
                return f(); // *note*
            return Base::f();
        }

        int default_f() { return this->Base::f(); }
    };

Notice how we implemented `BaseWrap::f`. Now, we have to check if there is an override for `f`. If none, then we call `Base::f()`.

MSVC6/7 Workaround If you are using Microsoft Visual C++ 6 or 7, you have to rewrite the line with the `*note*` as: `return call<char const*>(f.ptr());`.

Finally, exposing:

    class_<BaseWrap, boost::noncopyable>("Base")
        .def("f", &Base::f, &BaseWrap::default_f)
        ;

Take note that we expose both `&Base::f` and `&BaseWrap::default_f`. Boost.Python needs to keep track of 1) the dispatch function `f` and 2) the forwarding function to its default implementation `default_f`. There's a special `def` function for this purpose.

In Python, the results would be as expected:

    >>> base = Base()
    >>> class Derived(Base):
    ...     def f(self):
    ...         return 42
    ...
    >>> derived = Derived()

Calling `base.f()`:

    >>> base.f()
    0

Calling `derived.f()`:

    >>> derived.f()
    42

### Class Operators/Special Functions

## Python Operators

C is well known for the abundance of operators. C++ extends this to the extremes by allowing operator overloading. Boost.Python takes advantage of this and makes it easy to wrap C++ operator-powered classes.

Consider a file position class `FilePos` and a set of operators that take on FilePos instances:

    class FilePos { /*...*/ };

    FilePos     operator+(FilePos, int);
    FilePos     operator+(int, FilePos);
    int         operator-(FilePos, FilePos);
    FilePos     operator-(FilePos, int);
    FilePos&    operator+=(FilePos&, int);
    FilePos&    operator-=(FilePos&, int);
    bool        operator<(FilePos, FilePos);

The class and the various operators can be mapped to Python rather easily and intuitively:

    class_<FilePos>("FilePos")
        .def(self + int())          // __add__
        .def(int() + self)          // __radd__
        .def(self - self)           // __sub__
        .def(self - int())          // __sub__
        .def(self += int())         // __iadd__
        .def(self -= other<int>())
        .def(self < self);          // __lt__

The code snippet above is very clear and needs almost no explanation at all. It is virtually the same as the operators' signatures. Just take note that `self` refers to FilePos object. Also, not every class `T` that you might need to interact with in an operator expression is (cheaply) default-constructible. You can use `other<T>()` in place of an actual `T` instance when writing "self expressions".

## Special Methods

Python has a few more Special Methods. Boost.Python supports all of the standard special method names supported by real Python class instances. A similar set of intuitive interfaces can also be used to wrap C++ functions that correspond to these Python special functions. Example:

    class Rational
    { operator double() const; };

    Rational pow(Rational, Rational);
    Rational abs(Rational);
    ostream& operator<<(ostream&,Rational);

    class_<Rational>()
        .def(float_(self))                  // __float__
        .def(pow(self, other<Rational>))    // __pow__
        .def(abs(self))                     // __abs__
        .def(str(self))                     // __str__
        ;

Need we say more?

What is the business of `operator<<``.def(str(self))`? Well, the method `str` requires the `operator<<` to do its work (i.e. `operator<<` is used by the method defined by def(str(self)).

| Copyright © 2002-2004 Joel de Guzman, David Abrahams