libircpp manual

Welcome to the libircpp manual.
  1. Introduction
  2. Hello World
  3. Compiling and installing
  4. Signals
  5. Example: op-me bot
  6. Messages
  7. Users and Channels
  8. User and Channel Lists
  9. Example: Channel Logger
  10. Planned features

4. Signals

What's all this about Boost signals anyway?

Boost Signals is a library that implements a programming construct called "Signals and Slots". Conceptually, you can create a signal, and connect that signal to any number of slots. When you "emit" that signal, all the slots (which are actually just functions), get called.

Why is it needed?

Well, IRC is an asynchronous protocol. If you send a JOIN command, other messages may be received before the JOIN reply comes back - or it may not come back at all - you might be banned, for example, and get an error reply. So, normal function calls are not very useful, as a join() function would have to wait for a response before it could return a status value. Meanwhile, other messages might arrive - and the response might never come. This would lead to, at best, a laggy program which can't process a string of messages while it waits for some particular response, and at worst a program that hangs.

The normal way to deal with this is to use "Callback functions". You would tell libircpp about a function in your program that it can call when the JOIN reply arrives. Then, you call the join() function, which returns immediately. When the JOIN reply arrives, libircpp will "call back" the function you told it about. In the meantime, it is free to call other callback functions for any other messages that arrive.

Unfortunately, callback functions do not work so well in C++, because objects' member functions are shared between all objects belonging to that class. If libircpp just called that member function, the class would have no idea which object the call was for.

Boost signals solves this problem. libircpp emits signals for certain events, for example when a channel is joined, and you may connect these signals to any number of slots in your program, which may be member functions, or normal functions.

Signals have parameters and return values, which must match the functions they are connected to. For example, the session object's sig_join_msg signal, emitted when a channel is joined, has one parameter - a message object - and a return type of void. So to connect to it, you would need to create a function like this:

void channel_joined(const message& msg);
And then connect the signal to it:
mysession->sig_join_msg.connect(channel_joined);
Then, every time a JOIN message is received, your function channel_joined will be called.

Signals in libircpp

Libircpp uses signals for all messages, and some other events - for example the session object has a sig_connected signal that is emitted when the session successfully connects to an IRC server.

Here are some other interesting messages emitted by the session object:

sig_connectedEmitted when the session object connects to an IRC server
sig_disconnectedEmitted when the session object disconnects from an IRC server
sig_join_msgEmitted when a JOIN message is received (us, or someone else joining a channel)
sig_privmsg_msgEmitted when we receive a channel or private message
sig_kick_msgEmitted when someone is kicked from a channel

In the next section, we'll see how to use these signals, by creating a simple bot that can op you on demand.

Signals and member functions

In the example above, we connected a signal to an ordinary global function called channel_joined:
mysession->sig_join_msg.connect(channel_joined);
You can also connect signals to objects' member functions, but the syntax is slightly more complicated:
mysession->sig_join_msg.connect(boost::bind(&myclass::my_member_function, &myobject, _1));
You can see that we have replaced the function name "channel_joined" with the boost::bind macro:
boost::bind(&myclass::my_member_function, &myobject, _1)
boost::bind packages up an object's member function so the signal can use it. It takes the following arguments:

So, if your class was called farmer, and you had made a farmer object called fred, with a member function called notice_handler which you wanted to connect to sig_notice_msg, which has one parameter, you would use the following invocation of boost_bind:
boost::bind(&farmer::notice_handler, &fred, _1)
The connect call would look like this:
mysession->sig_join_msg.connect(boost::bind(&farmer::notice_handler, &fred, _1));

Slot ordering

It is quite likely that you will want to connect more than one slot to a signal - and this is perfectly acceptable. If you do this, you can control which order the slots are called in by specifying a number as the first parameter to the connect() call, before the function name. For example:
mysession->sig_join_msg.connect(1, first_handler);
mysession->sig_join_msg.connect(3, third_handler);
mysession->sig_join_msg.connect(2, second_handler);
Lower numbered slots will be called before higher numbered ones, and slots with the same number may be called in any order.

Next: Op-me bot