X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Finclude%2Flistener.hh;h=b9042ba301ac79f715b197ed22d159bbb47943df;hb=36fba3138858ee42ba3f03e3de0efa1d1f49d147;hp=e0827f8786dc372ceaee9256f159e7d1dbe0c7d5;hpb=2f0c6eb19208485a86d3416db3f3640a1d54752a;p=lilypond.git diff --git a/lily/include/listener.hh b/lily/include/listener.hh index e0827f8786..b9042ba301 100644 --- a/lily/include/listener.hh +++ b/lily/include/listener.hh @@ -23,109 +23,120 @@ /* Listeners - Listeners are used for stream event dispatching. If you want to - register a method as an event handler in a dispatcher, then you - must: + Listeners are used for stream event dispatching. The usual way to + work with them is to use the GET_LISTENER macro which combines the + basic Listener algorithm with a Callback_wrapper structure providing + a Scheme handle into a member function. - - declare the method using the DECLARE_LISTENER macro. + To register a member function of Foo as an event handler in a + dispatcher, you must: + + - declare the function: class Foo { - DECLARE_LISTENER (method); + void method (SCM); ... }; - This macro declares the method to take a SCM as parameter, and to - return void. It also declares some other stuff that shouldn't be - touched. - - implement the method using IMPLEMENT_LISTENER: - IMPLEMENT_LISTENER (Foo, method) - void method (SCM e) + - implement the method:: + void Foo::method (SCM e) { write ("Foo hears an event!"); } - - Extract a listener using GET_LISTENER (Foo->method) + - Extract a listener using GET_LISTENER (Foo, method) - Register the method to the dispatcher using Dispatcher::register Example: Foo *foo = (...); - Stream_distributor *d = (...); - Listener l = GET_LISTENER (foo->method); - d->register_listener (l, "EventClass"); + Dispatcher *d = (...); + Listener l = foo->GET_LISTENER (Foo, method); + d->add_listener (l, ly_symbol2scm ("EventClass")); Whenever d hears a stream-event ev of class "EventClass", the implemented procedure is called. + GET_LISTENER actually makes use of a member function + get_listener (SCM) available in every Smob<...>-derived class. + get_listener receives a function getting an object instance and an + event and will turn it into a Listener that will (after turning into + Scheme), behave as a function receiving an event as its sole + argument, with the object instance being the object from which + get_listener was called as a member. + + So (p->get_listener (f)).smobbed_copy () is roughly equivalent to + (lambda (ev) (f p->self_scm() ev)) + Limitations: - - DECLARE_LISTENER currently only works inside smob classes. + + The Callback_wrapper mechanism used in GET_LISTENER works only for + classes derived from Smob<...>. */ +#include "callback.hh" #include "smobs.hh" -typedef struct -{ - void (*listen_callback) (void *, SCM); - void (*mark_callback) (void *); - bool (*equal_callback) (void *, void *); -} Listener_function_table; - -class Listener +// A listener is essentially any procedure accepting a single argument +// (namely an event). The class Listener (or rather a smobbed_copy of +// it) behaves like such a procedure and is composed of a generic +// callback function accepting two arguments, namely a "target" +// (usually an engraver instance) and the event. Its Scheme +// equivalent would be +// +// (define (make-listener callback target) +// (lambda (event) (callback target event))) +// +// The class construction is lightweight: as a Simple_smob, this is +// only converted into Scheme when a smobbed_copy is created. + +class Listener : public Simple_smob { - void *target_; - Listener_function_table *type_; +private: + SCM callback_; + SCM target_; public: - Listener (const void *target, Listener_function_table *type); - Listener (Listener const &other); - Listener (); + static const char type_p_name_[]; - void listen (SCM ev) const; + Listener (SCM callback, SCM target) + : callback_ (callback), target_ (target) { } + + LY_DECLARE_SMOB_PROC (&Listener::listen, 1, 0, 0) + SCM listen (SCM ev) + { + scm_call_2 (callback_, target_, ev); + return SCM_UNSPECIFIED; + } + + SCM mark_smob () const + { + scm_gc_mark (callback_); + return target_; + } bool operator == (Listener const &other) const { - return type_ == other.type_ - && (*type_->equal_callback) ((void *) target_, (void *) other.target_); + return scm_is_eq (callback_, other.callback_) + && scm_is_eq (target_, other.target_); + } + + static SCM equal_p (SCM a, SCM b) + { + return *unchecked_unsmob (a) == *unchecked_unsmob (b) + ? SCM_BOOL_T : SCM_BOOL_F; } - DECLARE_SIMPLE_SMOBS (Listener); + template + static SCM trampoline (SCM target, SCM ev) + { + T *t = unsmob (target); + LY_ASSERT_SMOB (T, target, 1); + + (t->*callback) (ev); + return SCM_UNDEFINED; + } }; -DECLARE_UNSMOB (Listener, listener); - -#define IMPLEMENT_LISTENER(cl, method) \ -void \ -cl :: method ## _callback (void *self, SCM ev) \ -{ \ - cl *s = (cl *)self; \ - s->method (ev); \ -} \ -void \ -cl :: method ## _mark (void *self) \ -{ \ - cl *s = (cl *)self; \ - scm_gc_mark (s->self_scm ()); \ -} \ -bool \ -cl :: method ## _is_equal (void *a, void *b) \ -{ \ - return a == b; \ -} \ -Listener \ -cl :: method ## _listener () const \ -{ \ - static Listener_function_table callbacks; \ - callbacks.listen_callback = &cl::method ## _callback; \ - callbacks.mark_callback = &cl::method ## _mark; \ - callbacks.equal_callback = &cl::method ## _is_equal; \ - return Listener (this, &callbacks); \ -} - -#define GET_LISTENER(proc) proc ## _listener () - -#define DECLARE_LISTENER(name) \ - inline void name (SCM); \ - static void name ## _callback (void *self, SCM ev); \ - static void name ## _mark (void *self); \ - static bool name ## _is_equal (void *a, void *b); \ - Listener name ## _listener () const + +#define GET_LISTENER(cl, proc) get_listener (Callback_wrapper::make_smob > ()) #endif /* LISTENER_HH */