boost::python保护的析构函数issu

2024-06-14 19:18:48 发布

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

namespace test_py
{

class Event
{
public:
    enum Type { BEGIN = 0, RESULT, END };

    Type get_type( ) const { return m_type; }

protected:
    Event( ) { }
    ~Event( ) { }
    Type m_type;
};

class EventBegin : public Event
{
public:
    EventBegin( ) { m_type = Event::BEGIN; }
    ~EventBegin( ) {}
};

class EventResult : public Event
{
public:
    EventResult( int result ) { m_type = Event::RESULT; m_result = result; }
    ~EventResult( ) {}
    int get_result( ) { return m_result; }

protected:
    int m_result;
};

class EventEnd : public Event
{
public:
    EventEnd( ) { m_type = Event::END; }
    ~EventEnd( ) {}
};

class EventListener
{
public:
    virtual void on_event( const Event& event ) = 0;
};


struct EventListenerWrap: EventListener, py::wrapper< EventListener >
{
    void
    on_event( const Event& event )
    {
        this->get_override( "on_event" )( event );
    }
};

BOOST_PYTHON_MODULE( test_py )
{
    {
        py::scope outer = py::class_< Event, boost::noncopyable >( "Event", py::no_init )
            .add_property( "event_type", &Event::get_type );

        py::enum_< Event::Type >( "EventType" )
            .value( "BEGIN", Event::BEGIN )
            .value( "RESULT", Event::RESULT )
            .value( "END", Event::END )
            .export_values( );
    }

    {
        py::class_< EventBegin, py::bases< Event > >( "EventBegin" );
    }

    {
        py::class_< EventResult, py::bases< Event > >( "EventResult", py::no_init )
            .def( py::init< int >( ( py::arg( "result" ) ) ) )
            .add_property( "result", &EventResult::get_result );
    }

    {
        py::class_< EventEnd, py::bases< Event > >( "EventEnd" );
    }

    {
        py::class_< EventListenerWrap, boost::noncopyable >( "EventListener", py::no_init )
            .def( "on_event", py::pure_virtual( &EventListener::on_event ) );
    }
}

}

我在事件基类中有一个受保护的构造函数和析构函数,不能更改它。 在Python 2.7中,我需要从EnviistListNER类派生,并将指针发送回C++代码。 在编译过程中,我遇到了这样的错误:

^{pr2}$

Tags: pyeventgetontyperesultpublicclass
2条回答
    py::scope outer = py::class_< Event, boost::noncopyable >( "Event", py::no_init )
        .add_property( "event_type", &Event::get_type );

第一眼就知道你有问题。py::class_<Event, ...>只知道绑定到Event,它有受保护的析构函数。在

您必须将Event包装在一个公开析构函数的类中。在

如果这不可能(因为你不能改变EventBeginEventEnd等的定义),那么你就必须编写一个多态容器,通过它自己的内部接口保存派生类,在内部将事件作为非多态对象处理。在

这并不像听起来那么困难:

^{pr2}$

当暴露一个函数时,Boost.Python将为每个参数生成转换器。对于类型为T和{}的参数,生成的Python转换器将保存对象的副本,因此需要访问复制构造函数和析构函数。这种行为的基本原理是防止意外地暴露悬空引用。当将C++参数传递给Python时,情况也是如此。在

这种行为在以下情况下会出现问题:

  • 暴露EventListener::on_event(const Event&),如Boost.Python正在尝试创建一个将保存Event副本的对象。要解决此问题,请考虑公开一个接受Event*的辅助函数,然后委托给原始函数。在
  • 将一个Event对象传递给EventListenerWrap::on_event中的Python。要解决此问题,请考虑将参数包装在boost::ref()boost::python::ptr()中。在

请注意,如果不创建副本,则会为悬空引用创建机会。如果实际的{{4CD> }对象由Python拥有,则其生命期至少需要与C++中的任何引用相同。同样的。如果实际的^ {< CD4}}对象是C++所拥有的,那么它的生存期至少需要在Python中引用它的时间长。在

struct EventListenerWrap
  : EventListener,
    boost::python::wrapper<EventListener>
{
  void on_event(const Event& event)
  {
    this->get_override("on_event")(boost::ref(event));
  }
};

/// @brief Auxiliary function that will delegate to EventListener::on_event and
///        avoid by-value conversions at the language boundary.  This prevents
///        prevents Boost.Python from creating instance holders that would hold
///        the value as an rvalue.
void event_listener_on_event_aux(EventListener& listener, Event* event)
{
  return listener.on_event(*event);
}

BOOST_PYTHON_MODULE(...)
{
  namespace python = boost::python;
  python::class_<EventListenerWrap, boost::noncopyable>("EventListener")
    .def("on_event", python::pure_virtual(&event_listener_on_event_aux))
    ;
}

一个有趣的细节是,boost::python::pure_virtual()将复制它包装的函数的签名,但它永远不会实际调用包装的函数。因此,被包装的函数可以有一个no-op/empty实现,但是如果pure_virtual指示符被删除或者辅助函数被直接调用,那么提供一个实现是一个好主意。在

另外,请注意,为了允许Python类从Boost.Python同学们Boost.Python必须公开__init__()方法。不提供任何方法,例如使用boost::python::no_init(),将导致运行时错误。在


下面是一个基于原始代码的最小完整示例,demonstrates公开了一个具有受保护的构造函数和析构函数的类、两个派生类以及通过Boost.Python公司名称:

^{pr2}$

交互式使用:

>>> import example
>>> class Listener(example.EventListener):
...     def on_event(self, event):
...         assert(isinstance(event, example.Event))
...         print "Listener::on_event: ", event, event.name
... 
>>> listener = Listener()
>>> listener.on_event(example.EventA())
Listener::on_event:  <example.EventA object at 0x7f3bc1176368> event_a
>>> example.do_on_event(listener, example.EventB())
event_listener_wrap::on_event()
Listener::on_event:  <example.Event object at 0x7f3bc1246fa0> event_b

当Python直接知道方法时,它将调用它而不经过Python。推进. 注意^ { < CD16> }没有通过C++发送,并且^ {CD17}}对象保持其^ {CD18}}类型。另一方面,当强制调度进入C++时,不会发生上报。当通过example.do_on_event()调用Listener.on_event()时,event对象的类型是example.Event,而不是{}。在

相关问题 更多 >