在用pybind11绑定C++函数时,使用Python ctypes的 Array和 c_int作为参数
我有来自设备的数据,直接在Python中捕获,作为 ctypes.c_uint16 的一个 ctypes.Array。我正在使用PyTorch,在完成数据捕获后的若干后处理后创建一个新的Tensor。我计划把这部分用C/C++实现,以显著提升在Python中运行缓慢的众多迭代任务的速度,然后再用 pybind11 来创建绑定。
在C++中的函数签名看起来是这样的:
torch::Tensor foo(uint16_t* a, int b, int c, int d, uint64_t e, uint64_t* f);
使用pybind模块调用如下:
// Create python bindings
namespace py = pybind11;
PYBIND11_MODULE(foo_lib, m){
m.def("foo", &foo,
py::arg("a"),
py::arg("b"),
py::arg("c"),
py::arg("d"),
py::arg("e"),
py::arg("f")
);
}
其中 a 是一个C 风格的数组,包含 uint16_t、b、c 和 d 等信息(如数组长度),而 f 是一个C 风格的数组,包含在处理过程中所需的一些元数据 uint64_t。我已经实现并测试过一些用于对数据进行清理的C 函数,它们可以工作,因此在调用它们时,我更愿意在C++函数中使用这些数据类型。
我为这个函数用名为 foo_lib 的pybind模块创建了绑定,签名为 foo,并且可以导入到Python,但在传入以下类型:
# test.py #
import torch
import foo_lib
from ctypes import Array, c_int, c_uint16, c_uint64
a: Array[c_uint16] = ... # The array of input data
b: c_int = c_int(0)
c: c_int = c_int(13)
d: c_int = c_int(16)
e: c_uint64 = c_uint64(1000)
f: Array[c_uint64] = ... # An array of some required metadata
# Process the output
output: torch.Tensor = foo_lib.foo(a, b, c, d, e, f)
我得到如下输出:
Traceback (most recent call last):
File "~/test.py", line 14, in <module>
output: torch.Tensor = foo_lib.foo(a, b, c, d, e, f)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: foo(): incompatible function arguments. The following argument types are supported:
1. (a: int, b: int, c: int, d: int, e: int, f: int) -> torch.Tensor
Invoked with: <__main__.c_ushort_Array_1000 object at 0x7ad66ff620d0>, c_int(0), c_int(13), c_int(16), c_ulong(1000), <__main__.c_ulong_Array_10 object at 0x7ad66ff62250>
看起来pybind把所有ctype的参数都转换成了普通的Python int。
有没有办法让pybind将签名中的实际C/C++类型转换为其在Python端对应的ctypes类类型?把数据从Python转换成除了ctypes Arrays 之外的其他格式(比如 numpy.ndarray)至少会在某种程度上抵消使用这些C++绑定来加速数据处理的初衷,因此我在想是否可以有一种方式来配置pybind,使其按原样转换这些类型。我如果能够的话会用纯C 编写绑定并使用ctypes cdll.LoadLibrary,但我需要访问到 torch::Tensor 对象,所以只能使用C++。
我在Ubuntu 24.04.4上使用Python 3.12.3,配合 torch 2.7.1和 pybind11 2.11.2。
解决方案
no, do not bind the raw-pointer function directly and expect pybind11 to understand ctypes.Array; expose a wrapper accepting py::buffer or py::array_t<T> and pass plain Python integers for scalar arguments