2015-11-09 109 views
1
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中,我需要從EventListener類派生並將指針發送回C++代碼。 在編譯過程中我得到了錯誤這樣的:boost :: python受保護的析構函數問題

/boost/python/detail/destroy.hpp: In instantiation of ‘static void boost::python::detail::value_destroyer<false>::execute(const volatile T*) [with T = test_py::Event]’: 
/boost/python/detail/destroy.hpp:95:36: required from ‘void boost::python::detail::destroy_referent_impl(void*, T& (*)()) [with T = const test_py::Event]’ 
/boost/python/detail/destroy.hpp:101:39: required from ‘void boost::python::detail::destroy_referent(void*, T (*)()) [with T = const test_py::Event&]’ 
/boost/python/converter/rvalue_from_python_data.hpp:135:71: required from ‘boost::python::converter::rvalue_from_python_data<T>::~rvalue_from_python_data() [with T = const test_py::Event&]’ 
/boost/python/converter/arg_from_python.hpp:107:8: required from ‘PyObject* boost::python::detail::caller_arity<2u>::impl<F, Policies, Sig>::operator()(PyObject*, PyObject*) [with F = void (test_py::EventListener::*)(const test_py::Event&); Policies = boost::python::default_call_policies; Sig = boost::mpl::vector3<void, test_py::EventListener&, const test_py::Event&>; PyObject = _object]’ 
/boost/python/object/py_function.hpp:38:33: required from ‘PyObject* boost::python::objects::caller_py_function_impl<Caller>::operator()(PyObject*, PyObject*) [with Caller = boost::python::detail::caller<void (test_py::EventListener::*)(const test_py::Event&), boost::python::default_call_policies, boost::mpl::vector3<void, test_py::EventListener&, const test_py::Event&> >; PyObject = _object]’ 
EventListener.cpp:193:1: required from here 
EventListener.cpp:18:5: error: ‘test_py::Event::~Event()’ is protected 
    ~Event() { } 
    ^
In file included from /boost/python/converter/rvalue_from_python_data.hpp:10:0, 
       from /boost/python/converter/registry.hpp:9, 
       from /boost/python/converter/registered.hpp:8, 
       from /boost/python/object/make_instance.hpp:10, 
       from /boost/python/object/make_ptr_instance.hpp:8, 
       from /boost/python/to_python_indirect.hpp:11, 
       from /boost/python/converter/arg_to_python.hpp:10, 
       from /boost/python/call.hpp:15, 
       from /boost/python/object_core.hpp:14, 
       from /boost/python/object/class.hpp:9, 
       from /boost/python/class.hpp:13, 
       from ../../defs.hpp:6, 
       from ../defs.hpp:3, 
       from defs.hpp:3, 
       from EventListener.cpp:1: 
/boost/python/detail/destroy.hpp:33:9: error: within this context 
     p->~T(); 
     ^
+0

我沒有太多分析你的代碼,但是,你是否有任何機會將派生類的指針分配給基類指針,並嘗試通過基類指針來刪除它? –

回答

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

第一眼告訴我,你在這裏有一個問題。 py::class_<Event, ...>只知道綁定到Event,它具有受保護的析構函數。

你將不得不將Event包裝在公開暴露析構函數的類中。

如果這是不可能的(因爲你不能改變的EventBegin定義,EventEnd等爲例),然後你將不得不寫,通過其自身的內部接口持有的派生類的多態容器,內部處理事件作爲非多態對象。

這並不困難,因爲它的聲音:

#include <memory> 
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 EventProxy 
    { 
     // define an interface for turning a non-polymorphic event 
     // into a polymorphic one 
     struct concept 
     { 
      virtual const Event* as_event() const = 0; 
      virtual ~concept() = default; 
     }; 

     // define a model to support the polymorphic interface for a 
     // non-polymorphic concrete object 
     template<class T> struct model : concept 
     { 
      template<class...Args> model(Args&&... args) 
      : _event(std::forward<Args>(args)...) 
      {} 

      const Event* as_event() const override { 
       return &_event; 
      } 

      T _event; 
     }; 

     // construct the model that contains any Event 
     template<class T> 
     EventProxy(std::shared_ptr<T> ptr) 
     : _impl(std::move(ptr)) 
     {} 

    public: 
     // T should be derived from Event... 
     template<class T, class...Args> 
     static EventProxy create(Args&&... args) 
     { 
      return EventProxy(std::make_shared<model<T>>(std::forward<Args>(args)...)); 
     } 

     // simply return the address of the internal non-polymorphic event  
     const Event* as_event() const { 
      return _impl->as_event(); 
     } 

     // return a shared pointer that points to the internal Event BUT 
     // defers lifetime ownership to our internal shared_ptr to 
     // our model. This means we never invoke the polymorphic 
     // destructor of Event through the protected interface. 
     std::shared_ptr<const Event> as_shared_event() const { 
      return std::shared_ptr<const Event>(_impl, _impl->as_event()); 
     } 

    private: 
     // lifetime of the proxy is owned by this shared_ptr. 
     std::shared_ptr<concept> _impl; 
    }; 

} 

