【问题标题】:Calling member function through a pointer from Python with pybind11使用 pybind11 通过 Python 中的指针调用成员函数
【发布时间】:2022-01-30 02:27:17
【问题描述】:

我在 pybind11's tutorial on trampolines 之后创建一个 Python 模块 (module.so):

// 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 sn-p 总是给出分段错误。我怀疑 GIL 是罪魁祸首,因为嵌入了解释器来运行 Python 代码,但我不确定出了什么问题。

(我读过passing pointer to C++ from python using pybind11,但对我来说似乎是另一个问题,因为那里的指针是double *,所以没有类成员)

还有我的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})

【问题讨论】:

    标签: segmentation-fault pybind11


    【解决方案1】:

    接收原始指针通常* 意味着您不承担对象的所有权。当您在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 中以接收阅读器的所有权。

    * C++ 核心指南:Never transfer ownership by a raw pointer or reference.

    【讨论】:

    • 是的,谢谢。
    猜你喜欢
    • 2010-12-01
    • 2015-05-06
    • 2012-08-24
    相关资源
    最近更新 更多