首页 > Python > Python 最佳实践指南 2018 > Python 应用场景

6.18. 与 C/C++ 库交互

file

C语言外部函数接口

CFFI 提供了一套简单易用的机制来帮助 CPython 和 PyPy 与 C 语言进行交互。它支持两种模式:一种是内联的 ABI 兼容模式(下面举例说明),这一模式允许您从可执行模块中动态加载和运行函数(本质上与 LoadLibrary 和 dlopen 的功能相同);另一种是 API 模式,这一模式允许您构建 C 语言扩展模块。

from cffi import FFI
ffi = FFI()
ffi.cdef("size_t strlen(const char*);")
clib = ffi.dlopen(None)
length = clib.strlen("String to be evaluated.")
# 输出:23
print("{}".format(length))

ctypes

ctypes 是真正意义上让 CPython 与 C/C++ 进行交互的库,它不但提供了对大多数主流操作系统中原生 C 接口(例如 Windows 系统下的 kernel32,类 unix 系统下的 libc)的完全访问能力,而且还提供了对动态库进行加载和交互的支持,例如对 DLL 文件和运行时共享对象。ctypes 囊括了大量可用于与系统 API 交互的类型,并使您能够相当轻松地定义您自己的复杂类型,如结构体和联合体。如果需要的话,ctypes 还允许您修改诸如内边距、对齐方式等属性。ctypes 用起来可能略显繁琐,但与 struct 模块结合起来,您基本上就可以随心所欲地控制您的数据类型,把它们转换为可由纯 C/C++ 方法使用的内容了。

等价的结构体

MyStruct.h

struct my_struct {
    int a;
    int b;
};

MyStruct.py

import ctypes
class my_struct(ctypes.Structure):
    _fields_ = [("a", c_int),
                ("b", c_int)]

SWIG

SWIG 是一个用来为 C/C++ 头文件和解释型语言生成绑定的工具,不过它并不局限于Python(实际上它支持大量脚本语言)。这一工具极其简单易用:用户只需要定义一个接口文件(详见 SWIG 教程与文档),将必要的 C/C++ 头文件包含进来,再运行构建工具就可以了。虽然这一工具也有一些局限(例如,当前它似乎对一些较新的 C++ 特性不太支持,并且那些严重模板化的代码运行起来显得有点冗杂),但是它仅付出了很小的代价就提供了强大的功能并带给 Python 大量的特性。此外,您可以轻易地对 SWIG 创建的绑定进行扩展来重载 Python 的操作符与内置函数,以及高效地将 C++ 中的异常重新抛出并交由 Python 进行捕获等等。

示例:重载 repr

MyClass.h

#include <string>
class MyClass {
private:
    std::string name;
public:
    std::string getName();
};

myclass.i

%include "string.i"

%module myclass
%{
#include <string>
#include "MyClass.h"
%}

%extend MyClass {
    std::string __repr__()
    {
        return $self->getName();
    }
}

Boost.Python

Boost.Python 需要一点人工操作来引入 C++ 对象的功能,但它能够提供 SWIG 所有的相同特性,并且还包括了提供了封装函数,可用于在 C++ 中访问 Python 的 PyObjects 类型对象,提取 SWIG 封装的对象,甚至将 Python 的一些代码嵌入到您的 C++ 代码中。