文章

Pyxcpp混合调用

使用Python调用Cpp

ctypes

ctypes是Python的一个外部函数库,它提供了与C语言兼容的数据类型,允许调用动态链接库中的函数。

official doc

使用cdll加载动态链接库

假设有一个cpp文件dyna.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
extern "C"{
    int add(int, int);
}

int add(int a, int b){
    return a + b;
}

// extern等价写法:
extern "C" int minus(int a, int b){
    return a - b;
}

注意:由于cpp为实现多态,在编译时会将函数名进行重命名,因此需要使用extern "C"来声明避免这种情况。

并将其编译为动态链接库dyna.so:

1
g++ -shared -fPIC dyna.cpp -o dyna.so

那么可在Python中调用:

1
2
3
4
5
6
7
from ctypes import cdll

dyna_lib = cdll.LoadLibrary("./dyna.so")
print(dyna_lib.add(1, 2)) 
# >>> 3
print(dyna_lib.minus(1, 2))
# >>> -1

使用c++数据类型/指针/数组

  1. 使用c_int, c_double, c_char_p等数据类型

ctypes 提供了一些与C语言数据类型对应的Python数据类型,如c_int, c_double, c_char_p等。

详见支持的数据类型

1
2
3
4
5
6
import ctypes

a = ctypes.c_int(1) # int
b = ctypes.c_double(1.0) # double
c = ctypes.c_char_p(b"hello") # char*
# ...
  1. 使用pointer指针

ctypes 提供了pointer函数用于获取指针。

1
2
3
4
5
6
import ctypes

a = ctypes.c_int(1)
b = ctypes.pointer(a) # b为a的指针
print(b.contents)
# >>> c_int(1)
  1. 使用c_int * n数组

创建数组类型的推荐方式是使用一个类型乘以一个正数:

1
2
3
4
5
import ctypes

arr = (ctypes.c_int * 3)(1, 2, 3)
print(arr[0], arr[1], arr[2])
# >>> 1 2 3

使用结构体

ctypes 提供了Structure类用于创建结构体。

假设有一个cpp文件dyna.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point{
    int x;
    int y;
};

extern "C"{
    Point get_point(){
        Point p;
        p.x = 1;
        p.y = 2;
        return p;
    }
}

并将其编译为动态链接库dyna.so:

1
g++ -shared -fPIC dyna.cpp -o dyna.so

那么可在Python中调用:

1
2
3
4
5
6
7
8
9
10
import ctypes

class Point(ctypes.Structure):
    _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]

dyna_lib = ctypes.CDLL("./dyna.so")
dyna_lib.get_point.restype = Point
p = dyna_lib.get_point()
print(p.x, p.y)
# >>> 1 2

cython

Cython是一个Python的扩展,它允许在Python中使用C语言的数据类型和函数。 实现混合编写,即在Python中调用C语言的函数。

official doc

以下是一个简单的例子:

1
cimport numpy as np

pybind11

pybind11是一个只有头文件的轻量级库,实现C++到Python的绑定

中文文档

英文文档

安装

  1. 直接pip安装
1
pip install pybind11
  1. 使用git submodule集成
1
2
git submodule add /path/to/pybind11
git submodule update --init

使用

简单示例:

1
2
3
4
5
6
7
8
9
10
#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) { // 模块名为example, m为模块对象
    m.doc() = "pybind11 example plugin"; // optional module docstring
    m.def("add", &add, "A function which adds two numbers");
}

然后编译为动态链接库:

1
c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

在Python中调用:

1
2
3
import example
print(example.add(1, 2))
# >>> 3L
本文由作者按照 CC BY 4.0 进行授权