我想知道,在python中,是否有办法将 "downcasted "对象作为函数的结果,使用 boost::python
,同时使用共享指针。考虑以下场景。
class A {
virtual ~A() { }
};
class B: public A {
std::shared_ptr<B> makeB() { return std::make_shared<B>(); }
std::shared_ptr<A> f(bool b) { return b ? std::make_shared<B>() : std::make_shared<A>();
};
BOOST_PYTHON_MODULE(module) {
using namespace boost::python;
register_ptr_to_python<std::shared_ptr<A>>();
register_ptr_to_python<std::shared_ptr<B>>();
class_<A, std::shared_ptr<A>, boost::noncopyable>("A", no_init);
class_<B, bases<A>, boost::noncopyable>("B", no_init)
.def("f", &B::f)
.def("makeB", &B::makeB).staticmethod("makeB");
}
现在在python中,我可以检索一个 B
对象并调用 f
但所产生的对象将被看作是一个 A
对象。
import module
b = module.B.makeB()
type(b) # This is module.B
u = b.f(true)
type(u) # This is always module.A
无论如何动态地返回一个类型的对象。B
在python中,我必须手动下传,这没关系,因为在C++中很常见,但在python中,我希望能有动态的返回类型。在C++中,我必须手动downcast,这是好的,因为它在C++中很常见,但在python中,我希望能有一个动态的返回类型。
所以我设法用一个 结果转换器:
template <class FromType, class ToType>
struct DowncastReturn {
template <class T>
struct apply;
template <>
struct apply<std::shared_ptr<FromType>> {
struct type {
bool convertible() const { return true; }
inline PyObject* operator()(std::shared_ptr<FromType> p) const {
if (p == nullptr) {
return bpy::detail::none();
}
else {
auto downcast_p = std::dynamic_pointer_cast<ToType>(p);
bpy::object p_value = downcast_p == nullptr ? bpy::object{ p } : bpy::object{ downcast_p };
return bpy::incref(p_value.ptr());
}
}
inline PyTypeObject const* get_pytype() const {
return bpy::converter::registered_pytype<FromType>::get_pytype();
}
};
};
};
那么就可以使用这个。
class_<B, bases<A>, boost::noncopyable>("B", no_init)
.def("f", &B::f, return_value_policy<DowncastReturn<A, B>>())
.def("makeB", &B::makeB).staticmethod("makeB");
这比我的第一个版本需要更多的设置 但如果多个方法都需要这个的话,就更容易使用了。
这是我的第一个版本的答案,我没有依靠一个新的系统,而是手动完成了下传。return_value_policy
. 这个想法很简单,就是把返回的对象手动包装成一个叫做 boost::python::object
:
class_<B, bases<A>, boost::noncopyable>("B", no_init)
.def("f", +[](B *self, bool b) {
auto *r = self->f();
auto *rb = dynamic_cast<B*>(r);
return rb == nullptr ? object{ r } : object{ rb };
})
.def("makeB", &B::makeB).staticmethod("makeB");