简体   繁体   English

Boost.Python:Python中C ++对象的inheret:无法添加到C ++基类列表

[英]Boost.Python: inheret from C++ object in python: Cannot add to C++ base class list

I am trying to extend existing C++ objects in Python via inheritance. 我试图通过继承扩展Python中现有的C ++对象。 I can do this successfully and run virtual methods overridden in Python. 我可以成功完成此操作,并运行Python中覆盖的虚拟方法。 When I however, try to add the python object to a list of pointers of the C++ Base object type(the Base object the python class has overridden), I get a type error: 'Attempting to append an invalid type' 但是,当我尝试将python对象添加到C ++基础对象类型的指针列表(python类已覆盖的基础对象)时,出现类型错误:“尝试附加无效类型”

I am sure this error is due to there begin no 'implicitly_convertible' functionality from derived* to base*. 我确定此错误是由于没有从派生*到基本*的“ implicitly_convertible”功能引起的。 In C++, this would be defined as so: implicitly_convertible<[Derived_from_base] ,Base >();. 在C ++中,将这样定义:hidden_​​convertible <[Derived_from_base] ,Base >();。 Is it possible to define this in python? 可以在python中定义吗?

How can I achieve this? 我该如何实现?

Here is sample code reproducing this behaviour. 这是重现此行为的示例代码。

C++ 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 from sandbox import * 从沙箱导入的Python *

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

d = derived()
d.f()          # Output: Hello Derived!

a = A()
a.baseList.append(d) # TypeError: Attempting to append an invalid type

Any help or ideas will be greatly appreciated. 任何帮助或想法将不胜感激。

The BaseList.append() function receives an argument with the right type ; BaseList.append()函数接收类型正确的参数; however, the argument has an inappropriate value . 但是,该论点具有不合适的价值 In Python, the derived initializer is not initializing the sandbox.Base part of its hierarchy. 在Python中, derived初始化程序不会初始化其层次结构的sandbox.Base部分。 This results in the Boost.Python object not containing a C++ BaseWrap object. 这导致Boost.Python对象不包含C ++ BaseWrap对象。 Hence, when BaseList.append() attempts to extract the C++ BaseWrap object, it fails and throws an error. 因此,当BaseList.append()尝试提取C ++ BaseWrap对象时,它会失败并引发错误。

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.

To resolve the issue, explicitly invoke Base.__init__() within derived.__init__() : 要解决此问题,请在derived.__init__()显式调用Base.__init__() derived.__init__()

class derived(Base):
    def __init__(self):
        self.name = "test"
        Base.__init__(self)

However, attempting to do this will surface other problems with how BaseWrap is exposed: 但是,尝试执行此操作将暴露有关BaseWrap公开方式的其他问题:

  • The sandbox.Base class must be constructible from Python, so the bindings cannot provide boost::python::no_init as its initializer specification. sandbox.Base类必须可从Python构造,因此绑定不能提供boost::python::no_init作为其初始化程序规范。 Generally, one would only want to use boost::python::no_init when the C++ objects are being explicitly instantiated from C++ and passed to Python, such as via factory functions. 通常,仅当从C ++显式实例化C ++对象并通过工厂函数将其传递给Python时,才希望使用boost::python::no_init
  • When T is BaseWrap , a HeldType of Base* fails to meet the requirements of HeldType . TBaseWrapBase*HeldType不能满足HeldType的要求。 In particular, the HeldType either needs to be: BaseWrap , a class derived from BaseWrap , or a dereferenceable type for which boost::python::pointee<Base*>::type is BaseWrap or a class derived from BaseWrap . 特别是, HeldType要么是: BaseWrap ,一个从BaseWrap派生的类,要么是一个可引用类型,其boost::python::pointee<Base*>::typeBaseWrap或从BaseWrap派生的类。 See the class_ specification for requirement details. 有关要求的详细信息,请参见class_规范。

These can be resolved by exposing the class as follows: 这些可以通过公开以下类来解决:

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

Here is a complete example demonstrating passing an object that derives from a C++ exposed class to a C++ vector exposed via the vector_indexing_suite : 这是一个完整的示例, 演示将从C ++暴露类派生的对象传递给通过vector_indexing_suite暴露的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();
  });
}

Interactive usage: 互动用法:

>>> 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)

With collections and pointers, there are often some caveats. 关于集合和指针,通常会有一些警告。 In this case: 在这种情况下:

  • The BaseList object does not have shared ownership of objects to which its elements refer. BaseList对象没有其元素引用的对象的共享所有权。 Be careful to guarantee that objects referenced by the container have a lifetime at least as long as the container itself. 注意确保容器引用的对象的生存期至少与容器本身一样长。 In the above example, if object d is deleted, then invoking base_list[0].perform() can result in undefined behavior. 在上面的示例中,如果删除了对象d ,则调用base_list[0].perform()可能导致未定义的行为。
  • One cannot iterate over the base_list , as the iterator's value will attempt to perform a base* -to-Python conversion, which does not exists. 不能迭代base_list ,因为迭代器的值将尝试执行从base*到Python的转换,该转换不存在。

The above example also demonstrates the difference in function dispatching. 上面的示例还演示了函数分派的区别。 If Python can directly invoke a method, it will do so using its own method-resolution mechanics. 如果Python可以直接调用方法,它将使用自己的方法解析机制进行调用。 Note how base_list[0].perform() and example.do_perform(base_list[0]) return different values, as one gets dispatched through base_wrap::perform() which manipulates the result, and the other does not. 请注意, base_list[0].perform()example.do_perform(base_list[0])返回不同的值,因为一个是通过操作结果的base_wrap::perform()调度的,而另一个则不是。

In the original code: 在原始代码中:

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

d = derived()
d.f()

As Python is aware of derived.f() , invoking df() will not get dispatched through BaseWrap::f() . 如Python所知, derived.f()不会通过BaseWrap::f()调度df() BaseWrap::f() If BaseWrap::f() had been invoked, it would have thrown because derived.f() returned None , which will fail to convert to an int : 如果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. 
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM