]> git.donarmstrong.com Git - lilypond.git/blob - lily/include/listener.hh
Issue 4357/6: Extend Callback_wrapper class to extend for translator listening needs
[lilypond.git] / lily / include / listener.hh
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2005 Erik Sandberg <mandolaerik@gmail.com>
5
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.
10
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.
15
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/>.
18 */
19
20 #ifndef LISTENER_HH
21 #define LISTENER_HH
22
23 /*
24   Listeners
25
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.
30
31   To register a member function of Foo as an event handler in a
32   dispatcher, you must:
33
34   - declare the function:
35   class Foo
36   {
37     void method (SCM);
38     ...
39   };
40
41   - implement the method::
42   void Foo::method (SCM e)
43   {
44     write ("Foo hears an event!");
45   }
46
47   - Extract a listener using GET_LISTENER (Foo, method)
48   - Register the method to the dispatcher using Dispatcher::register
49
50   Example:
51
52   Foo *foo = (...);
53   Dispatcher *d = (...);
54   Listener l = foo->GET_LISTENER (Foo, method);
55   d->add_listener (l, ly_symbol2scm ("EventClass"));
56
57   Whenever d hears a stream-event ev of class "EventClass",
58   the implemented procedure is called.
59
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.
67
68   So (p->get_listener (f)).smobbed_copy () is roughly equivalent to
69   (lambda (ev) (f p->self_scm() ev))
70
71   Limitations:
72
73   The Callback_wrapper mechanism used in GET_LISTENER works only for
74   classes derived from Smob<...>.
75 */
76
77 #include "smobs.hh"
78 #include "stream-event.hh"
79
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
86 //
87 // (define (make-listener callback target)
88 //   (lambda (event) (callback target event)))
89 //
90 // The class construction is lightweight: as a Simple_smob, this is
91 // only converted into Scheme when a smobbed_copy is created.
92
93 class Listener : public Simple_smob<Listener>
94 {
95 private:
96   SCM callback_;
97   SCM target_;
98 public:
99   static const char type_p_name_[];
100
101   Listener (SCM callback, SCM target)
102     : callback_ (callback), target_ (target) { }
103
104   void listen (SCM ev) const { scm_call_2 (callback_, target_, ev); }
105
106   LY_DECLARE_SMOB_PROC (1, 0, 0, (SCM self, SCM ev))
107   {
108     Listener::unsmob (self)->listen (ev);
109     return SCM_UNSPECIFIED;
110   }
111
112   SCM mark_smob ()
113   {
114     scm_gc_mark (callback_);
115     return target_;
116   }
117
118   bool operator == (Listener const &other) const
119   {
120     return scm_is_eq (callback_, other.callback_)
121       && scm_is_eq (target_, other.target_);
122   }
123
124   static SCM equal_p (SCM a, SCM b)
125   {
126     return *Listener::unsmob (a) == *Listener::unsmob (b)
127       ? SCM_BOOL_T : SCM_BOOL_F;
128   }
129 };
130
131 // A callback wrapper creates a Scheme-callable version of a
132 // non-static class member function callback which you can call with a
133 // class instance and an event.
134 //
135 // If you have a class member function
136 // void T::my_listen (SCM ev)
137 // then Callback_wrapper::make_smob<T, SCM, &T::my_listen> ()
138 // will return an SCM function roughly defined as
139 // (lambda (target ev) (target->my_listen ev))
140 //
141 // The setup is slightly tricky since the make_smob quasi-constructor
142 // call is a template function templated on the given callback, and so
143 // is the trampoline it uses for redirecting the callback.  The class
144 // itself, however, is not templated as that would create a wagonload
145 // of SCM types.
146
147 class Callback_wrapper : public Simple_smob<Callback_wrapper>
148 {
149   // We use an ordinary function pointer pointing to a trampoline
150   // function (templated on the callback in question) instead of
151   // storing a member function pointer to a common base class like
152   // Smob_core.  The additional code for the trampolines is negligible
153   // and the performance implications of using member function
154   // pointers in connection with inheritance are somewhat opaque as
155   // this involves an adjustment of the this pointer from Smob_core to
156   // the scope containing the callback.
157   void (*trampoline_) (SCM, SCM);
158   template <class T, void (T::*callback)(SCM)>
159   static void trampoline (SCM target, SCM ev)
160   {
161     T *t = derived_unsmob<T> (target);
162     LY_ASSERT_DERIVED_SMOB (T, target, 1);
163
164     (t->*callback) (ev);
165   }
166   template <class T, void (T::*callback)(Stream_event *)>
167   static void trampoline (SCM target, SCM event)
168   {
169     // The same, but for callbacks for translator listeners which get
170     // the unpacked event which, in turn, gets protected previously
171
172     T *t = derived_unsmob<T> (target);
173     LY_ASSERT_DERIVED_SMOB (T, target, 1);
174     LY_ASSERT_SMOB (Stream_event, event, 2);
175
176     t->protect_event (event);
177     (t->*callback) (Stream_event::unsmob (event));
178   }
179
180   Callback_wrapper (void (*trampoline) (SCM, SCM)) : trampoline_ (trampoline)
181   { } // Private constructor, use only in make_smob
182 public:
183   LY_DECLARE_SMOB_PROC (2, 0, 0, (SCM self, SCM target, SCM ev))
184   {
185     unsmob (self)->trampoline_ (target, ev);
186     return SCM_UNSPECIFIED;
187   }
188   // Callback wrappers are for an unchanging entity, so we do the Lisp
189   // creation just once on the first call of make_smob.  So we only
190   // get a single Callback_wrapper instance for each differently
191   // templated make_smob call.
192   template <class T, class Arg, void (T::*callback)(Arg)>
193   static SCM make_smob ()
194   {
195     static SCM res = scm_permanent_object
196       (Callback_wrapper (trampoline<T, callback>).smobbed_copy ());
197     return res;
198   }
199 };
200
201 #define GET_LISTENER(cl, proc) get_listener (Callback_wrapper::make_smob<cl, SCM, &cl::proc> ())
202
203 #endif /* LISTENER_HH */