const iterators
[ardour.git] / libs / pbd / pbd / signals.py
1 #!/usr/bin/python
2 #
3 #   Copyright (C) 2009-2012 Paul Davis 
4 #
5 #   This program is free software; you can redistribute it and/or modify
6 #   it under the terms of the GNU General Public License as published by
7 #   the Free Software Foundation; either version 2 of the License, or
8 #   (at your option) any later version.
9 #
10 #   This program is distributed in the hope that it will be useful,
11 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #   GNU General Public License for more details.
14 #
15 #   You should have received a copy of the GNU General Public License
16 #   along with this program; if not, write to the Free Software
17 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 #
19
20 #
21 # This file generates the header signals_generated.h, which
22 # will be put in build/libs/pbd/pbd by waf.
23 #
24 # It is probably easier to read build/libs/pbd/pbd/signals_generated.h
25 # than this if you want to read the code!
26 #
27
28 from __future__ import print_function
29 import sys
30
31 if len(sys.argv) < 2:
32     print('Syntax: %s <path>' % sys.argv[0])
33     sys.exit(1)
34
35 f = open(sys.argv[1], 'w')
36
37 print("/** THIS FILE IS AUTOGENERATED by signals.py: CHANGES WILL BE LOST */\n", file=f)
38
39 # Produce a comma-separated string from a list of substrings,
40 # giving an optional prefix to each substring
41 def comma_separated(n, prefix = ""):
42     r = ""
43     for i in range(0, len(n)):
44         if i > 0:
45             r += ", "
46         r += "%s%s" % (prefix, n[i])
47     return r
48
49 # Generate one SignalN class definition
50 # @param f File to write to
51 # @param n Number of parameters
52 # @param v True to specialize the template for a void return type
53 def signal(f, n, v):
54
55     # The parameters in the form A1, A2, A3, ...
56     An = []
57     for i in range(0, n):
58         An.append("A%d" % (i + 1))
59
60     # The parameters in the form A1 a1, A2 a2, A3 a3, ...
61     Anan = []
62     for a in An:
63         Anan.append('%s %s' % (a, a.lower()))
64
65     # The parameters in the form a1, a2, a3, ...
66     an = []
67     for a in An:
68         an.append(a.lower())
69
70     # If the template is fully specialized, use of typename SomeTypedef::iterator is illegal
71     # in c++03 (should use just SomeTypedef::iterator) [although use of typename is ok in c++0x]
72     # http://stackoverflow.com/questions/6076015/typename-outside-of-template
73     if n == 0 and v:
74         typename = ""
75     else:
76         typename = "typename "
77
78     if v:
79         print("/** A signal with %d parameters (specialisation for a void return) */" % n, file=f)
80     else:
81         print("/** A signal with %d parameters */" % n, file=f)
82     if v:
83         print("template <%s>" % comma_separated(An, "typename "), file=f)
84         print("class Signal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An)), file=f)
85     else:
86         print("template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue<R> "], "typename "), file=f)
87         print("class Signal%d : public SignalBase" % n, file=f)
88
89     print("{", file=f)
90     print("public:", file=f)
91     print("", file=f)
92     if v:
93         print("\ttypedef boost::function<void(%s)> slot_function_type;" % comma_separated(An), file=f)
94         print("\ttypedef void result_type;", file=f)
95     else:
96         print("\ttypedef boost::function<R(%s)> slot_function_type;" % comma_separated(An), file=f)
97         print("\ttypedef boost::optional<R> result_type;", file=f)
98
99     print("", file=f)
100
101     print("private:", file=f)
102
103     print("""
104         /** The slots that this signal will call on emission */
105         typedef std::map<boost::shared_ptr<Connection>, slot_function_type> Slots;
106         Slots _slots;
107 """, file=f)
108
109     print("public:", file=f)
110     print("", file=f)
111     print("\t~Signal%d () {" % n, file=f)
112
113     print("\t\tGlib::Threads::Mutex::Lock lm (_mutex);", file=f)
114     print("\t\t/* Tell our connection objects that we are going away, so they don't try to call us */", file=f)
115     print("\t\tfor (%sSlots::const_iterator i = _slots.begin(); i != _slots.end(); ++i) {" % typename, file=f)
116
117     print("\t\t\ti->first->signal_going_away ();", file=f)
118     print("\t\t}", file=f)
119     print("\t}", file=f)
120     print("", file=f)
121
122     if n == 0:
123         p = ""
124         q = ""
125     else:
126         p = ", %s" % comma_separated(Anan)
127         q = ", %s" % comma_separated(an)
128     
129     print("\tstatic void compositor (%sboost::function<void(%s)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir%s) {" % (typename, comma_separated(An), p), file=f)
130     print("\t\tevent_loop->call_slot (ir, boost::bind (f%s));" % q, file=f)
131     print("\t}", file=f)
132
133     print("""
134         /** Arrange for @a slot to be executed whenever this signal is emitted. 
135             Store the connection that represents this arrangement in @a c.
136
137             NOTE: @a slot will be executed in the same thread that the signal is
138             emitted in.
139         */
140
141         void connect_same_thread (ScopedConnection& c, const slot_function_type& slot) {
142                 c = _connect (slot);
143         }
144
145         /** Arrange for @a slot to be executed whenever this signal is emitted. 
146             Add the connection that represents this arrangement to @a clist.
147
148             NOTE: @a slot will be executed in the same thread that the signal is
149             emitted in.
150         */
151         
152         void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) {
153                 clist.add_connection (_connect (slot));
154         }
155
156         /** Arrange for @a slot to be executed in the context of @a event_loop
157             whenever this signal is emitted. Add the connection that represents
158             this arrangement to @a clist.
159         
160             If the event loop/thread in which @a slot will be executed will
161             outlive the lifetime of any object referenced in @a slot,
162             then an InvalidationRecord should be passed, allowing
163             any request sent to the @a event_loop and not executed
164             before the object is destroyed to be marked invalid.
165         
166             "outliving the lifetime" doesn't have a specific, detailed meaning,
167             but is best illustrated by two contrasting examples:
168         
169             1) the main GUI event loop/thread - this will outlive more or 
170             less all objects in the application, and thus when arranging for
171             @a slot to be called in that context, an invalidation record is 
172             highly advisable.
173         
174             2) a secondary event loop/thread which will be destroyed along
175             with the objects that are typically referenced by @a slot.
176             Assuming that the event loop is stopped before the objects are
177             destroyed, there is no reason to pass in an invalidation record,
178             and MISSING_INVALIDATOR may be used.
179         */
180
181         void connect (ScopedConnectionList& clist, 
182                       PBD::EventLoop::InvalidationRecord* ir, 
183                       const slot_function_type& slot,
184                       PBD::EventLoop* event_loop) {
185
186                 if (ir) {
187                         ir->event_loop = event_loop;
188                 }
189 """, file=f)
190     u = []
191     for i in range(0, n):
192         u.append("_%d" % (i + 1))
193
194     if n == 0:
195         p = ""
196     else:
197         p = ", %s" % comma_separated(u)
198
199     print("\t\tclist.add_connection (_connect (boost::bind (&compositor, slot, event_loop, ir%s)));" % p, file=f)
200
201     print("""
202         }
203
204         /** See notes for the ScopedConnectionList variant of this function. This
205          *  differs in that it stores the connection to the signal in a single
206          *  ScopedConnection rather than a ScopedConnectionList.
207          */
208
209         void connect (ScopedConnection& c, 
210                       PBD::EventLoop::InvalidationRecord* ir, 
211                       const slot_function_type& slot,
212                       PBD::EventLoop* event_loop) {
213
214                 if (ir) {
215                         ir->event_loop = event_loop;
216                 }
217 """, file=f)
218     print("\t\tc = _connect (boost::bind (&compositor, slot, event_loop, ir%s));" % p, file=f)
219     print("\t}", file=f)
220
221     print("""
222         /** Emit this signal. This will cause all slots connected to it be executed
223             in the order that they were connected (cross-thread issues may alter
224             the precise execution time of cross-thread slots).
225         */
226 """, file=f)
227
228     if v:
229         print("\tvoid operator() (%s)" % comma_separated(Anan), file=f)
230     else:
231         print("\ttypename C::result_type operator() (%s)" % comma_separated(Anan), file=f)
232     print("\t{", file=f)
233     print("\t\t/* First, take a copy of our list of slots as it is now */", file=f)
234     print("", file=f)
235     print("\t\tSlots s;", file=f)
236     print("\t\t{", file=f)
237     print("\t\t\tGlib::Threads::Mutex::Lock lm (_mutex);", file=f)
238     print("\t\t\ts = _slots;", file=f)
239     print("\t\t}", file=f)
240     print("", file=f)
241     if not v:
242         print("\t\tstd::list<R> r;", file=f)
243     print("\t\tfor (%sSlots::const_iterator i = s.begin(); i != s.end(); ++i) {" % typename, file=f)
244     print("""
245                         /* We may have just called a slot, and this may have resulted in
246                            disconnection of other slots from us.  The list copy means that
247                            this won't cause any problems with invalidated iterators, but we
248                            must check to see if the slot we are about to call is still on the list.
249                         */
250                         bool still_there = false;
251                         {
252                                 Glib::Threads::Mutex::Lock lm (_mutex);
253                                 still_there = _slots.find (i->first) != _slots.end ();
254                         }
255
256                         if (still_there) {""", file=f)
257     if v:
258         print("\t\t\t\t(i->second)(%s);" % comma_separated(an), file=f)
259     else:
260         print("\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an), file=f)
261     print("\t\t\t}", file=f)
262     print("\t\t}", file=f)
263     print("", file=f)
264     if not v:
265         print("\t\t/* Call our combiner to do whatever is required to the result values */", file=f)
266         print("\t\tC c;", file=f)
267         print("\t\treturn c (r.begin(), r.end());", file=f)
268     print("\t}", file=f)
269
270     print("""
271         bool empty () {
272                 Glib::Threads::Mutex::Lock lm (_mutex);
273                 return _slots.empty ();
274         }
275 """, file=f)
276
277     if v:
278         tp = comma_separated(["void"] + An)
279     else:
280         tp = comma_separated(["R"] + An + ["C"])
281
282     print("private:", file=f)
283     print("", file=f)
284     print("\tfriend class Connection;", file=f)
285
286     print("""
287         boost::shared_ptr<Connection> _connect (slot_function_type f)
288         {
289 #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
290                 if (_debug_connection) {
291                         PBD::stacktrace (std::cerr, 10);
292                 }
293 #endif
294                 boost::shared_ptr<Connection> c (new Connection (this));
295                 Glib::Threads::Mutex::Lock lm (_mutex);
296                 _slots[c] = f;
297                 return c;
298         }""", file=f)
299
300     print("""
301         void disconnect (boost::shared_ptr<Connection> c)
302         {
303                 Glib::Threads::Mutex::Lock lm (_mutex);
304                 _slots.erase (c);
305         }
306 };    
307 """, file=f)
308
309 for i in range(0, 6):
310     signal(f, i, False)
311     signal(f, i, True)