]> git.donarmstrong.com Git - lilypond.git/blob - lily/include/listener.hh
Merge branch 'issue4357' into HEAD
[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
79 // A listener is essentially any procedure accepting a single argument
80 // (namely an event).  The class Listener (or rather a smobbed_copy of
81 // it) behaves like such a procedure and is composed of a generic
82 // callback function accepting two arguments, namely a "target"
83 // (usually an engraver instance) and the event.  Its Scheme
84 // equivalent would be
85 //
86 // (define (make-listener callback target)
87 //   (lambda (event) (callback target event)))
88 //
89 // The class construction is lightweight: as a Simple_smob, this is
90 // only converted into Scheme when a smobbed_copy is created.
91
92 class Listener : public Simple_smob<Listener>
93 {
94 private:
95   SCM callback_;
96   SCM target_;
97 public:
98   static const char type_p_name_[];
99
100   Listener (SCM callback, SCM target)
101     : callback_ (callback), target_ (target) { }
102
103   void listen (SCM ev) const { scm_call_2 (callback_, target_, ev); }
104
105   LY_DECLARE_SMOB_PROC (1, 0, 0, (SCM self, SCM ev))
106   {
107     Listener::unsmob (self)->listen (ev);
108     return SCM_UNSPECIFIED;
109   }
110
111   SCM mark_smob ()
112   {
113     scm_gc_mark (callback_);
114     return target_;
115   }
116
117   bool operator == (Listener const &other) const
118   {
119     return scm_is_eq (callback_, other.callback_)
120       && scm_is_eq (target_, other.target_);
121   }
122
123   static SCM equal_p (SCM a, SCM b)
124   {
125     return *Listener::unsmob (a) == *Listener::unsmob (b)
126       ? SCM_BOOL_T : SCM_BOOL_F;
127   }
128 };
129
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.
133 //
134 // If you have a class member function
135 // void T::my_listen (SCM ev)
136 // then Callback_wrapper::make_smob<&T::my_listen> ()
137 // will return an SCM function roughly defined as
138 // (lambda (target ev) (target->my_listen ev))
139 //
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
144 // of SCM types.
145
146 class Callback_wrapper : public Simple_smob<Callback_wrapper>
147 {
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)
159   {
160     T *t = derived_unsmob<T> (target);
161     LY_ASSERT_DERIVED_SMOB (T, target, 1);
162
163     (t->*callback) (ev);
164   }
165
166   Callback_wrapper (void (*trampoline) (SCM, SCM)) : trampoline_ (trampoline)
167   { } // Private constructor, use only in make_smob
168 public:
169   LY_DECLARE_SMOB_PROC (2, 0, 0, (SCM self, SCM target, SCM ev))
170   {
171     unsmob (self)->trampoline_ (target, ev);
172     return SCM_UNSPECIFIED;
173   }
174   // Callback wrappers are for an unchanging entity, so we do the Lisp
175   // creation just once on the first call of make_smob.  So we only
176   // get a single Callback_wrapper instance for each differently
177   // templated make_smob call.
178   template <class T, void (T::*callback)(SCM)>
179   static SCM make_smob ()
180   {
181     static SCM res = scm_permanent_object
182       (Callback_wrapper (trampoline<T, callback>).smobbed_copy ());
183     return res;
184   }
185 };
186
187 #define GET_LISTENER(cl, proc) get_listener (Callback_wrapper::make_smob<cl, &cl::proc> ())
188
189 #endif /* LISTENER_HH */