gsoc09 daniloaf progress
A simple tutorial about what is done for C++ in Cython, to be merged in 0.13.
What we have:
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.
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").
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
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:
...
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
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
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
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.
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.