Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Wrapping C++ Classes in Cython


Overview

This page aims to get you quickly up to speed so you can wrap C++ interfaces with a minimum of pain and 'surprises'.

In the past, Pyrex (and later, Cython) only supported wrapping of C APIs, and not C++. To wrap C++, one had to write a pure-C shim, containing functions for constructors/destructors and method invocations. Object pointers were passed around as opaque void pointers, and cast to/from object pointers as needed. This approach did work, but it got awfully messy and error-prone when trying to wrap APIs with large class hierarchies and lots of inheritance. Later versions of Pyrex and Cython simplified this somewhat by allowing textual definitions that would get generated literally into the output sources. This was still error prone but was at least capable of interfacing with a lot of existing code through a somewhat self-contained declaration.

Since then, a lot of work went into improving Cython and starting with version 0.13, it offers heavily extended support for C++ code in 'real' language syntax. The approach described in this document will help you wrap a lot of C++ code with only little effort. Due to the vast landscape that C++ considers valid syntax, there are still some limitations, which we will discuss at the end of the document.


Procedure Overview

  • Specify C++ language in setup.py script
  • Create cdef extern from blocks with the optional namespace (if exists) and the namespace name as string
  • Declare classes as cdef cppclass blocks
  • Declare public attributes (variables, methods and constructors)

An example C++ API

Here is a tiny C++ API which we will use as an example throughout this document. Let's assume it will be in a header file called Rectangle.h:

namespace shapes {
  class Rectangle {
  public:
    int x0, y0, x1, y1;
    Rectangle(int x0, int y0, int x1, int y1);
    ~Rectangle();
    int getLength();
    int getHeight();
    int getArea();
    void move(int dx, int dy);
  };
}

This is pretty dumb, but should suffice to demonstrate the steps involved. An implementation is needed as well to test it out.


Specify C++ language in setup.py

In Cython setup.py scripts, one normally instantiates an Extension object. To make Cython generate and compile a C++ source, you just need to add a keyword to your Extension construction statement, as in:

ext = Extension(
    "rectangle",                 # name of extension
    ["rectangle.pyx"],           # filename of our Pyrex/Cython source
    language="c++",              # this causes Pyrex/Cython to create C++ source
    include_dirs=[...],          # usual stuff
    libraries=[...],             # ditto
    extra_link_args=[...],       # if needed
    cmdclass = {'build_ext': build_ext}
    )

With the language="c++" keyword, Cython distutils will generate a C++ file.


Create cdef extern from block

The procedure for wrapping a C++ class is quite similar to that for wrapping normal C structs, with a couple of additions. Let's start here by creating the basic cdef extern from block:

cdef extern from "Rectangle.h" namespace "shapes":

This will make the C++ class definition for Rectangle available by including its header file.

Declare class with cdef cppclass

Now, let's add the Rectangle class to this extern from block - just copy the class name from Rectangle.h and adjust for Cython syntax, so now it becomes:

cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Rectangle:

Add public attributes

We now need to declare the attributes for use on Cython:

cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Rectangle:
        Rectangle(int, int, int, int)
        int x0, y0, x1, y1
        int getLength()
        int getHeight()
        int getArea()
        void move(int, int)

Declare a var with the wrapped C++ class

Now, we use cdef to declare a var of the class with the know C++ new statement

cdef Rectangle *rec = new Rectangle(1, 2, 3, 4)
cdef int recLength = rec.getLength()
...
del rec #delete heap allocated object

It's also possible to declare a stack allocated object, but it's necessary to have a "default" constructor

cdef extern from "Foo.h":
    cdef cppclass Foo:
        Foo()

cdef Foo foo

Note that, like C++, if the class has only one constructor and it is a default one, it's not necessary to declare it.

Overloading

Overloading is very simple. Just declare the method with different parameters and use any of them.

cdef extern from "Foo.h":
    cdef cppclass Foo:
        Foo(int)
        Foo(bool)
        Foo(int, bool)
        Foo(int, int)

Overloading operators

Cython uses C++ for overloading operators.

