我有两个C++库,它们分别使用pybind11和cython框架,暴露Python API。我需要使用Python胶囊对象在它们之间传递对象(双向)。由于cython和pybind11使用Python胶囊对象的方式不同,这样做是否可行?
库A定义了一个类Foo,并使用pybind11将其暴露给Python。库B使用cython暴露其API。LibB拥有shared_ptr,它是LibB中一个类Bar的成员。
Bar将shared_ptr成员作为PyCapsule返回,我在Foo类的pybind11中捕获它。我从胶囊中解包shared_ptr,将其返回给Python,并且用户可以在Python中使用pybind11绑定的Foo操作此对象。
接下来,我需要将其放回到pybind11的胶囊中并返回给Bar。
Bar的Python API操作PyObject和PyCapsule,因为cython允许这样做。而pybind11和Foo的API不接受这些类型,我被迫使用pybind11::object和pybind11::capsule。
一切都运行良好,直到我尝试在需要PyCapsule*的Bar类的cython方法中使用pybind11::capsule时,pybind11::capsule中的shared_ptr被破坏并导致应用程序崩溃。
有人尝试过使这两个库相互通信吗?
库A -> 类Foo
库A定义了一个类Foo,并使用pybind11将其暴露给Python。库B使用cython暴露其API。LibB拥有shared_ptr,它是LibB中一个类Bar的成员。
Bar将shared_ptr成员作为PyCapsule返回,我在Foo类的pybind11中捕获它。我从胶囊中解包shared_ptr,将其返回给Python,并且用户可以在Python中使用pybind11绑定的Foo操作此对象。
接下来,我需要将其放回到pybind11的胶囊中并返回给Bar。
Bar的Python API操作PyObject和PyCapsule,因为cython允许这样做。而pybind11和Foo的API不接受这些类型,我被迫使用pybind11::object和pybind11::capsule。
一切都运行良好,直到我尝试在需要PyCapsule*的Bar类的cython方法中使用pybind11::capsule时,pybind11::capsule中的shared_ptr被破坏并导致应用程序崩溃。
有人尝试过使这两个库相互通信吗?
库A -> 类Foo
namespace foo{
class Foo {
public:
void foo() {...}
}
}
libB -> 类 Bar
namespace bar {
class Bar {
public:
PyObject* get_foo() {
const char * capsule_name = "foo_in_capsule";
return PyCapsule_New(&m_foo, capsule_name, nullptr);
}
static Bar fooToBar(PyObject * capsule) {
void * foo_ptr = PyCapsule_GetPointer(capsule, "foo_in_capsule");
auto foo = static_cast<std::shared_ptr<foo::Foo>*>(foo_ptr);
// here the shared_ptr is corrupted (garbage numbers returned for use_count() and get() )
std::cout << "checking the capsule: " << foo->use_count() << " " << foo->get() << std::endl
Bar b;
b.m_foo = *foo; //this is what I would like to get
return b;
}
std::shared_ptr<Foo> m_foo;
};
}
Pybind11适用于Foo
void regclass_foo_Foo(py::module m)
{
py::class_<foo::Foo, std::shared_ptr<foo::Foo>> foo(m, "Foo");
foo.def("foo", &foo::Foo::foo);
foo.def_static("from_capsule", [](py::object* capsule) {
auto* pycapsule_ptr = capsule->ptr();
auto* foo_ptr = reinterpret_cast<std::shared_ptr<foo::Foo>*>(PyCapsule_GetPointer(pycapsule_ptr, "foo_in_capsule"));
return *foo_ptr;
});
foo.def_static("to_capsule", [](std::shared_ptr<foo::Foo>& foo_from_python) {
auto pybind_capsule = py::capsule(&foo_from_python, "foo_in_capsule", nullptr);
return pybind_capsule;
});
}
为Bar使用Cython
cdef extern from "bar.hpp" namespace "bar":
cdef cppclass Bar:
object get_foo() except +
def foo_to_bar(capsule):
b = C.fooToBar(capsule)
return b
将所有东西整合在Python中
from bar import Bar, foo_to_bar
from foo import Foo
bar = Bar(... some arguments ...)
capsule1 = bar.get_foo()
foo_from_capsule = Foo.from_capsule(capsule1)
// this is the important part - need to operate on foo using its python api
print("checking if foo works", foo_from_capsule.foo())
// and use it to create another bar object with a (possibly) modified foo object
capsule2 = Foo.to_capsule(foo_from_capsule)
bar2 = foo_to_bar(capsule2)
PyCapsule
不是正确的方法 - 它被设计用于保存指针而不是共享指针,因此我猜测您正在从相同的C指针初始化两个shared_ptr
。在Cython中,您可以将一个类设置为public
,这将提供一个包含底层C结构的.h文件。那似乎是我开始的地方... - DavidWstatic Bar fooToBar(PyObject * capsule)
方法卡住了,因为我在运行时遇到了段错误。 - tomdol