1 <?xml version="1.0" encoding="utf-8"?>
2 <book xmlns="http://docbook.org/ns/docbook"
3 xmlns:xlink="http://www.w3.org/1999/xlink"
4 version="5.0" xml:id="index" xml:lang="en">
7 <title>libsigc++</title>
9 <firstname>Ainsley</firstname>
10 <surname>Pereira</surname>
11 </personname></author>
12 <date>September 2002</date>
13 <pubdate>September 2002. Updated January 2004 by Murray Cumming</pubdate>
15 <para>libsigc++ is a C++ template library implementing typesafe callbacks. This is an intro to libsigc++.</para>
19 <chapter xml:id="chapter-introduction">
20 <info><title>Introduction</title></info>
22 <section xml:id="sect-motivation">
23 <info><title>Motivation</title></info>
25 <para>There are many situations in which it is desirable to decouple code that
26 detects an event, and the code that deals with it. This is especially common in
27 GUI programming, where a toolkit might provide user interface elements such as
28 clickable buttons but, being a generic toolkit, doesn't know how an individual
29 application using that toolkit should handle the user clicking on it.</para>
31 <para>In C the callbacks are generally handled by the application calling a
32 'register' function and passing a pointer to a function and a <literal remap="tt">void*</literal>
36 void clicked(void* data);
38 button* okbutton = create_button("ok");
39 static char somedata[] = "This is some data I want the clicked() function to have";
41 register_click_handler(okbutton, clicked, somedata);
44 <para>When clicked, the toolkit will call <literal remap="tt">clicked()</literal> with the data pointer passed
45 to the <literal remap="tt">register_click_handler()</literal> function.</para>
47 <para>This works in C, but is not typesafe. There is no compile-time way of
48 ensuring that <literal remap="tt">clicked()</literal> isn't expecting a struct of some sort instead of a
49 <literal remap="tt">char*</literal>.</para>
51 <para>As C++ programmers, we want type safety. We also want to be able to use
52 things other than free-standing functions as callbacks.</para>
54 <para>libsigc++ provides the concept of a slot, which holds a reference to one of
55 the things that can be used as a callback:
57 <listitem><para>A free-standing function as in the example</para></listitem>
58 <listitem><para>A functor object that defines operator() (a lambda expression
59 is such an object)</para></listitem>
60 <listitem><para>A pointer-to-a-member-function and an instance of an object on which to invoke it (the
61 object should inherit from <literal remap="tt">sigc::trackable</literal>)</para></listitem>
62 </itemizedlist></para>
64 <para>All of which can take different numbers and types of arguments.</para>
66 <para>To make it easier to construct these, libsigc++ provides the sigc::ptr_fun() and sigc::mem_fun() functions, for creating slots from static functions and member functions, respectively. They return
67 a generic <literal remap="tt">signal::slot</literal> type that can be invoked with <literal remap="tt">emit()</literal> or <literal remap="tt">operator()</literal>.</para>
69 <para>For the other side of the fence, libsigc++ provides <literal remap="tt">signal</literal>s, to which the
70 client can attach <literal remap="tt">slot</literal>s. When the <literal remap="tt">signal</literal> is emitted, all the connected
71 <literal remap="tt">slot</literal>s are called.</para>
75 <chapter xml:id="chapter-connecting">
76 <info><title>Connecting your code to signals</title></info>
78 <section xml:id="sect-simple-ex">
79 <info><title>A simple example</title></info>
81 <para>So to get some experience, lets look at a simple example...</para>
83 <para>Lets say you and I are writing an application which informs the user when
84 aliens land in the car park. To keep the design nice and clean, and allow for
85 maximum portability to different interfaces, we decide to use libsigc++ to
86 split the project in two parts.</para>
88 <para>I will write the <literal remap="tt">AlienDetector</literal> class, and you will write the code to inform
89 the user. (Well, OK, I'll write both, but we're pretending, remember?)</para>
91 <para>Here's my class:</para>
101 sigc::signal<void()> signal_detected;
105 <para>(I'll explain the type of signal_detected later.)</para>
107 <para>Here's your code that uses it:</para>
112 std::cout << "There are aliens in the carpark!" << std::endl;
117 AlienDetector mydetector;
118 mydetector.signal_detected.connect( sigc::ptr_fun(warn_people) );
126 <para>You can use a lambda expression instead of sigc::ptr_fun().</para>
128 mydetector.signal_detected.connect( [](){ warn_people(); } );
131 <para>Pretty simple really - you call the <literal remap="tt">connect()</literal> method on the signal to
132 connect your function. <literal remap="tt">connect()</literal> takes a <literal remap="tt">slot</literal> parameter (remember slots
133 are capable of holding any type of callback), so you convert your
134 <literal remap="tt">warn_people()</literal> function to a slot using the <literal remap="tt">slot()</literal> function.</para>
136 <para>To compile this example, use:</para>
137 <programlisting>g++ example1.cc -o example1 `pkg-config --cflags --libs sigc++-2.0`</programlisting>
138 <para>Note that those `` characters are backticks, not single quotes. Run it with</para>
139 <programlisting>./example1</programlisting>
140 <para>(Try not to panic when the aliens land!)</para>
144 <section xml:id="sect-using-mem-func">
145 <info><title>Using a member function</title></info>
147 <para>Suppose you found a more sophisticated alien alerter class on the web,
151 class AlienAlerter : public sigc::trackable
154 AlienAlerter(char const* servername);
161 <para>(Handily it derives from <literal remap="tt">sigc::trackable</literal> already. This isn't quite so
162 unlikely as you might think; all appropriate bits of the popular gtkmm library do so,
165 <para>You could rewrite your code as follows:</para>
170 AlienDetector mydetector;
171 AlienAlerter myalerter("localhost"); // added
172 mydetector.signal_detected.connect( sigc::mem_fun(myalerter, &AlienAlerter::alert) ); // changed
180 <para>Note that only 2 lines are different - one to create an instance of the
181 class, and the line to connect the method to the signal.</para>
183 <para>This code is in example2.cc, which can be compiled in the same way as
186 <para>It's possible to use a lambda expression instead of sigc::mem_fun(),
187 but it's not recommended, if the class derives from <literal remap="tt">sigc::trackable</literal>.
188 With a lambda expression you would lose the automatic disconnection that the
189 combination of <literal remap="tt">sigc::trackable</literal> and sigc::mem_fun()
193 <section xml:id="sect-signals-with-pars">
194 <info><title>Signals with parameters</title></info>
196 <para>Functions taking no parameters and returning void are quite useful,
197 especially when they're members of classes that can store unlimited amounts of
198 safely typed data, but they're not sufficient for everything.</para>
200 <para>What if aliens don't land in the carpark, but somewhere else? Let's modify
201 the example so that the callback function takes a <literal remap="tt">std::string</literal> with the location
202 in which aliens were detected.</para>
204 <para>I change my class to:</para>
214 sigc::signal<void(std::string)> signal_detected; // changed
218 <para>The only line I had to change was the signal line (in <literal remap="tt">run()</literal> I need to change
219 my code to supply the argument when I emit the signal too, but that's not shown
222 <para>The name of the type is '<literal remap="tt">sigc::signal</literal>'.
223 The template parameters are the return type, then the argument types in parentheses.
224 (libsigc++2 also accepts a different syntax, with a comma between the return type
225 and the parameter types. That syntax is deprecated, though.)</para>
227 <para>The types in the function signature are in the same order as the template
228 parameters, eg:</para>
231 sigc::signal<void(std::string)>
232 void function(std::string foo);
235 <para>So now you can update your alerter (for simplicity, lets go back to the
236 free-standing function version):</para>
239 void warn_people(std::string where)
241 std::cout << "There are aliens in " << where << "!" << std::endl;
246 AlienDetector mydetector;
247 mydetector.signal_detected.connect( sigc::ptr_fun(warn_people) );
258 <section xml:id="sect-disconnecting">
259 <info><title>Disconnecting</title></info>
261 <para>If you decide you no longer want your code to be called whenever a signal is
262 emitted, you must remember the return value of <literal remap="tt">connect()</literal>, which we've been
263 ignoring until now.</para>
265 <para><literal remap="tt">connect()</literal> returns a <literal remap="tt">sigc::connection</literal> object, which has a <literal remap="tt">disconnect()</literal> member method. This does just what you think it does.</para>
270 <chapter xml:id="chapter-writing">
271 <info><title>Writing your own signals</title></info>
273 <section xml:id="sect-quick-recap">
274 <info><title>Quick recap</title></info>
276 <para>If all you want to do is use gtkmm, and connect your functionality to its
277 signals, you can probably stop reading here.</para>
279 <para>You might benefit from reading on anyway though, as this section is going to
280 be quite simple, and the 'Rebinding' technique from the next section is
281 occasionally useful.</para>
283 <para>We've already covered the way the types of signals are made up, but lets
286 <para>A signal is an instance of a template, named <literal remap="tt">sigc::signal</literal>.
287 The template arguments are the types,
288 in the order they appear in the function signature that can be connected to that
289 signal; that is the return type, then the argument types in parentheses.</para>
291 <para>To provide a signal for people to connect to, you must make available an
292 instance of that <literal remap="tt">sigc::signal</literal>. In <literal remap="tt">AlienDetector</literal> this was done
293 with a public data member. That's not considered good practice usually, so you
294 might want to consider making a member function that returns the signal by
295 reference. (This is what gtkmm does.)</para>
297 <para>Once you've done this, all you have to do is emit the signal when you're
298 ready. Look at the code for <literal remap="tt">AlienDetector::run()</literal>:</para>
301 void AlienDetector::run()
303 sleep(3); // wait for aliens
304 signal_detected.emit(); // panic!
308 <para>As a shortcut, <literal remap="tt">sigc::signal</literal> defines <literal remap="tt">operator()</literal> as a synonym for
309 <literal remap="tt">emit()</literal>, so you could just write <literal remap="tt">signal_detected();</literal> as in the second
310 example version:</para>
313 void AlienDetector::run()
315 sleep(3); // wait for aliens
316 signal_detected("the carpark"); // this is the std::string version, looks like
317 // they landed in the carpark after all.
322 <section xml:id="sect-return-values">
323 <info><title>What about return values?</title></info>
325 <para>If you only ever have one slot connected to a signal, or if you only care
326 about the return value of the last registered one, it's quite straightforward:</para>
329 sigc::signal<int()> somesignal;
332 a_return_value = somesignal.emit();
337 <chapter xml:id="chapter-advanced">
338 <info><title>Advanced topics</title></info>
340 <section xml:id="sect-rebinding">
341 <info><title>Rebinding</title></info>
343 <para>Suppose you already have a function that you want to be called when a
344 signal is emitted, but it takes the wrong argument types. For example, lets try
345 to attach the <literal remap="tt">warn_people(std::string)</literal> function to the detected signal
346 from the first example, which didn't supply a location string.</para>
348 <para>Just trying to connect it with:</para>
351 myaliendetector.signal_detected.connect(sigc::ptr_fun(warn_people));
354 <para>results in a compile-time error, because the types don't match. This is good!
355 This is typesafety at work. In the C way of doing things, this would have just
356 died at runtime after trying to print a random bit of memory as the location -
359 <para>We have to make up a location string, and bind it to the function, so that
360 when signal_detected is emitted with no arguments, something adds it in before
361 <literal remap="tt">warn_people</literal> is actually called.</para>
362 <para>We could write it ourselves - it's not hard:</para>
365 void warn_people_wrapper() // note this is the signature that 'signal_detected' expects
367 warn_people("the carpark");
371 <para>but after our first million or so we might start looking for a better way. As
372 it happens, libsigc++ has one.</para>
375 sigc::bind(slot, arg);
378 <para>binds arg as the argument to slot, and returns a new slot of the same return
379 type, but with one fewer arguments.</para>
381 <para>Now we can write:</para>
383 myaliendetector.signal_detected.connect(sigc::bind( sigc::ptr_fun(warn_people), "the carpark" ) );
386 <para>If the input slot has multiple args, the rightmost one is bound.</para>
388 <para>The return type can also be bound with <literal remap="tt">sigc::bind_return(slot, returnvalue);</literal> though
389 this is not so commonly useful.</para>
391 <para>So if we can attach the new <literal remap="tt">warn_people()</literal> to the old detector, can we attach
392 the old <literal remap="tt">warn_people</literal> (the one that didn't take an argument) to the new detector?</para>
394 <para>Of course, we just need to hide the extra argument. This can be done with
395 <literal remap="tt">sigc::hide</literal>, eg.</para>
398 myaliendetector.signal_detected.connect( sigc::hide<std::string>( sigc::ptr_fun(warn_people) ) );
401 <para>The template arguments are the types to hide (from the right only - you can't
402 hide the first argument of 3, for example, only the last).</para>
404 <para><literal remap="tt">sigc::hide_return</literal> effectively makes the return type void.</para>
407 <section xml:id="sect-retyping">
408 <info><title>Retyping</title></info>
410 <para>A similar topic is retyping. Perhaps you have a signal that takes an <literal remap="tt">int</literal>, but
411 you want to connect a function that takes a <literal remap="tt">double</literal>.</para>
413 <para>This can be achieved with the <literal remap="tt">sigc::retype()</literal> template.
414 It takes a <literal remap="tt">sigc::slot</literal>, and returns a <literal remap="tt">sigc::slot</literal>. eg.</para>
417 void dostuff(double foo)
421 sigc::signal<void(int)> asignal;
423 asignal.connect( sigc::retype( sigc::ptr_fun(&dostuff) ) );
426 <para>If you only want to change the return type, you can use <literal remap="tt">sigc::retype_return()</literal>.
427 <literal remap="tt">retype_return()</literal> needs one template argument, the new return type.</para>
431 <chapter xml:id="chapter-reference">
432 <info><title>Reference</title></info>
434 <para>See the reference documentation <link xlink:href="http://library.gnome.org/devel/libsigc++/2.10/">online</link></para>