Boost.PythonPython中的C++对象继承:不能添加到C++基类LIS

2024-06-16 11:24:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图通过继承扩展Python中现有的C++对象。 我可以成功地做到这一点,并运行Python中重写的虚拟方法。然而,当我尝试将Python对象添加到C++基础对象类型的指针列表中时(Python类已经重写的基础对象),我会得到一个类型错误:“尝试追加一个无效类型”

我确信这个错误是由于没有从deriver*到base*的begin“隐式\u convertible”功能造成的。在C++中,这将被定义为:SimulyLyTyl& Lt[[派生-Fas-Base] ] ,基< EEM> Gt;;())。 可以用python定义它吗?在

我怎样才能做到这一点?在

下面是复制此行为的示例代码。在

C++ +<

struct Base {
    virtual ~Base() {}
    virtual int f() = 0;
};
struct A {
    std::vector<Base*>& GetBaseList() { return m_base_List; }
    std::vector<Base*> m_base_List;
};
struct BaseWrap : Base, wrapper<Base> {
    int f() { return this->get_override("f")(); }
};

BOOST_PYTHON_MODULE(sandbox)
{
    class_<BaseWrap, Base*, boost::noncopyable>("Base", no_init)
        .def("f", pure_virtual(&Base::f));

    class_<A, A*>("A", init<>())
        .add_property("baseList", make_function(&A::GetBaseList, return_internal_reference<>()));

    //implicitly_convertible<[Derived_from_base]*,Base*>();
    class_<std::vector<Base*>>("BaseList").def(vector_indexing_suite<std::vector<Base*>>());
}

Python 从sandbox导入*

^{pr2}$

任何帮助或想法将不胜感激。在


Tags: 对象类型basereturn定义错误virtual基础
1条回答
网友
1楼 · 发布于 2024-06-16 11:24:20

BaseList.append()函数接收一个右类型的参数;但是,该参数具有不适当的。在Python中,derived初始值设定项没有初始化其层次结构的sandbox.Base部分。这会导致Boost.Python对象,不包含C++ +{< CD4}}对象。因此,当{{CD1}}试图提取C++ ^ {< CD4}}对象时,它失败并引发错误。在

class derived(Base):
    def __init__(self):
        self.name = "test"
        # Base is not initialized.
    def f(self):
        print("Hello Derived!")

d = derived()
d.f() # `derived.f()` is resolved through Python's method-resolution-order.  
      # It is not invoking `BaseWrap::f()`.

a = A()
a.baseList.append(d) # d does not contain a BaseWrap object, so this throws.

要解决此问题,请在derived.__init__()中显式调用Base.__init__()

^{pr2}$

但是,尝试这样做会暴露BaseWrap的其他问题:

  • sandbox.Base类必须可以从Python构造,因此绑定不能提供boost::python::no_init作为其初始值设定项规范。通常,当C++对象被从C++显式实例化并传递到Python时,只需要使用^ {CD11}},例如通过工厂函数。在
  • TBaseWrap时,Base*HeldType不能满足{}的要求。尤其是,HeldType必须是:BaseWrap,它是从BaseWrap派生的类,或者是{}是{}的可解引用类型,或者是从{}派生的类。有关需求的详细信息,请参见^{}规范。在

可以通过如下方式公开类来解决这些问题:

namespace python = boost::python;
python::class_<BaseWrap, boost::noncopyable>("Base", python::init<>())
  .def("f", python::pure_virtual(&Base::f))
  ;

这里是一个完整的例子demonstrating,它传递一个从C++暴露类派生到C++向量的对象,该向量通过^ {}:

#include <vector>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

struct base
{
  virtual ~base() {}
  virtual int perform() = 0;
};

struct base_wrap: base, boost::python::wrapper<base>
{
  int perform() { return int(this->get_override("perform")()) - 10; }
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<base_wrap, boost::noncopyable>("Base", python::init<>())
    .def("perform", python::pure_virtual(&base::perform))
    ;

  python::class_<std::vector<base*>>("BaseList")
    .def(python::vector_indexing_suite<std::vector<base*>>())
    ;

  python::def("do_perform", +[](base* object) {
    return object->perform();
  });
}

交互式使用:

>>> import example
>>> class derived(example.Base):
...     def __init__(self):
...         self.name = "test"
...         example.Base.__init__(self)
...     def perform(self):
...         return 42
...       
>>> d = derived()
>>> base_list = example.BaseList()
>>> base_list.append(d)
>>> assert(len(base_list) == 1)
>>> assert(base_list[0].perform() == 42)
>>> assert(example.do_perform(base_list[0]) == 32)

对于集合和指针,通常有一些注意事项。在这种情况下:

  • BaseList对象没有其元素所引用的对象的共享所有权。请注意确保容器引用的对象的生存期至少与容器本身一样长。在上面的例子中,如果对象d被删除,那么调用base_list[0].perform()会导致未定义的行为。在
  • 不能对base_list进行迭代,因为迭代器的值将尝试执行base*-到Python的转换,这是不存在的。在

上面的例子也展示了功能调度的区别。如果Python可以直接调用方法,那么它将使用自己的方法解析机制来调用方法。注意base_list[0].perform()和{}如何返回不同的值,因为其中一个通过操纵结果的base_wrap::perform()进行调度,而另一个则没有。在

在原始代码中:

class derived(sandbox.Base):
    ...
    def f(self):
        print("Hello Derived!")

d = derived()
d.f()

由于Python知道derived.f(),调用d.f()将不会通过BaseWrap::f()进行调度。如果调用了BaseWrap::f(),那么它将被抛出,因为derived.f()返回了None,这将无法转换为int

struct BaseWrap : Base, wrapper<Base> {
    int f() { return this->get_override("f")(); }
                        // ^~~ returns a boost::python::object, faling to 
                        //     extract `int` will throw. 
};

相关问题 更多 >