ardour
signals.py
Go to the documentation of this file.
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)
def comma_separated
Definition: signals.py:41
def signal
Definition: signals.py:53