如何在C++与Python间传递兼容主机/设备内存的指针?
问题:C++通过Python C API调用支持CPU/GPU的
apply_filter函数的指针转换方案 我有一个名为apply_filter的Python函数,可基于CPU(使用NumPy)或GPU(使用CuPy)执行,该函数接收一个输入缓冲区对象,代表指向系统内存或GPU全局设备内存的指针。我希望通过Python C API从C代码中调用该函数,但不清楚如何在C侧构造对应的输入缓冲区对象(对应原始指针),也不知道如何完成双向的指针与PyObject转换。以下是简化代码:
C++调用代码(待补全)
#include <Python.h> void PythonObjectWrapper::applyFilter(float* image, std::array<int, 3> dim) { PyObject* python_method = PyObject_GetAttrString(class_object_, method_name_); PyObject* py_image = ??? // convert C-array to PyObject PyObject* method_args = PyTuple_New(2); PyTuple_SetItem(method_args, 0, py_image); PyTuple_SetItem(method_args, 1, ...); // transfer dim PyObject* py_filtered_image = PyObject_CallObject(python_method, method_args); float* filtered_image = ??? // convert PyObject to C-array }
Python被调用函数(待补全)
class Filter: def __init__(self, gpu): self.gpu_ = gpu def apply_filter(self, image_ptr, dim): image_array = ??? // convert image_ptr PyObject to NumPy / CuPy array apply_filter_(image_array) filtered_image_ptr = ??? // convert image_array to ptr return filtered_image_ptr
需求:方案需避免不必要的数据拷贝(尤其主机与设备间的拷贝),高效且稳定支持CPU/GPU两种运行模式。
解决方案
1. C++侧:将C数组指针转为PyObject(py_image = ???)
假设PythonObjectWrapper包含is_gpu_成员变量标记运行模式,通过NumPy/CuPy的C API直接包装指针,不拷贝数据:
// 先处理维度转换为Python元组(对应代码中...的部分) PyObject* py_dim = PyTuple_New(3); PyTuple_SetItem(py_dim, 0, PyLong_FromLong(dim[0])); PyTuple_SetItem(py_dim, 1, PyLong_FromLong(dim[1])); PyTuple_SetItem(py_dim, 2, PyLong_FromLong(dim[2])); // 处理image指针转为PyObject PyObject* py_image = nullptr; if (is_gpu_) { // GPU模式:包装CUDA设备指针为CuPy数组 PyObject* cupy_module = PyImport_ImportModule("cupy"); PyObject* cupy_ndarray = PyObject_GetAttrString(cupy_module, "ndarray"); // 构造参数:shape、dtype、设备指针 PyObject* shape_args = PyTuple_New(1); PyTuple_SetItem(shape_args, 0, py_dim); PyObject* dtype_obj = PyObject_GetAttrString(PyImport_ImportModule("numpy"), "float32"); PyObject* memptr_obj = PyLong_FromVoidPtr(image); py_image = PyObject_CallFunctionObjArgs(cupy_ndarray, shape_args, dtype_obj, memptr_obj, NULL); // 释放临时对象 Py_DECREF(shape_args); Py_DECREF(dtype_obj); Py_DECREF(memptr_obj); Py_DECREF(cupy_ndarray); Py_DECREF(cupy_module); } else { // CPU模式:包装主机指针为NumPy数组 import_array(); // 必须初始化NumPy C API npy_intp shape[3] = {dim[0], dim[1], dim[2]}; PyArrayObject* np_array = (PyArrayObject*)PyArray_SimpleNewFromData( 3, shape, NPY_FLOAT, (void*)image ); // 设置数组不拥有内存,避免Python侧释放C++分配的内存 PyArray_SetBaseObject(np_array, NULL); py_image = (PyObject*)np_array; }
2. C++侧:将返回的PyObject转为C数组指针(filtered_image = ???)
根据运行模式提取底层指针:
float* filtered_image = nullptr; if (is_gpu_) { // 从CuPy数组提取CUDA设备指针 PyObject* memptr_attr = PyObject_GetAttrString(py_filtered_image, "data"); if (memptr_attr) { PyObject* memptr_obj = PyObject_GetAttrString(memptr_attr, "memptr"); filtered_image = (float*)PyLong_AsVoidPtr(memptr_obj); Py_DECREF(memptr_obj); Py_DECREF(memptr_attr); } } else { // 从NumPy数组提取主机指针 if (PyArray_Check(py_filtered_image)) { filtered_image = (float*)PyArray_DATA((PyArrayObject*)py_filtered_image); } }
3. Python侧:将PyObject转为NumPy/CuPy数组(image_array = ???)
基于self.gpu_标记,直接将指针包装为数组,无数据拷贝:
import numpy as np import cupy as cp from ctypes import c_void_p class Filter: def __init__(self, gpu): self.gpu_ = gpu def apply_filter(self, image_ptr, dim): if self.gpu_: # GPU模式:将设备指针转为CuPy数组 image_array = cp.ndarray(shape=dim, dtype=np.float32, memptr=image_ptr) else: # CPU模式:将主机指针转为NumPy数组 image_array = np.ndarray(shape=dim, dtype=np.float32, buffer=c_void_p(image_ptr)) apply_filter_(image_array) # ...后续处理
4. Python侧:将NumPy/CuPy数组转为指针返回(filtered_image_ptr = ???)
提取数组底层指针并转为Python整数返回:
def apply_filter(self, image_ptr, dim): # ...前面的代码 apply_filter_(image_array) if self.gpu_: # 提取CuPy设备指针 filtered_image_ptr = image_array.data.memptr else: # 提取NumPy主机指针 filtered_image_ptr = image_array.ctypes.data_as(c_void_p).value return filtered_image_ptr
关键注意事项
- 内存所有权:必须明确内存由C还是Python侧管理。若C分配内存,Python侧需设置数组不拥有内存;若Python侧分配内存,C++侧需通过Python API释放内存。
- 错误处理:实际代码需添加
PyErr_Occurred()检查,避免内存泄漏或程序崩溃。 - 类型匹配:确保指针类型与数组
dtype严格匹配(此处均为float32),避免类型错误。
内容的提问来源于stack exchange,提问作者Amit
相关产品推荐
相关产品推荐

