Skip to content

Latest commit

 

History

History
103 lines (76 loc) · 4.7 KB

File metadata and controls

103 lines (76 loc) · 4.7 KB

调用C函数

这篇教程简短介绍了如何在Cython代码中调用C的库函数。如果需要一个更长且综合性更强的教程来指导你如何使用外部的C库、包装他们和处理错误,请看使用C库

让我们从一个标准C库的函数开始。这不会给你的代码添加其他的依赖关系,而且Cython已经定义了很多这样的函数,所以你可以直接cimport并且使用它们。

举个例子,假设你需要用一种低层方法来从char*值中解析数字,你可以使用被stdlib.h头文件定义的atoi()函数,如下所示:

from libc.stdlib cimport atoi

cdef parse_charptr_to_py_int(char* s):
    assert s is not NULL, "byte string value is NULL"
    return atoi(s)  # note: atoi() has no error detection!

你可以在Cython的源码包Cython/Includes/里找到标准cimport文件的完整列表。它们被存储在.pxd文件中。.pxd文件提供了一种可以在模块间共用可重利用的Cython声明的标准方法。(请看在Cython模块间共用声明)。

Cython还有一个供CPython的C-API使用的声明的完备集。举例来说,要在C编译时测试你的代码是由哪个CPython版本编译的,你可以进行如下操作:

from cpython.version cimport PY_VERSION_HEX

# Python version >= 3.2 final ?
print(PY_VERSION_HEX >= 0x030200F0)

Cython还提供了给C数学库使用的声明:

from libc.math cimport sin

cdef double f(double x):
    return sin(x * x)

动态链接

libc math库在部分类Unix系统(比如Linux)中没有被默认链接,因此比较特殊。除了cimport声明,你还必须要配置你的创建系统来链接到共享库m。对于distutils,将它添加到Extension()安装中的libraries参数中就可以了:

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

ext_modules = [
    Extension("demo",
              sources=["demo.pyx"],
              libraries=["m"]  # Unix-like specific
              )
]

setup(name="Demos",
      ext_modules=cythonize(ext_modules))

外部声明

如果你想访问一些C代码,而这些C代码还未被Cython提供准备好使用的声明,你就必须自己声明它们。比如说,上面的sin()就被定义如下:

cdef extern from "math.h":
    double sin(double x)

这将以Cython代码可以使用的一种方式声明sin()函数,并命令Cython生成包含math.h头文件的C代码。C编译器在编译时将会在math.h里找到原始的声明,但是Cython并不会解析"math.h",并且需要一个单独的定义。

正如math库中的sin()函数一样,只要Cython生成的模块被正确地链接到共享库或静态库中,我们就可以声明或是调用任何C库。

注意你可以可以通过将Cython模块声明为cpdef轻松地导出一个外部C函数。这会为它生成一个Python包装器,并把它加入模块字典中。下面是一个为Python代码提供了直接访问C的sin()函数功能的Cython模块:

"""
>>> sin(0)
0.0
"""

cdef extern from "math.h":
    cpdef double sin(double x)

当这个声明出现在属于Cython模块的.pxd文件中时,你将得到相同的结果(即具有相同名称的文件,请看在Cython模块间共用声明)。这允许在其他Cython模块中重用C声明,同时仍然在此特定模块中提供自动生成的Python包装器。

命名参数

C和Cython都支持没有参数名的签名声明,如下所示:

cdef extern from "string.h":
    char* strstr(const char*, const char*)

但是,这会阻碍Cython通过关键词参数调用它。所以,我们最好使用如下的声明替代:

cdef extern from "string.h":
    char* strstr(const char *haystack, const char *needle)

现在,你就可以搞清楚在你的调用中,这两个参数分别是什么,这样就可以避免歧义,并使你的代码可读性更高:

cdef extern from "string.h":
    char* strstr(const char *haystack, const char *needle)

cdef char* data = "hfvcakdfagbcffvschvxcdfgccbcfhvgcsnfxjh"

cdef char* pos = strstr(needle='akd', haystack=data)
print(pos is not NULL)

注意,稍后更改现有参数名称是一种向后不兼容的API修改,就像对Python代码一样。因此,如果你为外部C或C++函数提供了自己的声明,那么通常值得花费额外的精力来选择它们的参数名。