在 pybind11 中是否可以使用 py::cast 访问抽象基础 class?
Is it possible in pybind11 to use py::cast to access an abstract base class?
我在下面包含了一个最小的工作示例 - 它可以使用典型的 pybind11 指令(我使用 cmake)进行编译。
我有一个抽象基础class,Abstract
,它是纯虚拟的。我可以使用 "trampoline class" 轻松地将它包装在 pybind11 中(这在 pybind11 中有详细记录)。
此外,我有一个 Abstract
、ToBeWrapped
的具体实现,它也是使用 pybind11 包装的。
我的问题是我有一些客户端代码接受任意 PyObject*
(或者,在这个例子中,pybind11 的包装器 py::object
)并希望将其转换为 Abstract*
.
但是,如我的示例所示,我无法将 py::object
转换为 Abstract*
。
我可以毫无问题地转换为 ToBeWrapped*
,然后将其存储为 Abstract*', however this would require my client code to know ahead of time what kind of
Abstract*` python 解释器正在发送,这违背了抽象基础 class.
TL;DR
是否可以修改此代码,使客户端 accessMethod
能够任意处理从 python 解释器传递的 Abstract*
?
#include <pybind11/pybind11.h>
#include <iostream>
namespace py = pybind11;
// abstract base class - cannot be instantiated on its own
class Abstract
{
public:
virtual ~Abstract() = 0;
virtual std::string print() const = 0;
};
Abstract::~Abstract(){}
// concrete implementation of Abstract
class ToBeWrapped : public Abstract
{
public:
ToBeWrapped(const std::string& msg = "heh?")
: myMessage(msg){};
std::string print() const override
{
return myMessage;
}
private:
const std::string myMessage;
};
// We need a trampoline class in order to wrap this with pybind11
class AbstractPy : public Abstract
{
public:
using Abstract::Abstract;
std::string print() const override
{
PYBIND11_OVERLOAD_PURE(
std::string, // return type
Abstract, // parent class
print, // name of the function
// arguments (if any)
);
}
};
// I have client code that accepts a raw PyObject* - this client code base implements its
// own python interpreter, and calls this "accessMethod" expecting to convert the python
// object to its c++ type.
//
// Rather than mocking up the raw PyObject* method (which would be trivial) I elected to
// keep this minimal example 100% pybind11
void accessMethod(py::object obj)
{
// runtime error: py::cast_error
//Abstract* casted = obj.cast<Abstract*>();
// this works
Abstract* casted = obj.cast<ToBeWrapped*>();
}
PYBIND11_MODULE(PyMod, m)
{
m.doc() = R"pbdoc(
This is a python module
)pbdoc";
py::class_<Abstract, AbstractPy>(m, "Abstract")
.def("print", &Abstract::print)
;
py::class_<ToBeWrapped>(m, "WrappedClass")
.def(py::init<const std::string&>())
;
m.def("access", &accessMethod, "This method will attempt to access the wrapped type");
}
需要声明层级关系,所以:
py::class_<ToBeWrapped>(m, "WrappedClass")
应该是:
py::class_<ToBeWrapped, Abstract>(m, "WrappedClass")
我在下面包含了一个最小的工作示例 - 它可以使用典型的 pybind11 指令(我使用 cmake)进行编译。
我有一个抽象基础class,Abstract
,它是纯虚拟的。我可以使用 "trampoline class" 轻松地将它包装在 pybind11 中(这在 pybind11 中有详细记录)。
此外,我有一个 Abstract
、ToBeWrapped
的具体实现,它也是使用 pybind11 包装的。
我的问题是我有一些客户端代码接受任意 PyObject*
(或者,在这个例子中,pybind11 的包装器 py::object
)并希望将其转换为 Abstract*
.
但是,如我的示例所示,我无法将 py::object
转换为 Abstract*
。
我可以毫无问题地转换为 ToBeWrapped*
,然后将其存储为 Abstract*', however this would require my client code to know ahead of time what kind of
Abstract*` python 解释器正在发送,这违背了抽象基础 class.
TL;DR
是否可以修改此代码,使客户端 accessMethod
能够任意处理从 python 解释器传递的 Abstract*
?
#include <pybind11/pybind11.h>
#include <iostream>
namespace py = pybind11;
// abstract base class - cannot be instantiated on its own
class Abstract
{
public:
virtual ~Abstract() = 0;
virtual std::string print() const = 0;
};
Abstract::~Abstract(){}
// concrete implementation of Abstract
class ToBeWrapped : public Abstract
{
public:
ToBeWrapped(const std::string& msg = "heh?")
: myMessage(msg){};
std::string print() const override
{
return myMessage;
}
private:
const std::string myMessage;
};
// We need a trampoline class in order to wrap this with pybind11
class AbstractPy : public Abstract
{
public:
using Abstract::Abstract;
std::string print() const override
{
PYBIND11_OVERLOAD_PURE(
std::string, // return type
Abstract, // parent class
print, // name of the function
// arguments (if any)
);
}
};
// I have client code that accepts a raw PyObject* - this client code base implements its
// own python interpreter, and calls this "accessMethod" expecting to convert the python
// object to its c++ type.
//
// Rather than mocking up the raw PyObject* method (which would be trivial) I elected to
// keep this minimal example 100% pybind11
void accessMethod(py::object obj)
{
// runtime error: py::cast_error
//Abstract* casted = obj.cast<Abstract*>();
// this works
Abstract* casted = obj.cast<ToBeWrapped*>();
}
PYBIND11_MODULE(PyMod, m)
{
m.doc() = R"pbdoc(
This is a python module
)pbdoc";
py::class_<Abstract, AbstractPy>(m, "Abstract")
.def("print", &Abstract::print)
;
py::class_<ToBeWrapped>(m, "WrappedClass")
.def(py::init<const std::string&>())
;
m.def("access", &accessMethod, "This method will attempt to access the wrapped type");
}
需要声明层级关系,所以:
py::class_<ToBeWrapped>(m, "WrappedClass")
应该是:
py::class_<ToBeWrapped, Abstract>(m, "WrappedClass")