Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Supporting C++ interfaces through inline C++ code

This is just Dag's thoughts, nothing official.

C++ and Cython semantics doesn't match in lots of areas (operator overloading, references, f() = 3...).

As the semantics doesn't match anyway, why try to support 90% of it by wedging C++ into Cython?

This is a very different approach: Do not support C++ semantics, but use inline C++ code and "overlay methods" in order to tell Cython which C++ code should be generated from case to case.

Inline C++ methods

Example, using a simple container storing floats at integer indices.

#!python
from cython import c

cdef extern "C++" from "mylib.h":
    class MyIterator:
        cdef void move_next(self):
            c("++self")
        cdef void move_prev(self):
            c("--self")
        cdef float get(self):
            return c("*self")
        cdef float __eq__(self, MyIterator other) # defaults to c("self == other")
        cdef float __ne__(self, MyIterator other) # defaults to c("self != other")
        cdef float __le__(self, MyIterator other) # defaults to c("self < other")
        ...

    class MyContainer:
        cdef MyIterator begin(self) # present in C++
        cdef MyIterator end(self)   # present in C++

        cdef float __getitem__(self, int key)             # defaults to c("self[value]")
        cdef void __setitem__(self, int key, float value) # defaults to c("self[key] = value")
        cdef MyContainer __add__(self, MyContainer other) # defaults to c("self + other")

        # Perhaps in C++ one has "float& middle()", then we can instead wrap it as
        cdef float middle(self):
            return c("self.middle()")
        cdef void set_middle(self, float value):
            c("self.middle() = value")

        # Cython code is allowed too
        cdef bint is_empty(self):
            return self.begin() == self.end()

    # Finally, if a function takes a reference, like inc_arg(int& x), one can
    # use c() to rewrite to taking a pointer instead, saving Cython semantics.
    void inc_arg(int* value):
        c("inc_arg(*value)")

Note that C++ side, operator[] could for instance return a reference, or an object with overloaded operator=.

Notes:
  • This is not creating a Python type; instead it is merely telling Cython how to deal with a C++ type, by adding additional methods as syntax candy for a custom wrapper.
  • In many cases, there's no need to resort to inlining, the defaults will suffice.

Implementation

The Cython code iter.move_next() would be transformed into mangled_MyIterator_move_next(iter) (assuming iter is a pointer, otherwise prepend &), which would look like

void mangled_MyIterator_move_next(MyIterator* __pyx_self) {
    ++(*__pyx_self);
}

It seems to me that implementing the c command can be done pretty reliable through string substitution.

Outstanding issues for C++ support

The above is not enough.

Conversion of objects

In the same spirit, rather than declaring operator= or operator Type etc., just tell Cython about the types:

cdef extern "C++":
    class MyClass:
        __convertsto__ = (OtherClass, YetAnotherClass)

Thoughts on plain C and inline C methods

There is no reason to keep this to C++, perhaps somebody would think this is convenient for OO-style C code:

cdef extern:
    struct my_type:
        cdef void operation(self): my_type_operation(self)
    cdef void my_type_operation(my_type* obj)

Note: OpenMP support

The c command could be used to get rudimentary very-low-level OpenMP support, but that's another story...

Something went wrong with that request. Please try again.