cdef extern from "foo.h":
    cdef cppclass Foo:
        Foo()
        Foo* operator+(Foo*)
        Foo* operator-(Foo)
        int operator*(Foo*)
        int operator/(int)

cdef Foo* foo = new Foo()
cdef int x

cdef Foo* foo2 = foo[0] + foo
foo2 = foo[0] - foo[0]

x = foo[0] * foo2
x = foo[0] / 1

cdef Foo f
foo = f + &f
foo2 = f - f

del foo, foo2

Templates

Cython uses a bracket syntax for templating. A simple example for wrapping C++ vector:

from cython.operator cimport dereference as deref, preincrement as inc #dereference and increment operators

cdef extern from "<vector>" namespace "std":
    cdef cppclass vector[T]:
        cppclass iterator:
            T operator*()
            iterator operator++()
            bint operator==(iterator)
            bint operator!=(iterator)
        vector()
        void push_back(T&)
        T& operator[](int)
        T& at(int)
        iterator begin()
        iterator end()

cdef vector[int] *v = new vector[int]()
cdef int i
for i in range(10):
    v.push_back(i)

cdef vector[int].iterator it = v.begin()
while it != v.end():
    print deref(it)
    inc(it)

del v

Multiple template parameters can be defined as a list, such as [T, U, V] or [int, bool, char].

Caveats and Limitations

In this document, we have discussed a relatively straightforward way of wrapping C++ classes with Cython. However, there are some limitations in this approach, some of which could be overcome with clever workarounds (anyone here want to share some?), but some of which will require new features in Cython.

The major limitations I'm most immediately aware of (and there will be many more) include:

Access to C-only functions

Whenever generating C++ code, Cython generates declarations of and calls to functions assuming these functions are C++ (ie, not declared as ``extern "C" {...} ``. This is ok if the C functions have C++ entry points, but if they're C only, you will hit a roadblock. If you have a C++ Cython module needing to make calls to pure-C functions, you will need to write a small C++ shim module which:

  • includes the needed C headers in an extern "C" block
  • contains minimal forwarding functions in C++, each of which calls the respective pure-C function

Inherited C++ methods

If you have a class Foo with a child class Bar, and Foo has a method fred, then you'll have to cast to access this method from Bar objects. For example:

class MyClass:
    Bar *b
    ...
    def myfunc(self):
        ...
        b.fred()   # wrong, won't work
        (<Foo *>(self.b)).fred() # should work, Cython now thinks it's a 'Foo'

It might take some experimenting by others (you?) to find the most elegant ways of handling this issue.

Advanced C++ features

Exceptions

Cython cannot raise C++ exceptions, or catch them with a try-except statement, but it is possible to declare a function as potentially raising an C++ exception and converting it into a Python exception. For example,

cdef extern from "some_file.h":
    cdef int foo() except +

This will translate try and the C++ error into an appropriate Python exception (currently an IndexError on std::out_of_range and a RuntimeError otherwise (preserving the what() message).

cdef int bar() except +MemoryError

This will catch any C++ error and raise a Python MemoryError in its place. (Any Python exception is valid here.)

cdef int raise_py_error()
cdef int something_dangerous() except +raise_py_error

If something_dangerous raises a C++ exception then raise_py_error will be called, which allows one to do custom C++ to Python error "translations." If raise_py_error does not actually raise an exception a RuntimeError will be raised.

Declaring/Using References

__Question__: How do you declare and call a function that takes a reference as an argument?


Conclusion

A great many existing C++ classes can be wrapped using these techniques, in a way much easier than writing a large messy C shim module. There's a bit of manual work involved, and an annoying maintenance burden if the C++ library you're wrapping is frequently changing, but this recipe should hopefully keep the discomfort to a minimum.

If you do have any problems, I'd be happy to hear from you: rebirth_REMOVETHISPART@orcon.net.nz

The full examples used on this page are attachment:Setup.py, attachment:cpp_rect.cpp, attachment:cpp_rect.h, and attachment:rectangle.pyx.

Something went wrong with that request. Please try again.