Using C++ in Cython
Cython对C++语言的大部分特性都支持,例如:
- 可以通过
new
和del
关键字在堆区动态创建和消耗对象 - 可以在栈区创建对象
- 也可以通过Cython提供的
cppclass
关键字声明类类型 - Cython支持函数模板(template function)和类模板(template class),函数重载
- Cython支持C++操作符重载(eg: operator+,operator[] …)
通过Cython封装一个C++文件的大致步骤
- 1、在
setup.py
脚步文件中指定C++语言或指名C++源文件 - 2、创建一个或多个
.pxd
文件,并用cdef extern from
blocks 和C++命名空间名。在每个blocks中:- 使用
cppclass
声明类 - 将成员变量、方法、构造方法声明为公有的(public)
- 使用
- 3、在
.pyx
文件中,通过cimport
导入上面的这些扩展模块
C++ API 封装示例:
-
1、实现一个C++ API,用于计算矩形面积的。
-
新建头文件
Rectangle.h
#ifndef RECTANGLE_H #define RECTANGLE_H namespace shapes { class Rectangle { public: int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; } #endif
-
新建对应的类类型实现文件
Rectangle.cpp
#include "Rectangle.h" namespace shapes { // 默认构造 Rectangle::Rectangle () {} // 有参构造 Rectangle::Rectangle (int x0, int y0, int x1, int y1) { this->x0 = x0; this->y0 = y0; this->x1 = x1; this->y1 = y1; } // 析构函数 Rectangle::~Rectangle () {} // 获取矩形的面积 int Rectangle::getArea () { return (this->x1 - this->x0) * (this->y1 - this->y0); } // 获取矩形的长宽 // Put the size in the pointer args void Rectangle::getSize (int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } // 移动矩形位置 void Rectangle::move (int dx, int dy) { this->x0 += dx; this->y0 += dy; this->x1 += dx; this->y1 += dy; } }
-
-
2、在Cython声明上述的成员,Cython将这些声明放在
Rectangle.pxd
文件,可以将它看着是Cython可读的头文件。cdef extern from "Rectangle.cpp": pass # Declare the class with cdef cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int)
第1~2两行是显示指明包含C++的代码文件,也可以在这里写,再后续的
.pyx
文件中通过如下代码指明也可以。# distutils: sources = Rectangle.cpp
-
3 最后一步,通过Cython开放Rectangle接口,新建
rect.pyx
文件# distutils: language = c++ from Rectangle cimport Rectangle # Create a Cython extension type which holds a C++ instance # as an attribute and create a bunch of forwarding methods # Python extension type. cdef class PyRectangle: cdef Rectangle c_rect # Hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy)
第一行的
# distutils: language = c++
是显示告诉Cython按照C++语言编译。 -
4 脚本编译setup.py
from distutils.core import setup from Cython.Build import cythonize setup(ext_modules=cythonize("rect.pyx"))
编译链接:
python3 setup.py build_ext --inplace
生成rect.xxx.so文件 -
5 接口测试,当前目录下终端python3或ipython3
import rect x0, y0, x1, y1 = 1, 2, 3, 4 rect_obj = rect.PyRectangle(x0, y0, x1, y1) print(dir(rect_obj)) print('矩形长宽: ',rect_obj.get_size()) #矩形长宽: (2, 2) print('矩形面积: ',rect_obj.get_area()) 矩形长宽: (2, 2)
总结
展望
- 函数重载
- 操作符重载
- 模板
- CPP标准库
- 静态成员方法