// a quick test.  
auto main() -> int 
{ 
    auto ep = test_py::EventProxy::create<test_py::EventBegin>(); 

    const test_py::Event* p = ep.as_event(); 

    std::shared_ptr<const test_py::Event> sp = ep.as_shared_event(); 
} 
+0

感謝理查德的快速回復,但是我仍然不清楚如何在python綁定中使用這個新的EventProxy類。請問,請將這段代碼添加到您的示例中? –

+0

eventproxy類爲您提供了一個指向該事件的指針,您可以在python綁定中使用該指針。其目的是將刪除器與Event界面分離。 –

+0

所以你可以使用py :: class_ 來代替py :: class 。您可以根據需要將盡可能多的方法放在代理上。它甚至可以複製。 –

0

當暴露一個函數,Boost.Python的將生成用於每個參數的轉換器。對於類型爲TT&的參數,生成的Python轉換器將保存該對象的副本,因此需要訪問複製構造函數和析構函數。這種行爲的基本原理是防止意外暴露懸掛引用。將C++參數傳遞給Python時也是如此。

這種行爲提出了一個問題的時候:

  • 暴露EventListener::on_event(const Event&),因爲Boost.Python的試圖創建一個對象,將舉行Event的副本。要解決此問題,請考慮公開一個接受Event*的輔助功能,然後委託給原始功能。
  • Event對象傳遞給EventListenerWrap::on_event中的Python。要解決此問題,請考慮將參數包裝在boost::ref()boost::python::ptr()中。

請注意,通過不創建副本,它會創建懸空引用的機會。如果實際的Event對象由Python擁有,那麼它的生命週期至少與在C++中引用它的時間一樣長。同樣。如果實際的Event對象由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()將複製它所包裝的函數的簽名,但它永遠不會實際調用包裝的函數。因此,封裝的函數可以有一個無操作/空的實現,但提供實現是一個好主意,因爲指定的代號被刪除或者直接調用輔助函數。

此外,請注意,要允許Python類從Boost.Python類派生,Boost.Python必須公開__init__()方法。不提供任何方法(例如使用boost::python::no_init())將導致運行時錯誤。


這裏是基於原始代碼demonstrates通過Boost.Python的曝光與受保護的構造和析構函數,兩個派生類和派生類的虛擬調度一類的最小完整的例子:

#include <iostream> 
#include <string> 
#include <boost/python.hpp> 

// Legacy API. 
class event 
{ 
public: 
    std::string name; 
protected: 
    event(std::string name) : name(name) {} 
    ~event() = default; 
}; 

struct event_a: event { event_a(): event("event_a") {} }; 
struct event_b: event { event_b(): event("event_b") {} }; 

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

// Boost.Python helper types and functions. 

struct event_listener_wrap 
: event_listener, 
    boost::python::wrapper<event_listener> 
{ 
    void on_event(const event& event) 
    { 
    std::cout << "event_listener_wrap::on_event()" << std::endl; 
    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_wrap(event_listener& listener, event* event) 
{ 
    return listener.on_event(*event); 
} 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 

    // Expose event and suppress default by-value converters and initailizers. 
    // This will prevent Boost.Python from trying to access constructors and 
    // destructors. 
    python::class_<event, boost::noncopyable>("Event", python::no_init) 
    .def_readonly("name", &event::name) 
    ; 

    // Expose event_a and event_b as derived from event. 
    python::class_<event_a, python::bases<event>>("EventA"); 
    python::class_<event_b, python::bases<event>>("EventB"); 

    // Expose event_listener_wrap. 
    python::class_<event_listener_wrap, boost::noncopyable>("EventListener") 
    .def("on_event", python::pure_virtual(&event_listener_on_event_wrap)) 
    ; 

    // Expose a function that will perform virtual resolution. 
    python::def("do_on_event", &event_listener_on_event_wrap); 
} 

交互式用法:

>>> 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是直接感知方法,它將調用它而不穿過Boost.Python的。請注意,listener.on_event()未通過C++派發,而event對象保持其example.EventA類型。另一方面,當調度強制進入C++時,不會發生向上轉換。當通過example.do_on_event()調用Listener.on_event()時,event對象的類型爲example.Event而不是example.EventB