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!
31 print 'Syntax: %s <path>' % sys.argv[0]
34 f = open(sys.argv[1], 'w')
36 print >>f,"/** THIS FILE IS AUTOGENERATED by signals.py: CHANGES WILL BE LOST */\n\n"
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 = ""):
42 for i in range(0, len(n)):
45 r += "%s%s" % (prefix, n[i])
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
54 # The parameters in the form A1, A2, A3, ...
57 An.append("A%d" % (i + 1))
59 # The parameters in the form A1 a1, A2 a2, A3 a3, ...
62 Anan.append('%s %s' % (a, a.lower()))
64 # The parameters in the form a1, a2, a3, ...
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
75 typename = "typename "
78 print >>f,"template <%s>" % comma_separated(An, "typename ")
79 print >>f,"class Signal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An))
81 print >>f,"template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue<R> "], "typename ")
82 print >>f,"class Signal%d : public SignalBase" % n
88 print >>f,"\ttypedef boost::function<void(%s)> slot_function_type;" % comma_separated(An)
89 print >>f,"\ttypedef void result_type;"
91 print >>f,"\ttypedef boost::function<R(%s)> slot_function_type;" % comma_separated(An)
92 print >>f,"\ttypedef boost::optional<R> result_type;"
99 typedef std::map<boost::shared_ptr<Connection>, slot_function_type> Slots;
105 print >>f,"\t~Signal%d () {" % n
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
110 print >>f,"\t\t\ti->first->signal_going_away ();"
118 p = ", %s" % comma_separated(Anan)
119 q = ", %s" % comma_separated(an)
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
127 /** Arrange for @a slot to be executed whenever this signal is emitted.
128 Store the connection that represents this arrangement in @a c.
130 NOTE: @a slot will be executed in the same thread that the signal is
134 void connect_same_thread (ScopedConnection& c, const slot_function_type& slot) {
138 /** Arrange for @a slot to be executed whenever this signal is emitted.
139 Add the connection that represents this arrangement to @a clist.
141 NOTE: @a slot will be executed in the same thread that the signal is
145 void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) {
146 clist.add_connection (_connect (slot));
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.
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.
159 "outliving the lifetime" doesn't have a specific, detailed meaning,
160 but is best illustrated by two contrasting examples:
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
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.
174 void connect (ScopedConnectionList& clist,
175 PBD::EventLoop::InvalidationRecord* ir,
176 const slot_function_type& slot,
177 PBD::EventLoop* event_loop) {
180 ir->event_loop = event_loop;
184 for i in range(0, n):
185 u.append("_%d" % (i + 1))
190 p = ", %s" % comma_separated(u)
192 print >>f,"\t\tclist.add_connection (_connect (boost::bind (&compositor, slot, event_loop, ir%s)));" % p
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.
202 void connect (ScopedConnection& c,
203 PBD::EventLoop::InvalidationRecord* ir,
204 const slot_function_type& slot,
205 PBD::EventLoop* event_loop) {
208 ir->event_loop = event_loop;
211 print >>f,"\t\tc = _connect (boost::bind (&compositor, slot, event_loop, ir%s));" % p
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).
222 print >>f,"\tvoid operator() (%s)" % comma_separated(Anan)
224 print >>f,"\ttypename C::result_type operator() (%s)" % comma_separated(Anan)
226 print >>f,"\t\tSlots s;"
228 print >>f,"\t\t\tboost::mutex::scoped_lock lm (_mutex);"
229 print >>f,"\t\t\ts = _slots;"
232 print >>f,"\t\tstd::list<R> r;"
233 print >>f,"for (%sSlots::iterator i = s.begin(); i != s.end(); ++i) {" % typename
235 bool still_there = false;
237 boost::mutex::scoped_lock lm (_mutex);
238 still_there = _slots.find (i->first) != _slots.end ();
241 if (still_there) {"""
243 print >>f,"\t\t\t\t(i->second)(%s);" % comma_separated(an)
245 print >>f,"\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an)
250 print >>f,"\t\treturn c (r.begin(), r.end());"
255 boost::mutex::scoped_lock lm (_mutex);
256 return _slots.empty ();
261 tp = comma_separated(["void"] + An)
263 tp = comma_separated(["R"] + An + ["C"])
268 print >>f,"\tfriend class Connection;"
271 boost::shared_ptr<Connection> _connect (slot_function_type f)
273 boost::shared_ptr<Connection> c (new Connection (this));
274 boost::mutex::scoped_lock lm (_mutex);
281 void disconnect (boost::shared_ptr<Connection> c)
283 boost::mutex::scoped_lock lm (_mutex);
289 for i in range(0, 6):