3 # Copyright (C) 2009-2012 Paul Davis
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.
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.
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.
21 # This file generates the header signals_generated.h, which
22 # will be put in build/libs/pbd/pbd by waf.
24 # It is probably easier to read build/libs/pbd/pbd/signals_generated.h
25 # than this if you want to read the code!
28 from __future__ import print_function
32 print('Syntax: %s <path>' % sys.argv[0])
35 f = open(sys.argv[1], 'w')
37 print("/** THIS FILE IS AUTOGENERATED by signals.py: CHANGES WILL BE LOST */\n", file=f)
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 = ""):
43 for i in range(0, len(n)):
46 r += "%s%s" % (prefix, n[i])
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
55 # The parameters in the form A1, A2, A3, ...
58 An.append("A%d" % (i + 1))
60 # The parameters in the form A1 a1, A2 a2, A3 a3, ...
63 Anan.append('%s %s' % (a, a.lower()))
65 # The parameters in the form a1, a2, a3, ...
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
76 typename = "typename "
79 print("/** A signal with %d parameters (specialisation for a void return) */" % n, file=f)
81 print("/** A signal with %d parameters */" % n, file=f)
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)
86 print("template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue<R> "], "typename "), file=f)
87 print("class Signal%d : public SignalBase" % n, file=f)
90 print("public:", file=f)
93 print("\ttypedef boost::function<void(%s)> slot_function_type;" % comma_separated(An), file=f)
94 print("\ttypedef void result_type;", file=f)
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)
101 print("private:", file=f)
104 /** The slots that this signal will call on emission */
105 typedef std::map<boost::shared_ptr<Connection>, slot_function_type> Slots;
109 print("public:", file=f)
111 print("\t~Signal%d () {" % n, file=f)
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::iterator i = _slots.begin(); i != _slots.end(); ++i) {" % typename, file=f)
117 print("\t\t\ti->first->signal_going_away ();", file=f)
118 print("\t\t}", file=f)
126 p = ", %s" % comma_separated(Anan)
127 q = ", %s" % comma_separated(an)
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)
134 /** Arrange for @a slot to be executed whenever this signal is emitted.
135 Store the connection that represents this arrangement in @a c.
137 NOTE: @a slot will be executed in the same thread that the signal is
141 void connect_same_thread (ScopedConnection& c, const slot_function_type& slot) {
145 /** Arrange for @a slot to be executed whenever this signal is emitted.
146 Add the connection that represents this arrangement to @a clist.
148 NOTE: @a slot will be executed in the same thread that the signal is
152 void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) {
153 clist.add_connection (_connect (slot));
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.
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.
166 "outliving the lifetime" doesn't have a specific, detailed meaning,
167 but is best illustrated by two contrasting examples:
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
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.
181 void connect (ScopedConnectionList& clist,
182 PBD::EventLoop::InvalidationRecord* ir,
183 const slot_function_type& slot,
184 PBD::EventLoop* event_loop) {
187 ir->event_loop = event_loop;
191 for i in range(0, n):
192 u.append("_%d" % (i + 1))
197 p = ", %s" % comma_separated(u)
199 print("\t\tclist.add_connection (_connect (boost::bind (&compositor, slot, event_loop, ir%s)));" % p, file=f)
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.
209 void connect (ScopedConnection& c,
210 PBD::EventLoop::InvalidationRecord* ir,
211 const slot_function_type& slot,
212 PBD::EventLoop* event_loop) {
215 ir->event_loop = event_loop;
218 print("\t\tc = _connect (boost::bind (&compositor, slot, event_loop, ir%s));" % p, file=f)
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).
229 print("\tvoid operator() (%s)" % comma_separated(Anan), file=f)
231 print("\ttypename C::result_type operator() (%s)" % comma_separated(Anan), file=f)
233 print("\t\t/* First, take a copy of our list of slots as it is now */", 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)
242 print("\t\tstd::list<R> r;", file=f)
243 print("\t\tfor (%sSlots::iterator i = s.begin(); i != s.end(); ++i) {" % typename, file=f)
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.
250 bool still_there = false;
252 Glib::Threads::Mutex::Lock lm (_mutex);
253 still_there = _slots.find (i->first) != _slots.end ();
256 if (still_there) {""", file=f)
258 print("\t\t\t\t(i->second)(%s);" % comma_separated(an), file=f)
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)
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)
272 Glib::Threads::Mutex::Lock lm (_mutex);
273 return _slots.empty ();
278 tp = comma_separated(["void"] + An)
280 tp = comma_separated(["R"] + An + ["C"])
282 print("private:", file=f)
284 print("\tfriend class Connection;", file=f)
287 boost::shared_ptr<Connection> _connect (slot_function_type f)
289 #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
290 if (_debug_connection) {
291 PBD::stacktrace (std::cerr, 10);
294 boost::shared_ptr<Connection> c (new Connection (this));
295 Glib::Threads::Mutex::Lock lm (_mutex);
301 void disconnect (boost::shared_ptr<Connection> c)
303 Glib::Threads::Mutex::Lock lm (_mutex);
309 for i in range(0, 6):