]> git.donarmstrong.com Git - lilypond.git/blob - lily/include/listener.hh
Doc: NR add @knownissues for Percent Repeats
[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   LY_DECLARE_SMOB_PROC (&Listener::listen, 1, 0, 0)
105   SCM listen (SCM ev)
106   {
107     scm_call_2 (callback_, target_, ev);
108     return SCM_UNSPECIFIED;
109   }
110
111   SCM mark_smob () const
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 *unchecked_unsmob (a) == *unchecked_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, SCM, &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 = unsmob<T> (target);
161     LY_ASSERT_SMOB (T, target, 1);
162
163     (t->*callback) (ev);
164   }
165   template <class T, void (T::*callback)(Stream_event *)>
166   static void trampoline (SCM target, SCM event)
167   {
168     // The same, but for callbacks for translator listeners which get
169     // the unpacked event which, in turn, gets protected previously
170
171     T *t = unsmob<T> (target);
172     LY_ASSERT_SMOB (T, target, 1);
173     LY_ASSERT_SMOB (Stream_event, event, 2);
174
175     t->protect_event (event);
176     (t->*callback) (unsmob<Stream_event> (event));
177   }
178
179   Callback_wrapper (void (*trampoline) (SCM, SCM)) : trampoline_ (trampoline)
180   { } // Private constructor, use only in make_smob
181 public:
182   LY_DECLARE_SMOB_PROC (&Callback_wrapper::call, 2, 0, 0)
183   SCM call (SCM target, SCM ev)
184   {
185     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 */