Cython基础教程(五) -用cython包装C++库

用cython包装C++库

假设有一个简单的生成随机数的类, 提供了一下接口

namespace mtrandom {

const static unsigned int N = 624;

class MT_RNG {
    public:
        MT_RNG();
        MT_RNG(unsigned long s);
        MT_RNG(unsigned long init_key[], int ket_length);
    
    void init_genrand(unsigned long s);
    
    unsigned long genrand_int32();
    
    double genrand_real1();
    
    private:
        unsigned long mt[N];
        int mti;
};
}

Cython只能包装public方法和成员,不能访问任何privated或者protected方法和成员。
为了在Cython中使用这个类接口,可以使用extern关键词,有三个要求:

  • Declaring the c++ namespace with cython namespace clause
  • Using the cppclass keyword to declare a C++ class interface block
  • Declaring the class’s interface in this block

因为MT_RNG是在mtrandom命名空间中声明的,在cython中使用cdef extern必须说明命名空间,
,在extern代码快内部,声明了常数N, 以及用cppclass关键词声明MT_RNG c++类, 并且在该类内部,将所有想在cython中使用的的Public结构体,函数,类成员都放进去,这样在Cython代码中就可以访问。

cdef extern from "mt19937.h" namespace "mtrandom":
    unsigned int N
    cdef cppclass MT_RNG:
        MT_RNG(unsigned long s)
        MT_RNG(unsigned long init_key[], int key_length)
        void init_genrand_int32()
        unsigned long genrand_int32()
        double genrand_real1()

在cython中一般使用扩展类型包装c++类。定义一个扩展类型RNG, 并且该扩展类型有一个指针指向被包装的c++类, 用__cinit__方式进行初始化, 用__dealloc__进行内存回收。__dealloc__只在程序结束的时候使用,并且只调用一次。现在可以通过cpdef定义一些类成员函数来生成随机数(通过_thisptr指针),下面是一个包装c++类的范例。

cdef class RNG:
    cdef MT_RNG *_thisptr
    def __cinit__(self, unsigned long s):
        self._thisptr = new MT_RNG(S)
    def __dealloc__(self):
        if self._thisptr != NULL:
            del self._thisptr
    cpdef unsigned long randint(self):
        return self._thisptr.genrand_int32()
    cpdef double rand(self):
        return self._thisptr.genrand_real1()

编译

当我们编译一个c++项目的时候,我们需要说明使用c++语言,并且包含c++的源文件。例如,要编译RNG.pyx文件,需要新建一个setup.py,包含的代码如下:

from distutils.core import setup, Extension
from Cython.Build import cythonize

ext = Extension("RNG", source=['RNG.pyx', 'mt19937.cpp'], language='c++')

setup(name="RNG", ext_modules=cythonize(ext))

重载函数和方法

对于MT_RNG类来说,不同的参数对应不同的构造函数,那么在python中,该如何调用呢?因为python不支持函数的重载,所以只能在__cinit__中检查参数的类型,使用对应的c++构造函数. 为此,需要python标准库中的内置array(要求内置的元素类型都一样),cython知道如何在python和c++中处理array类型的变量。

from cython.array import array 

cdef class RNG:
    cdef MT_RNG *_thisptr
    def __cinit__(self, seed_or_state):
        if isinstance(seed_or_state, int):
            self._thisptr = new MT_RNG(seed_or_state)
        else:
            state_arr = array("L", seed_or_state)
            self._thisptr = new MT_RNG(state_arr.data.as_ulongs, len(state_Arr))

在cython代码中直接使用C++类

我们已经知道怎么用扩展类型来包装c++类,这也是在cython使用c++类最常见的做法。当然,也可以直接在Cython代码里面使用C++类,可以使用stack-allocate一个MT_RNG实例,并且在程序结束的时候清理这个实例。为了使用c++对象,同样需要在cef cppclass代码块中进行声明:

cdef extern from "mt19937.h" namespace "mtrandom":
    cdef cppclass MT_RNG:
        MT_RNG()
        void init_genrand(unsigned long s)

现在可以在函数中对MT_RNG对象进行一些操作

def make_random_list(unsigned long seed, unsigned int len):
    cdef:
        #...
        MT_RNG *rng
    rng = new MT_RNG(seed)
    try:
        #...
    finally:
        del rng

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值