使用 Python、C++ 和 pybind11 返回和传递原始 POD 指针(数组)
Returning and passing around raw POD pointers (arrays) with Python, C++, and pybind11
我有一个 C++ 函数,它 returns 一个原始 float
指针,另一个 C++ 函数接受一个原始 float
指针作为参数。类似于:
float* ptr = something;
float* get_ptr(void) { return ptr; }
void use_ptr(float* ptr) { do_work(ptr); }
我希望能够使用 Python 传递指针。像这样:
import my_native_functions as native
ptr = native.get_ptr()
native.use_ptr(ptr)
我正在使用 pybind11 创建我的原生 python 模块,但我不知道如何为 get_ptr()
函数创建绑定。如果我只是执行以下操作:
PYBIND11_MODULE(my_native_functions, m)
{
m.def("get_ptr", &get_ptr);
m.def("use_ptr", &use_ptr);
}
get_ptr()
函数returns一个PythonFloat
对象。我想这是有道理的,因为 python 中没有指针类型。但是,因为现在这是一个简单的 Float
,当我调用 use_ptr()
函数并迭代 C/C++ 中的指针时,只有数组的第一个元素是正确的。其余的都是垃圾。为了解决这个问题,在 C++ 中,我必须转换我的指针 to/from std::size_t
。通过这样做,一切正常。
但是,我想问一下:有没有"right way"不用用pybind11强制转换to/fromstd::size_t
就可以达到上面的效果?
如果你好奇我为什么这样做:
我明白我所做的不是类型安全的。此外,我从不触摸 Python 一侧的 pointer/integer 。我只是从一个本机模块中检索它并将其传递给另一个。此外,我不能将指针转换为某种 numpy 视图,因为指针并不总是在 CPU 上。有时我想传递 CUDA 指针。从 CUDA 指针创建 py::array_t
是不可能的,除非我复制数据(我不想这样做)。
谢谢。
按照 here 所述,将原始指针包装在自定义 "smart" 指针 class 中(只是假装真的很聪明)。您可以在此 class 中添加一些附加信息,例如数组元素的大小和元素的数量。这将使它成为 C++ 端的通用数组描述符(但不是 Python 端,因为您没有将原始指针公开给 Python)。
对于更简单的选项,只需将您的指针包裹在任何旧的 class 中,以便在 Python 中隐藏它。无需将其作为自定义智能指针公开给 Python。这是一个这样做的例子:
#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>
namespace py = pybind11;
template <class T> class ptr_wrapper
{
public:
ptr_wrapper() : ptr(nullptr) {}
ptr_wrapper(T* ptr) : ptr(ptr) {}
ptr_wrapper(const ptr_wrapper& other) : ptr(other.ptr) {}
T& operator* () const { return *ptr; }
T* operator->() const { return ptr; }
T* get() const { return ptr; }
void destroy() { delete ptr; }
T& operator[](std::size_t idx) const { return ptr[idx]; }
private:
T* ptr;
};
float array[3] = { 3.14, 2.18, -1 };
ptr_wrapper<float> get_ptr(void) { return array; }
void use_ptr(ptr_wrapper<float> ptr) {
for (int i = 0; i < 3; ++i)
std::cout << ptr[i] << " ";
std::cout << "\n";
}
PYBIND11_MODULE(Ptr,m)
{
py::class_<ptr_wrapper<float>>(m,"pfloat");
m.def("get_ptr", &get_ptr);
m.def("use_ptr", &use_ptr);
}
我有一个 C++ 函数,它 returns 一个原始 float
指针,另一个 C++ 函数接受一个原始 float
指针作为参数。类似于:
float* ptr = something;
float* get_ptr(void) { return ptr; }
void use_ptr(float* ptr) { do_work(ptr); }
我希望能够使用 Python 传递指针。像这样:
import my_native_functions as native
ptr = native.get_ptr()
native.use_ptr(ptr)
我正在使用 pybind11 创建我的原生 python 模块,但我不知道如何为 get_ptr()
函数创建绑定。如果我只是执行以下操作:
PYBIND11_MODULE(my_native_functions, m)
{
m.def("get_ptr", &get_ptr);
m.def("use_ptr", &use_ptr);
}
get_ptr()
函数returns一个PythonFloat
对象。我想这是有道理的,因为 python 中没有指针类型。但是,因为现在这是一个简单的 Float
,当我调用 use_ptr()
函数并迭代 C/C++ 中的指针时,只有数组的第一个元素是正确的。其余的都是垃圾。为了解决这个问题,在 C++ 中,我必须转换我的指针 to/from std::size_t
。通过这样做,一切正常。
但是,我想问一下:有没有"right way"不用用pybind11强制转换to/fromstd::size_t
就可以达到上面的效果?
如果你好奇我为什么这样做:
我明白我所做的不是类型安全的。此外,我从不触摸 Python 一侧的 pointer/integer 。我只是从一个本机模块中检索它并将其传递给另一个。此外,我不能将指针转换为某种 numpy 视图,因为指针并不总是在 CPU 上。有时我想传递 CUDA 指针。从 CUDA 指针创建 py::array_t
是不可能的,除非我复制数据(我不想这样做)。
谢谢。
按照 here 所述,将原始指针包装在自定义 "smart" 指针 class 中(只是假装真的很聪明)。您可以在此 class 中添加一些附加信息,例如数组元素的大小和元素的数量。这将使它成为 C++ 端的通用数组描述符(但不是 Python 端,因为您没有将原始指针公开给 Python)。
对于更简单的选项,只需将您的指针包裹在任何旧的 class 中,以便在 Python 中隐藏它。无需将其作为自定义智能指针公开给 Python。这是一个这样做的例子:
#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>
namespace py = pybind11;
template <class T> class ptr_wrapper
{
public:
ptr_wrapper() : ptr(nullptr) {}
ptr_wrapper(T* ptr) : ptr(ptr) {}
ptr_wrapper(const ptr_wrapper& other) : ptr(other.ptr) {}
T& operator* () const { return *ptr; }
T* operator->() const { return ptr; }
T* get() const { return ptr; }
void destroy() { delete ptr; }
T& operator[](std::size_t idx) const { return ptr[idx]; }
private:
T* ptr;
};
float array[3] = { 3.14, 2.18, -1 };
ptr_wrapper<float> get_ptr(void) { return array; }
void use_ptr(ptr_wrapper<float> ptr) {
for (int i = 0; i < 3; ++i)
std::cout << ptr[i] << " ";
std::cout << "\n";
}
PYBIND11_MODULE(Ptr,m)
{
py::class_<ptr_wrapper<float>>(m,"pfloat");
m.def("get_ptr", &get_ptr);
m.def("use_ptr", &use_ptr);
}