Maybe fix typename / no-typename problems better.
[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 import sys
29
30 if len(sys.argv) < 2:
31     print 'Syntax: %s <path>' % sys.argv[0]
32     sys.exit(1)
33
34 f = open(sys.argv[1], 'w')
35
36 print >>f,"/** THIS FILE IS AUTOGENERATED by signals.py: CHANGES WILL BE LOST */\n\n"
37
38 # Produce a comma-separated string from a list of substrings,
39 # giving an optional prefix to each substring
40 def comma_separated(n, prefix = ""):
41     r = ""
42     for i in range(0, len(n)):
43         if i > 0:
44             r += ", "
45         r += "%s%s" % (prefix, n[i])
46     return r
47
48 # Generate one SignalN class definition
49 # @param f File to write to
50 # @param n Number of parameters
51 # @param v True to specialize the template for a void return type
52 def signal(f, n, v):
53
54     # The parameters in the form A1, A2, A3, ...
55     An = []
56     for i in range(0, n):
57         An.append("A%d" % (i + 1))
58
59     # The parameters in the form A1 a1, A2 a2, A3 a3, ...
60     Anan = []
61     for a in An:
62         Anan.append('%s %s' % (a, a.lower()))
63
64     # The parameters in the form a1, a2, a3, ...
65     an = []
66     for a in An:
67         an.append(a.lower())
68
69     # If the template is fully specialized, use of typename SomeTypedef::iterator is illegal
70     # in c++03 (should use just SomeTypedef::iterator) [although use of typename is ok in c++0x]
71     # http://stackoverflow.com/questions/6076015/typename-outside-of-template
72     if n == 0 and v:
73         typename = ""
74     else:
75         typename = "typename "
76
77     if v:
78         print >>f,"template <%s>" % comma_separated(An, "typename ")
79         print >>f,"class Signal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An))
80     else:
81         print >>f,"template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue<R> "], "typename ")
82         print >>f,"class Signal%d : public SignalBase" % n
83
84     print >>f,"{"
85     print >>f,"public:"
86     print >>f,""
87     if v:
88         print >>f,"\ttypedef boost::function<void(%s)> slot_function_type;" % comma_separated(An)
89         print >>f,"\ttypedef void result_type;"
90     else:
91         print >>f,"\ttypedef boost::function<R(%s)> slot_function_type;" % comma_separated(An)
92         print >>f,"\ttypedef boost::optional<R> result_type;"
93
94     print >>f,""
95
96     print >>f,"private:"
97
98     print >>f,"""
99         typedef std::map<boost::shared_ptr<Connection>, slot_function_type> Slots;
100         Slots _slots;
101 """
102
103     print >>f,"public:"
104     print >>f,""
105     print >>f,"\t~Signal%d () {" % n
106
107     print >>f,"\t\tboost::mutex::scoped_lock lm (_mutex);"
108     print >>f,"\t\tfor (%sSlots::iterator i = _slots.begin(); i != _slots.end(); ++i) {" % typename
109
110     print >>f,"\t\t\ti->first->signal_going_away ();"
111     print >>f,"\t\t}"
112     print >>f,"\t}"
113
114     if n == 0:
115         p = ""
116         q = ""
117     else:
118         p = ", %s" % comma_separated(Anan)
119         q = ", %s" % comma_separated(an)
120     
121     print >>f,"\tstatic void compositor (%sboost::function<void(%s)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir%s) {" % (typename, comma_separated(An), p)
122     print >>f,"\t\tevent_loop->call_slot (ir, boost::bind (f%s));" % q
123     print >>f,"\t}"
124
125     print >>f,"""
126
127         /** Arrange for @a slot to be executed whenever this signal is emitted. 
128             Store the connection that represents this arrangement in @a c.
129
130             NOTE: @a slot will be executed in the same thread that the signal is
131             emitted in.
132         */
133
134         void connect_same_thread (ScopedConnection& c, const slot_function_type& slot) {
135                 c = _connect (slot);
136         }
137
138         /** Arrange for @a slot to be executed whenever this signal is emitted. 
139             Add the connection that represents this arrangement to @a clist.
140
141             NOTE: @a slot will be executed in the same thread that the signal is
142             emitted in.
143         */
144         
145         void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) {
146                 clist.add_connection (_connect (slot));
147         }
148
149         /** Arrange for @a slot to be executed in the context of @a event_loop
150             whenever this signal is emitted. Add the connection that represents
151             this arrangement to @a clist.
152         
153             If the event loop/thread in which @a slot will be executed will
154             outlive the lifetime of any object referenced in @a slot,
155             then an InvalidationRecord should be passed, allowing
156             any request sent to the @a event_loop and not executed
157             before the object is destroyed to be marked invalid.
158         
159             "outliving the lifetime" doesn't have a specific, detailed meaning,
160             but is best illustrated by two contrasting examples:
161         
162             1) the main GUI event loop/thread - this will outlive more or 
163             less all objects in the application, and thus when arranging for
164             @a slot to be called in that context, an invalidation record is 
165             highly advisable.
166         
167             2) a secondary event loop/thread which will be destroyed along
168             with the objects that are typically referenced by @a slot.
169             Assuming that the event loop is stopped before the objects are
170             destroyed, there is no reason to pass in an invalidation record,
171             and MISSING_INVALIDATOR may be used.
172         */
173
174         void connect (ScopedConnectionList& clist, 
175                       PBD::EventLoop::InvalidationRecord* ir, 
176                       const slot_function_type& slot,
177                       PBD::EventLoop* event_loop) {
178
179                 if (ir) {
180                         ir->event_loop = event_loop;
181                 }
182 """
183     u = []
184     for i in range(0, n):
185         u.append("_%d" % (i + 1))
186
187     if n == 0:
188         p = ""
189     else:
190         p = ", %s" % comma_separated(u)
191
192     print >>f,"\t\tclist.add_connection (_connect (boost::bind (&compositor, slot, event_loop, ir%s)));" % p
193
194     print >>f,"""
195         }
196
197         /** See notes for the ScopedConnectionList variant of this function. This
198         * differs in that it stores the connection to the signal in a single
199         * ScopedConnection rather than a ScopedConnectionList.
200         */
201
202         void connect (ScopedConnection& c, 
203                       PBD::EventLoop::InvalidationRecord* ir, 
204                       const slot_function_type& slot,
205                       PBD::EventLoop* event_loop) {
206
207                 if (ir) {
208                         ir->event_loop = event_loop;
209                 }
210 """
211     print >>f,"\t\tc = _connect (boost::bind (&compositor, slot, event_loop, ir%s));" % p
212     print >>f,"\t}"
213
214     print >>f,"""
215         /** Emit this signal. This will cause all slots connected to it be executed
216             in the order that they were connected (cross-thread issues may alter
217             the precise execution time of cross-thread slots).
218         */
219 """
220
221     if v:
222         print >>f,"\tvoid operator() (%s)" % comma_separated(Anan)
223     else:
224         print >>f,"\ttypename C::result_type operator() (%s)" % comma_separated(Anan)
225     print >>f,"\t{"
226     print >>f,"\t\tSlots s;"
227     print >>f,"\t\t{"
228     print >>f,"\t\t\tboost::mutex::scoped_lock lm (_mutex);"
229     print >>f,"\t\t\ts = _slots;"
230     print >>f,"\t\t}"
231     if not v:
232         print >>f,"\t\tstd::list<R> r;"
233     print >>f,"for (%sSlots::iterator i = s.begin(); i != s.end(); ++i) {" % typename
234     print >>f,"""
235                         bool still_there = false;
236                         {
237                                 boost::mutex::scoped_lock lm (_mutex);
238                                 still_there = _slots.find (i->first) != _slots.end ();
239                         }
240
241                         if (still_there) {"""
242     if v:
243         print >>f,"\t\t\t\t(i->second)(%s);" % comma_separated(an)
244     else:
245         print >>f,"\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an)
246     print >>f,"\t\t\t}"
247     print >>f,"\t\t}"
248     if not v:
249         print >>f,"\t\tC c;"
250         print >>f,"\t\treturn c (r.begin(), r.end());"
251     print >>f,"\t}"
252
253     print >>f,"""
254         bool empty () {
255                 boost::mutex::scoped_lock lm (_mutex);
256                 return _slots.empty ();
257         }
258 """
259
260     if v:
261         tp = comma_separated(["void"] + An)
262     else:
263         tp = comma_separated(["R"] + An + ["C"])
264
265     print >>f,""
266     print >>f,"private:"
267     print >>f,""
268     print >>f,"\tfriend class Connection;"
269
270     print >>f,"""
271         boost::shared_ptr<Connection> _connect (slot_function_type f)
272         {
273                 boost::shared_ptr<Connection> c (new Connection (this));
274                 boost::mutex::scoped_lock lm (_mutex);
275                 _slots[c] = f;
276                 return c;
277         }
278 """
279
280     print >>f,"""
281         void disconnect (boost::shared_ptr<Connection> c)
282         {
283                 boost::mutex::scoped_lock lm (_mutex);
284                 _slots.erase (c);
285         }
286 };    
287 """
288
289 for i in range(0, 6):
290     signal(f, i, False)
291     signal(f, i, True)