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標準庫
- 靜態成員方法