提升::p ython 受保护的析构函数问题
boost::python protected destructor issue
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/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();
^
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();
}
当公开一个函数时,Boost.Python 将为每个参数生成转换器。 对于类型为 T
和 T&
的参数,生成的 Python 转换器将保存对象的副本,因此需要访问复制构造函数和析构函数。 此行为的基本原理是防止意外公开悬而未决的引用。 将C++参数传递给 Python 时也是如此。
在以下情况下,此行为会出现问题:
- 公开
EventListener::on_event(const Event&)
,因为Boost.Python正在尝试创建一个将保存Event
副本的对象。 若要解决此问题,请考虑公开一个辅助函数,该函数接受Event*
,然后委托给原始函数。 - 在
EventListenerWrap::on_event
中将一个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()
将复制它包装的函数的签名,但它永远不会实际调用包装的函数。 因此,包装的函数可以有一个无操作/空实现,但如果删除pure_virtual
指示符或直接调用辅助函数,则提供一个实现是一个好主意。
另外,请注意,要允许 Python 类从 Boost.Python 类派生,Boost.Python 必须公开一个__init__()
方法。 不提供任何方法(例如使用 boost::python::no_init()
(将导致运行时错误。
下面是一个基于原始代码的最小完整示例,该示例演示了公开具有受保护构造函数和析构函数的类、两个派生类以及通过 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
。
- 类中C++析构函数的 Xcode 问题
- 为什么我以前调用析构函数的方式会导致模因问题
- 二叉搜索树析构函数问题
- 与C++析构函数相比,C#析构函数和GC并没有真正解决问题
- C++二进制树递归析构函数问题
- 我不明白析构函数有什么问题?
- 创建'new'实例可以解决析构函数崩溃问题?
- c++析构函数解分配问题
- 为什么我的析构函数导致我的链表出现问题
- 析构函数问题 + 列表显示
- 崩溃,可能是由于构造函数或析构函数中的某些问题
- 通用二进制树节点析构函数问题
- 标量删除析构函数问题
- 提升::p ython 受保护的析构函数问题
- 二叉树析构函数问题
- 矩阵类操作符重载,析构函数问题
- RHEL5中的字符串析构函数问题
- c++的析构函数问题
- 图像析构函数问题
- C++中的继承和虚拟析构函数问题