boost.python / How to

Legacy Wiki Page

This page was migrated from the old MoinMoin-based wiki. Information may be outdated or no longer applicable. For current documentation, see python.org.

How to expose…

static class data members

object x_class
    = class_<X>("X")
         .def( ... )
         ...
         ;

x_class.attr("fu") = X::fu;
x_class.attr("bar") = X::bar;
...

Since version 1.30 you can use class_ method:

   .add_static_property("name", &fget [,&fset])

static class functions

It’s likely to be in 1.30 release.

Meanwhile you should do something which mirrors pure Python:

class foo(object):
    def f(x,y):
        return x*y
    f = staticmethod(f)

So in C++, it would be something like:

    class_<foo>("foo")
        .def("f", &foo::f)
        .staticmethod("f")  // **
        ;

In version 1.33 staticmethod is already implemented, just use the same syntax as above.

Where the marked line would be implemented something like this:

self& staticmethod(char const* name)
{
    dict d(handle<>(borrowed(downcast<PyTypeObject>(this->ptr())->tp_dict)));
    object method = (object)(d[name]);
    this->attr(name) = object(handle<>(
         PyStaticMethod_New( callable_check(method.ptr()) )
      ));
    return *this;
}

To create overloaded staticmethods in python, you overload first, then you make it static.

If you want to expose a static function that returns a pointer to an already exposed C++ type, you have to specify a return policy. This is because python needs to know what to do with that pointer and how to track it after it is returned. The return policy specified what to do with the pointer, such as whether or not to free the memory after exiting. Like so:

    class_<foo>("foo")
        .def("f", &foo::f, return_value_policy<manage_new_object>())
        .staticmethod("f")  // **
        ;

This has told python that a pointer returned from foo:f() must be managed as a new object and is to be deleted when out of scope or at exit. There are more return policies. Return policies are all subtypes of ResultConverterGenerator, check it out in the docs.

module level objects

at module creation time

First, create those objects like

object class_X = class_<X>("X");
object x = class_X();

Second, expose them:

scope().attr("x") = x; // injects x into current scope

By default current scope is module.

at run-time

Use a function:

template <class T>
void set(const std::string& name, const T& value) {
  interpreter()->mainmodule()[name] = value;
}

Note:: interpreter() is going to be added.

mutable C++ object

Perhaps you’d like the resulting Python object to contain a raw pointer to the argument? In that case, the caveat is that if the lifetime of the C++ object ends before that of the Python object, that pointer will dangle and using the Python object may cause a crash.

Here’s how to expose mutable C++ object during module initialisation:

  scope().attr("a") = object(ptr(&class_instance));

std::C++ container

You can always wrap the container with class_ directive. For example for std::map:

template<class Key, class Val>
struct map_item
{
    typedef std::map<Key,Val> Map;

    static Val& get(Map const& self, const Key idx) {
      if( self.find(idx) != self.end() ) return self[idx];
      PyErr_SetString(PyExc_KeyError,"Map key not found");
      throw_error_already_set();
    }

    static void set(Map& self, const Key idx, const Val val) { self[idx]=val; }

    static void del(Map& self, const Key n) { self.erase(n); }

    static bool in(Map const& self, const Key n) { return self.find(n) != self.end(); }

    static list keys(Map const& self)
    {
        list t;
        for(Map::const_iterator it=self.begin(); it!=self.end(); ++it)
            t.append(it->first);
        return t;
    }
    static list values(Map const& self)
    {
        list t;
        for(Map::const_iterator it=self.begin(); it!=self.end(); ++it)
            t.append(it->second);
        return t;
    }
    static list items(Map const& self)
    {
        list t;
        for(Map::const_iterator it=self.begin(); it!=self.end(); ++it)
            t.append( make_tuple(it->first, it->second) );
        return t;
    }
}

using namespace boost::python;
typedef std::map<Key,Val> Map;
class_<Map>("Map")
  .def("__len__", &Map::size)
  .def("__getitem__", &map_item<Key,Val>().get, return_value_policy<copy_non_const_reference>() )
  .def("__setitem__", &map_item<Key,Val>().set)
  .def("__delitem__", &map_item<Key,Val>().del)
  .def("clear", &Map::clear)
  .def("__contains__", &map_item<Key,Val>().in)
  .def("has_key", &map_item<Key,Val>().in)
  .def("keys", &map_item<Key,Val>().keys)
  .def("values", &map_item<Key,Val>().values)
  .def("items", &map_item<Key,Val>().items)
  ;

To compile, you might need to add ‘typename’ before Map::const_iterator.

“Raw” function

I want to write in python:

    def function( *args ):
        # Mess arount with elements of args.

Method 1 (old way)

You can make one with the library’s implementation details. Be warned that these interfaces may change, but hopefully we’ll have an “official” interface for what you want to do by then.

python::objects::function_object(
    f                       // must be py_function compatible
    , 0, std::numeric_limits<unsigned>::max()  // arity range
    , python::detail::keyword_range());        // no keywords

will create a Python callable object.

f is any function pointer, function reference, or function object which can be invoked with two PyObject* arguments and returns something convertible to a PyObject*.

You can add this to your module namespace with:

scope().attr("name") = function_object(f, ... );

Now you can also

class_<foo>("foo")
    .def("bar",function_object(f, ... ) );

as well, but not

function_object bar(f, ... );
...
class_<foo>("foo")
    .def("bar",bar);

because function_object is a function, not a class.

Method 2 (new way)

There’s a poorly documented function called raw_function in boost::python that makes defining the functions with arbitrary arguments much easier. Another advantage of this method is that this is not using any functionality from the details namespace, so this should be more stable.

This is our goal:

   1 def function(*args, **kwargs):
   2         # do stuff here

To accomplish this from C++, we must wrap our functionality into a function with the following signature:

   1 object bar(tuple args, dict kwargs)
   2 {
   3         // do stuff here
   4 }

Then we can use raw_function to add it to our class:

   1 class_