3.8.0
Freundlich's C++ toolkit
Classes | Typedefs
fcppt.signal

Description

An implementation of a managed signals and slots system (replacement for boost's signals).

Introduction

fcppt::signal is a library similar to boost.signals2. It was created when boost.signals (version 1) was the only signals library available in boost, which was really slow. See the old benchmark in Benchmarks. One of the major differences between fcppt::signal and boost.signals2 is that boost.signals2 is thread-safe, while fcppt::signal is not, which makes up for some of the speed difference.

Basic usage

Here is a simple, motivating example:

struct test_struct
{
};
void callback(int const _value)
{
fcppt::io::cout() << FCPPT_TEXT("\"callback\" called with ") << _value << FCPPT_TEXT("!\n");
}
void other_callback(fcppt::reference<test_struct>)
{
fcppt::io::cout() << FCPPT_TEXT("\"other_callback\" called\n");
}
void main_example()
{
using signal_type = fcppt::signal::object<void(int)>;
signal_type signal{};
// Connect function "callback" to signal
fcppt::signal::auto_connection const connection(signal.connect(signal_type::function{&callback}));
// Call the signal, will output: "callback" called with 3
signal(3);
// Define another function
signal2_type signal2{};
fcppt::signal::auto_connection const connection2(
signal2.connect(signal2_type::function{&other_callback}));
test_struct foo{};
// Outputs: "other_callback" called
signal2(fcppt::make_ref(foo));
}

A signal is of type fcppt::signal::object. This type has a template parameter which contains the signal's function type. Note that signals may have a return type; see below for more on that.

To attach a callback function to the signal, you have to call the fcppt::signal::object::connect member function. Arbitrarily many attached functions are allowed. The connect function returns an fcppt::signal::auto_connection, which is a unique pointer to a connection. The reason behind this is that connections may keep additional information that is important when they are destroyed (see Disconnect callbacks). The callback function is disconnected from the signal when the connection object dies.

Signals with return values

If a signal has a return value other than void, you cannot use fcppt::signal::object's default constructor. Instead, there is a constructor that takes a function of type

result_type(result_type,result_type)

where result_type is the return type of the signal's function type. This function is used to combine the results of the different callbacks that are connected to the signal. The initial value of this operation is passed when the signal itself is called.

Here is an example which should clarify how combiners are used:

#include <fcppt/text.hpp>
#include <fcppt/io/cout.hpp>
#include <fcppt/signal/auto_connection.hpp>
#include <fcppt/signal/object.hpp>
namespace
{
int first_callback() { return 4; }
int second_callback() { return 8; }
int third_callback() { return 15; }
int combiner(int const a, int const b) { return a * b; }
}
int main()
{
using function = int();
using int_signal = fcppt::signal::object<function>;
int_signal signal{int_signal::combiner_function(&combiner)};
fcppt::signal::auto_connection const connection1{
signal.connect(int_signal::function{&first_callback})};
fcppt::signal::auto_connection const connection2{
signal.connect(int_signal::function{&second_callback})};
fcppt::signal::auto_connection const connection3{
signal.connect(int_signal::function{&third_callback})};
// Outputs ((1*4)*8)*15=480
fcppt::io::cout() << signal(int_signal::initial_value{1}) << FCPPT_TEXT('\n');
}

Note that there are setters for both the initial value and the combiner.

Disconnect callbacks

One unique feature of fcppt::signal::object is the ability to receive a callback from the signal when a connection dies. To demonstrate how this works, let's say you want to associate a name with a signal. Users can then attach to such a named signal (think about a Quake-style console where you can register new console commands). However, when the last connection to a named signal dies, the signal should die with it. To do that, we define:

using name_to_signal = std::unordered_map<std::string, signal_type>;
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects, cppcoreguidelines-avoid-non-const-global-variables)
name_to_signal global_name_to_signal{};
// This function will be called whenever a connection dies. It receives the
// signal's name.
void remove_function(std::string const &_name)
{
std::cout << "Goodbye, " << _name << "!\n";
fcppt::container::find_opt_iterator(global_name_to_signal, _name),
[](name_to_signal::iterator const _pos) {
// "empty" returns true if there are connections attached to the signal.
if (_pos->second.empty())
{
global_name_to_signal.erase(_pos);
}
});
}

To receive disconnect "events", we use fcppt::signal::object's second template parameter and set it to fcppt::signal::unregister::base. Using this base class, we have the empty method at our disposal to check if there are connections attached to the signal.

To register to a named signal, we define the following function:

register_named_signal(std::string const &_name, fcppt::signal::unregister::function &&_function)
{
signal_type &signal{fcppt::container::get_or_insert(
global_name_to_signal, _name, [](std::string const &) { return signal_type(); })};
return signal.connect(
std::move(_function),
// Add our remove function as the disconnect handler (see above)
fcppt::signal::unregister::function{[_name] { remove_function(_name); }});
}

Note that the remove callbacks' signature is just void(). Our main might look like this:

fcppt::signal::auto_connection const hello_connection(register_named_signal(
"hello", fcppt::signal::unregister::function{[] { std::cout << "hello!\n"; }}));
// Will print "goodbye"
global_name_to_signal["hello"]();
{
fcppt::signal::auto_connection const goodbye_connection(register_named_signal(
"goodbye", fcppt::signal::unregister::function{[] { std::cout << "goodbye!\n"; }}));
// Will print "goodbye!"
global_name_to_signal["goodbye"]();
// Will print "Goodbye goodbye!" at end of scope
}
// Will print "Goodbye hello!" at end of scope

Benchmarks

Out benchmark is as follows:

The results were as follows:

Signals-Implementation gcc-4.6 (-O3) clang-3.0 (-O3) icc-12.1.0 (-O3)
boost.signals 1.426s 1.444s 1.679s
boost.signals2 0.871s 1.644s 1.728s
libsigc++ 0.614s 0.615s 0.647s
fcppt.signal 0.526s 0.505s 0.539s

Or graphically:

Benchmark results

Classes

class  fcppt::signal::base< T >
 Default base class for signals. Provides no unlinking capabilities. More...
 
class  fcppt::signal::connection
 A connection returned by a connect call. More...
 
class  fcppt::signal::object< T, Base, Enable >
 Represents a signal with a non-void return value. More...
 
class  fcppt::signal::unregister::base< T >
 A base class for signals providing unlinking. More...
 

Typedefs

using fcppt::signal::auto_connection = fcppt::unique_ptr< fcppt::signal::connection >
 A unique pointer to a connection. More...
 
using fcppt::signal::optional_auto_connection = fcppt::optional::object< fcppt::signal::auto_connection >
 An optional auto connection. More...
 
using fcppt::signal::unregister::function = fcppt::function< void()>
 The unregister function object. More...
 

Typedef Documentation

◆ auto_connection

A unique pointer to a connection.

◆ function

The unregister function object.

◆ optional_auto_connection

An optional auto connection.