使用 pybind11 通过来自 Python 的指针调用成员函数
Calling member function through a pointer from Python with pybind11
我正在创建一个 Python 模块 (module.so
),紧随 pybind11's tutorial on trampolines:
// module.cpp
#include <string>
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
namespace py = pybind11;
class IReader
{
public:
virtual std::vector<int> read(const std::string &path) = 0;
};
class PyIReader : public IReader
{
using IReader::IReader;
std::vector<int> read(const std::string &path) override
{
PYBIND11_OVERRIDE_PURE(std::vector<int>, IReader, read, path);
}
};
class C
{
public:
C(IReader *reader) : reader_(reader) {}
std::vector<int> workOn(const std::string &path) { return reader_->read(path); }
private:
IReader *reader_;
};
PYBIND11_MODULE(module, m)
{
py::class_<IReader, PyIReader>(m, "IReader").def(py::init<>()).def("read", &IReader::read);
// I need call_guard to avoid deadlocking in my real code;
// but removing it doesn't help
py::class_<C>(m, "C").def(py::init<IReader *>()).def("work_on", &C::workOn, py::call_guard<py::gil_scoped_release>());
}
和
# main.py
import pickle
from typing import *
from module import IReader, C
class PklReader(IReader):
def read(self, path: str) -> List[int]:
with open(path, "rb") as f:
return pickle.load(f)
if __name__ == "__main__":
c = C(PklReader())
print(c.work_on("a.pkl")) # a = [1, 2, 3]
Python 片段总是给出分段错误。我怀疑 GIL 是罪魁祸首,因为解释器嵌入到 运行 Python 代码中,但我不确定出了什么问题。
(我读过 但对我来说这似乎是另一个问题,因为那里的指针是 double *
所以没有 class 成员)
还有我的CMakelists.txt
如果有帮助的话:
cmake_minimum_required(VERSION 3.16)
project(CLASS_POINTER)
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-O3 -fopenmp -fPIC)
add_link_options(-fopenmp)
find_package(Python 3 REQUIRED COMPONENTS Development NumPy)
add_subdirectory(pybind11)
pybind11_add_module(module module.cpp)
set_target_properties(module PROPERTIES CXX_STANDARD_REQUIRED ON)
target_include_directories(module PRIVATE ${pybind11_INCLUDE_DIRS} ${Python_INCLUDE_DIRS} ${Python_NumPy_INCLUDE_DIRS})
target_link_directories(module PRIVATE ${Python_LIBRARY_DIRS})
target_link_libraries(module PRIVATE ${pybind11_LIBRARIES} ${Python_LIBRARIES})
通常接收原始指针* 意味着您不承担该对象的所有权。当您在 C
的构造函数中收到 IReader*
时,pybind11 假定您仍将持有临时 PklReader()
对象并使其在外部保持活动状态。但是你没有,所以它被释放了,你得到了一个段错误。
我觉得
if __name__ == "__main__":
pr = PklReader()
c = C(pkr)
print(c.work_on("a.pkl")) # a = [1, 2, 3]
应该可以工作,因为其他一切都是正确的。
您还可以接收 shared_ptr
并将其存储在 C
中以获得 reader 的所有权。
* C++ 核心指南:Never transfer ownership by a raw pointer or reference.
我正在创建一个 Python 模块 (module.so
),紧随 pybind11's tutorial on trampolines:
// module.cpp
#include <string>
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
namespace py = pybind11;
class IReader
{
public:
virtual std::vector<int> read(const std::string &path) = 0;
};
class PyIReader : public IReader
{
using IReader::IReader;
std::vector<int> read(const std::string &path) override
{
PYBIND11_OVERRIDE_PURE(std::vector<int>, IReader, read, path);
}
};
class C
{
public:
C(IReader *reader) : reader_(reader) {}
std::vector<int> workOn(const std::string &path) { return reader_->read(path); }
private:
IReader *reader_;
};
PYBIND11_MODULE(module, m)
{
py::class_<IReader, PyIReader>(m, "IReader").def(py::init<>()).def("read", &IReader::read);
// I need call_guard to avoid deadlocking in my real code;
// but removing it doesn't help
py::class_<C>(m, "C").def(py::init<IReader *>()).def("work_on", &C::workOn, py::call_guard<py::gil_scoped_release>());
}
和
# main.py
import pickle
from typing import *
from module import IReader, C
class PklReader(IReader):
def read(self, path: str) -> List[int]:
with open(path, "rb") as f:
return pickle.load(f)
if __name__ == "__main__":
c = C(PklReader())
print(c.work_on("a.pkl")) # a = [1, 2, 3]
Python 片段总是给出分段错误。我怀疑 GIL 是罪魁祸首,因为解释器嵌入到 运行 Python 代码中,但我不确定出了什么问题。
(我读过 double *
所以没有 class 成员)
还有我的CMakelists.txt
如果有帮助的话:
cmake_minimum_required(VERSION 3.16)
project(CLASS_POINTER)
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-O3 -fopenmp -fPIC)
add_link_options(-fopenmp)
find_package(Python 3 REQUIRED COMPONENTS Development NumPy)
add_subdirectory(pybind11)
pybind11_add_module(module module.cpp)
set_target_properties(module PROPERTIES CXX_STANDARD_REQUIRED ON)
target_include_directories(module PRIVATE ${pybind11_INCLUDE_DIRS} ${Python_INCLUDE_DIRS} ${Python_NumPy_INCLUDE_DIRS})
target_link_directories(module PRIVATE ${Python_LIBRARY_DIRS})
target_link_libraries(module PRIVATE ${pybind11_LIBRARIES} ${Python_LIBRARIES})
通常接收原始指针* 意味着您不承担该对象的所有权。当您在 C
的构造函数中收到 IReader*
时,pybind11 假定您仍将持有临时 PklReader()
对象并使其在外部保持活动状态。但是你没有,所以它被释放了,你得到了一个段错误。
我觉得
if __name__ == "__main__":
pr = PklReader()
c = C(pkr)
print(c.work_on("a.pkl")) # a = [1, 2, 3]
应该可以工作,因为其他一切都是正确的。
您还可以接收 shared_ptr
并将其存储在 C
中以获得 reader 的所有权。
* C++ 核心指南:Never transfer ownership by a raw pointer or reference.