Skip to content

gsoc09 daniloaf progress

daniloaf edited this page Mar 25, 2010 · 33 revisions

A simple tutorial about what is done for C++ in Cython, to be merged in 0.13.

What we have:

C++ classes declarations

It's very simple. For that, a new statement (cppclass) is used. It's important to know that C++ classes need to be extern. For example:

We have a file "a.h" with the following:

#ifndef _A_H_
#define _A_H_

class A
{
public:
    int getNumber();
    int something;
private
    int number;
};

#endif

Now we want to use it in Cython. Just do:

cdef extern from "a.h":
    cdef cppclass A:
        int getNumber()
        int something

As you can see, we don't use "cdef" statement to declare the class attributes.

new and del operators

To instanciate a C++ class, we need to use the "new" operator.

cdef extern from "foo.h":
    cdef cppclass Foo:
        Foo(int) #constructor
        int getNumber()
        int number

cdef Foo *foo = new Foo(10)
...

We also may declare without a constructor. When this is done, a default constructor is declared:

cdef extern from "foo.h":
    cdef cppclass Foo:
        int number
        int getNumber()

cdef Foo *foo = new Foo()
...

Now, when we don't need an object anymore, we want to delete it from memory, to free memory. For this, we use the statement "del":

...
cdef Foo *foo = new Foo(10)
cdef int n = foo.getNumber()
...
del foo

The statement "del" can be used with more than one argument (e. g. "del foo1, foo2, foo3").

Inheritance

C++ supports multiple inheritance, and that is fully supported in Cython. For example, if we have the class C that inherits from A and B, we write:

cdef extern from "cpp_classes.h":
    cdef cppclass A:
        ...

    cdef cppclass B:
        ...

    cdef cppclass C(A, B):
        ...

cdef A *a = new C()
cdef B *b = new C()

del a, b

Namespace

It's very simple the use of namespaces. If we have the file "foo.h" with the following:

namespace foo
{
    ...
}

And now we want to use it in Cython:

cdef extern from "foo.h" namespace foo:
    ...

Polymorphism

As Cython supports inheritance, it also needs to support polymorphim. Polymorphic functions work normally:

cdef extern from *:
    cdef cppclass A:
        int a
        int getA()
    cdef cppclass B(A):
        int b
        int getB()

cdef A *a = new A()
cdef B *b = new B()

print a.getA()
print b.getA() # works fine, but not really polymorphism. (A good test though.)

del a, b

Overloading Operators

For that, we're using C++ syntax (maybe we change this later). It's very simple to use this feature:

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 #Please avoid declaring C++ objects this way. Use 'cdef Foo *f = new Foo()'
foo = f + &f
foo2 = f - f

del foo, foo2

Templates

We're using a bracket syntax. We declare the type names after declaring something. For example:

#This example is from a test file from Cython (tests/compile/cpp_templates.pyx)
cdef extern from "templates.h":
    cdef cppclass TemplateTest1[T]:
        TemplateTest1()
        T value
        int t
        T getValue()

    cdef cppclass TemplateTest2[T, U]:
        TemplateTest2()
        T value1
        U value2
        T getValue1()
        U getValue2()

cdef TemplateTest1[int] a
cdef TemplateTest1[int]* b = new TemplateTest1[int]()

cdef int c = a.getValue()
c = b.getValue()

cdef TemplateTest2[int, char] d
cdef TemplateTest2[int, char]* e = new TemplateTest2[int, char]()

c = d.getValue1()
c = e.getValue2()

cdef char f = d.getValue2()
f = e.getValue2()

del b, e

Function Overloading

Suppose one has square.h:

int square(int x) {
    return x*x;
}

double square(double x) {
    return x*x;
}

void square(double* res, double x) {
    *res = x*x;
}

Then one can use these from a Cython file as

cdef extern from "square.h":
    int square(int n)
    double square(double x)
    void square(double* res, double x)

cdef int a = 3
cdef int b = square(a)
cdef double x = square(3.14159)
cdef double res
square(&res, 0.57721566)

Note that the exact function resolution depends on the notion of conversion and promotion, which for integral types may not be known until C compile time. This is ongoing work, a ticket about this will be created soon.

Examples

Let's wrap a bigger code to Cython. For example, if we have the following "Shapes.h" file:

#include <vector>

using namespace std;

namespace shapes {

    template<class T> class Point {
    private:
        T x, y;
    public:
        Point(T x, T y);
        virtual ~Point();
        T getX();
        T getY();
        Point operator+(Point p);
        Point operator-(Point p);
    };

    template<class T> class Polygon {
        protected:
            vector<Point<T> > points;
        public:
            Polygon();
            Polygon(vector<Point<T> > points);
            virtual ~Polygon();
            vector<Point<T> > getPoints();
            virtual T area();
        };

    template<class T> class RegularPolygon : public Polygon<T> {
    protected:
        T side;
    public:
        RegularPolygon();
        RegularPolygon(vector<Point<T> > points, T side);
        virtual ~RegularPolygon();
        T getSide();
        virtual T area();
    };

    template<class T> class Rectangle : public Polygon<T> {
        public:
            Rectangle();
            Rectangle(Point<T> downLeft, Point<T> upRight);
            virtual ~Rectangle();
    };

    template<class T> class Square : public Rectangle<T>, RegularPolygon<T> {
    public:
        Square(Point<T> downLeft, T side);
        virtual ~Square();
        virtual T area();
    };

}

Now, we want to use them on Cython.

cdef extern from "<vector>" namespace std:
    cdef cppclass vector[T]:
        void push_back(T)

cdef extern from "Shapes.h" namespace shapes:
    cdef cppclass Point[T]:
        Point(T, T)
        Point operator+(Point)
        Point operator-(Point)

    cdef cppclass Polygon[T]:
        Polygon()
        Polygon(vector[Point[T]])
        T area()

    cdef cppclass RegularPolygon[T](Polygon):
        RegularPolygon()
        RegularPolygon(vector[Point[T]], T)
        T getSide()
        #T area() - not necessary

    cdef cppclass Rectangle[T](Polygon):
        Rectangle()
        Rectangle(Point[T], Point[T])

    cdef cppclass Square[T](Rectangle, RegularPolygon):
        Square(Point[T], int)

cdef Point[double] *p1 = new Point[double](0, 0)
cdef Point[double] *p2 = new Point[double](10, 0)
cdef Point[double] *p3 = new Point[double](0, 10)
cdef Point[double] *p4 = new Point[double](10, 10)

cdef vector[Point[double]] *points = new vector[Point[double]]()
points.push_back(p1[0])
points.push_back(p2[0])
points.push_back(p3[0])
points.push_back(p4[0])

cdef Polygon[double] *polygon = new Polygon[double](points[0])
cdef Rectangle[double] *rec = new Rectangle[double](p1[0], p4[0])
cdef Square[double] *sqr = new Square[double](p1[0], 10)

del p1, p2, p3, p4, points, polygon, rec, sqr

So, we have an example using some C++ features.

Clone this wiki locally