2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005 Erik Sandberg <mandolaerik@gmail.com>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
26 Listeners are used for stream event dispatching. The usual way to
27 work with them is to use the GET_LISTENER macro which combines the
28 basic Listener algorithm with a Callback_wrapper structure providing
29 a Scheme handle into a member function.
31 To register a member function of Foo as an event handler in a
34 - declare the function:
41 - implement the method::
42 void Foo::method (SCM e)
44 write ("Foo hears an event!");
47 - Extract a listener using GET_LISTENER (Foo, method)
48 - Register the method to the dispatcher using Dispatcher::register
53 Dispatcher *d = (...);
54 Listener l = foo->GET_LISTENER (Foo, method);
55 d->add_listener (l, ly_symbol2scm ("EventClass"));
57 Whenever d hears a stream-event ev of class "EventClass",
58 the implemented procedure is called.
60 GET_LISTENER actually makes use of a member function
61 get_listener (SCM) available in every Smob<...>-derived class.
62 get_listener receives a function getting an object instance and an
63 event and will turn it into a Listener that will (after turning into
64 Scheme), behave as a function receiving an event as its sole
65 argument, with the object instance being the object from which
66 get_listener was called as a member.
68 So (p->get_listener (f)).smobbed_copy () is roughly equivalent to
69 (lambda (ev) (f p->self_scm() ev))
73 The Callback_wrapper mechanism used in GET_LISTENER works only for
74 classes derived from Smob<...>.
78 #include "stream-event.hh"
80 // A listener is essentially any procedure accepting a single argument
81 // (namely an event). The class Listener (or rather a smobbed_copy of
82 // it) behaves like such a procedure and is composed of a generic
83 // callback function accepting two arguments, namely a "target"
84 // (usually an engraver instance) and the event. Its Scheme
85 // equivalent would be
87 // (define (make-listener callback target)
88 // (lambda (event) (callback target event)))
90 // The class construction is lightweight: as a Simple_smob, this is
91 // only converted into Scheme when a smobbed_copy is created.
93 class Listener : public Simple_smob<Listener>
99 static const char type_p_name_[];
101 Listener (SCM callback, SCM target)
102 : callback_ (callback), target_ (target) { }
104 LY_DECLARE_SMOB_PROC (&Listener::listen, 1, 0, 0)
107 scm_call_2 (callback_, target_, ev);
108 return SCM_UNSPECIFIED;
111 SCM mark_smob () const
113 scm_gc_mark (callback_);
117 bool operator == (Listener const &other) const
119 return scm_is_eq (callback_, other.callback_)
120 && scm_is_eq (target_, other.target_);
123 static SCM equal_p (SCM a, SCM b)
125 return *unchecked_unsmob (a) == *unchecked_unsmob (b)
126 ? SCM_BOOL_T : SCM_BOOL_F;
130 // A callback wrapper creates a Scheme-callable version of a
131 // non-static class member function callback which you can call with a
132 // class instance and an event.
134 // If you have a class member function
135 // void T::my_listen (SCM ev)
136 // then Callback_wrapper::make_smob<T, SCM, &T::my_listen> ()
137 // will return an SCM function roughly defined as
138 // (lambda (target ev) (target->my_listen ev))
140 // The setup is slightly tricky since the make_smob quasi-constructor
141 // call is a template function templated on the given callback, and so
142 // is the trampoline it uses for redirecting the callback. The class
143 // itself, however, is not templated as that would create a wagonload
146 class Callback_wrapper : public Simple_smob<Callback_wrapper>
148 // We use an ordinary function pointer pointing to a trampoline
149 // function (templated on the callback in question) instead of
150 // storing a member function pointer to a common base class like
151 // Smob_core. The additional code for the trampolines is negligible
152 // and the performance implications of using member function
153 // pointers in connection with inheritance are somewhat opaque as
154 // this involves an adjustment of the this pointer from Smob_core to
155 // the scope containing the callback.
156 void (*trampoline_) (SCM, SCM);
157 template <class T, void (T::*callback)(SCM)>
158 static void trampoline (SCM target, SCM ev)
160 T *t = unsmob<T> (target);
161 LY_ASSERT_SMOB (T, target, 1);
165 template <class T, void (T::*callback)(Stream_event *)>
166 static void trampoline (SCM target, SCM event)
168 // The same, but for callbacks for translator listeners which get
169 // the unpacked event which, in turn, gets protected previously
171 T *t = unsmob<T> (target);
172 LY_ASSERT_SMOB (T, target, 1);
173 LY_ASSERT_SMOB (Stream_event, event, 2);
175 t->protect_event (event);
176 (t->*callback) (unsmob<Stream_event> (event));
179 Callback_wrapper (void (*trampoline) (SCM, SCM)) : trampoline_ (trampoline)
180 { } // Private constructor, use only in make_smob
182 static const char * const type_p_name_; // = 0
183 LY_DECLARE_SMOB_PROC (&Callback_wrapper::call, 2, 0, 0)
184 SCM call (SCM target, SCM ev)
186 trampoline_ (target, ev);
187 return SCM_UNSPECIFIED;
189 // Callback wrappers are for an unchanging entity, so we do the Lisp
190 // creation just once on the first call of make_smob. So we only
191 // get a single Callback_wrapper instance for each differently
192 // templated make_smob call.
193 template <class T, class Arg, void (T::*callback)(Arg)>
194 static SCM make_smob ()
196 static SCM res = scm_permanent_object
197 (Callback_wrapper (trampoline<T, callback>).smobbed_copy ());
202 #define GET_LISTENER(cl, proc) get_listener (Callback_wrapper::make_smob<cl, SCM, &cl::proc> ())
204 #endif /* LISTENER_HH */