观察者模式经常出现在我的C++项目中,我现在希望通过Cython绑定将其公开给Python解释器。我试图构建一个最小的例子来说明这种情况。Spectacle接受从抽象基类Observer派生的任何对象,如Onlooker。当我们调用Spectacle::event()时,每个注册的观察者都会收到通知。
这是文件ObserverPattern.h的内容
class Spectacle {
private:
std::vector<Observer*> observers;
public:
Spectacle() {};
virtual ~Spectacle() {};
virtual void registerObserver(Observer* observer) {
this->observers.push_back(observer);
}
virtual void event() {
std::cout << "event triggered" << std::endl;
for (Observer* observer : this->observers) {
observer->onEvent();
}
}
};
class Observer {
public:
Observer() {};
virtual ~Observer() {};
virtual void onEvent() = 0;
};
class Onlooker : public Observer {
public:
Onlooker() {};
virtual ~Onlooker() {};
virtual void onEvent() {
std::cout << "event observed" << std::endl;
}
};下面是我的.pyx文件的内容,其中包含绑定:
cdef extern from "ObserverPattern.h":
cdef cppclass _Spectacle "Spectacle":
_Spectacle() except +
void registerObserver(_Observer* observer)
void event()
cdef extern from "ObserverPattern.h":
cdef cppclass _Observer "Observer":
_Observer() except +
void onEvent()
cdef extern from "ObserverPattern.h":
cdef cppclass _Onlooker "Onlooker":
_Onlooker() except +
void onEvent()
cdef class Spectacle:
cdef _Spectacle _this
def event(self):
self._this.event()
def registerObserver(self, Observer observer):
self._this.registerObserver(observer._this)
cdef class Observer:
cdef _Observer* _this # must be a pointer because _Observer has pure virtual method
cdef class Onlooker(Observer):
pass # what should be the class body?这确实会编译,但在调用event()并通知观察者时会出现段错误:
>>> spec = CythonMinimal.Spectacle()
>>> look = CythonMinimal.Onlooker()
>>> spec.registerObserver(look)
>>> spec.event()
event triggered
Segmentation fault: 11这里的问题是什么,修复是什么样子的?
发布于 2013-06-13 00:13:03
你的问题本质上是“用Python实现一个C++接口”。
要做到这一点,唯一可移植的方法是编写一个实际的C++类,它将回调到Python中。
Cython有一个未公开的experimental_cpp_class_def选项,允许使用Cython语法创建C++类。它不是很漂亮(IMO),但它适用于许多场景。
下面是如何实现委托给所提供的Python callable的Observer:
from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
cdef cppclass ObserverImpl(_Observer):
PyObject* callback
__init__(object callback): # constructor. "this" argument is implicit.
Py_INCREF(callback)
this.callback = <PyObject*>callback
__dealloc__(): # destructor
Py_DECREF(<object>this.callback)
void onEvent():
(<object>this.callback)() # exceptions will be ignored这就是你使用它的方式:
def registerObserver(self, callback not None): # user passes any Python callable
self._this.registerObserver(new ObserverImpl(callback))就像C结构一样,C++对象不能包含Cython管理的object引用。这就是为什么你必须使用PyObject*字段并自己管理引用计数。当然,在方法内部,您可以强制转换并使用任何Cython特性。
另一个棘手的时刻是异常传播。在C++中定义的onEvent()方法不能传播Python异常。Cython将简单地忽略它无法传播的异常。如果您想做得更好,可以自己捕获它们并存储在某个地方,以备以后检查或作为C++异常重新抛出。(我认为在Cython语法中抛出C++异常是不可能的,但是您可以调用外部抛出辅助函数。)
如果你的观察者有多个方法,那么callback就是一个Python类,而不是直接调用它,而是调用它的方法,比如(<object>this.callback).onEvent()。
显然,也可以直接用C++编写ObserverImpl。Py_INCREF、Py_DECREF和PyObject_Call/PyObject_CallMethod是唯一需要的Python。
https://stackoverflow.com/questions/17067544
复制相似问题