Corrade::Interconnect::Emitter class

Emitter object.

Contains signals and manages connections between signals and slots. See Signals and slots for introduction.

Implementing signals

Signals are implemented as member functions with Signal as return type, argument count and types are not limited. Their body consists of a single emit() call, to which you pass pointer to the function and forward all arguments. Example signal implementations:

class Postman: public Interconnect::Emitter {
    public:
        Signal messageDelivered(const std::string& message, int price = 0) {
            return emit(&Postman::messageDelivered, message, price);
        }

        Signal paymentRequired(int amount) {
            return emit(&Postman::paymentRequired, amount);
        }
};

The implemented signal can be emitted simply by calling the function:

Postman postman;
postman.messageDelivered("hello");
postman.paymentRequired(245);

If the signal is not declared as public function, it cannot be connected or called from outside the class.

Connecting signals to slots

Signals implemented on Emitter subclasses can be connected to slots using various connect() functions. The argument count and types of slot function must be exactly the same as of the signal function. When a connection is established, returned Connection object can be used together with disconnect() to remove given connection:

Interconnect::Connection c = Interconnect::connect(
    postman, &Postman::paymentRequired,
    [](int amount) { Utility::Debug{} << "pay" << amount; });

// ...

Interconnect::disconnect(postman, c);

Note that the Connection object is just a handle — its destruction doesn't lead to the connection being removed. You can also call disconnectSignal() or disconnectAllSignals() on the emitter to remove the connections. All emitter connections are automatically removed when emitter object is destroyed.

You can connect any signal, as long as the emitter object is of proper type — in particular, referring a signal from a derived type while passing an emitter of base type is not allowed:

class Base: public Interconnect::Emitter {
    public:
        Signal baseSignal() { return emit(&Base::baseSignal); }
};

class Derived: public Base {
    public:
        Signal derivedSignal() { return emit(&Derived::derivedSignal); }
};

Base* a = new Derived;
Derived* b = new Derived;
Interconnect::connect(*a, &Base::baseSignal, [](){});           // ok
Interconnect::connect(*b, &Base::baseSignal, [](){});           // ok
//Interconnect::connect(*a, &Derived::derivedSignal, [](){});   // error
Interconnect::connect(*b, &Derived::derivedSignal, [](){});     // ok

Free function, lambda and function object slots

Slots can be simply free functions. Non-capturing lambdas, shown above, are converted to function pointers and treated the same. These have the least call overhead.

Capturing lambdas and function objects, as long as they are trivially copyable and destructible and small enough (not more than three pointers) are stored by-value similarly to free functions, but the call overhead is slightly larger.

Lambdas and function objects larger than three pointers or having non-trivial copy / destruction are allocated on heap. This is the case of std::function, for example, and the overhead is offset by another indirection.

Member function slots

Finally, it's possible to connect signals to member functions. The receiving object must be a subclass of Receiver and slot must be a non-constant member function with void as a return type. In addition to the cases mentioned above, the connection is automatically removed also when receiver object is destroyed. You can also use Receiver::disconnectAllSlots() to disconnect the receiver from everything.

class Mailbox: public Interconnect::Receiver {
    public:
        void addMessage(const std::string& message, int price) {
            
        }
};

Postman postman;
Mailbox mailbox;
Interconnect::connect(postman, &Postman::messageDelivered,
                      mailbox, &Mailbox::addMessage);

You can connect to any member function, as long as Receiver exists somewhere in given object type hierarchy:

class Foo: public Interconnect::Emitter {
    public:
        Signal signal() { return emit(&Foo::signal); }
};

class Base: public Interconnect::Receiver {
    public:
        void baseSlot() {}
};

class Derived: public Base {
    public:
        void derivedSlot() {}
};

Foo foo;
Base* a = new Derived;
Derived* b = new Derived;

Interconnect::connect(foo, &Foo::signal, *a, &Base::baseSlot);         // ok
Interconnect::connect(foo, &Foo::signal, *b, &Base::baseSlot);         // ok
//Interconnect::connect(foo, &Foo::signal, *a, &Derived::derivedSlot); // error
Interconnect::connect(foo, &Foo::signal, *b, &Derived::derivedSlot);   // ok

It is also possible to connect to member function of class which itself isn't subclass of Receiver, just add Receiver using multiple inheritance. Convoluted example:

class MyString: public std::string, public Interconnect::Receiver {};

std::string c;
MyString d;

//Interconnect::connect(foo, &Foo::signal, c, &std::string::clear);    // error
Interconnect::connect(foo, &Foo::signal, d, &std::string::clear);      // ok

Derived classes

template<std::size_t states, std::size_t inputs, class State, class Input>
class StateMachine
State machine.

Public types

class Signal
Signature for signals.

Constructors, destructors, conversion operators

Emitter(const Emitter&) deleted
Copying is not allowed.
Emitter(Emitter&&) deleted
Moving is not allowed.

Public functions

auto operator=(const Emitter&) -> Emitter& deleted
Copying is not allowed.
auto operator=(Emitter&&) -> Emitter& deleted
Moving is not allowed.
auto hasSignalConnections() const -> bool
Whether the emitter is connected to any slot.
template<class Emitter, class ... Args>
auto hasSignalConnections(Signal(Emitter::*)(Args...) signal) const -> bool
Whether given signal is connected to any slot.
auto isConnected(const Connection& connection) const -> bool
Whether given connection still exists.
auto signalConnectionCount() const -> std::size_t
Count of connections to this emitter signals.
template<class Emitter, class ... Args>
auto signalConnectionCount(Signal(Emitter::*)(Args...) signal) const -> std::size_t
Count of slots connected to given signal.
template<class Emitter, class ... Args>
void disconnectSignal(Signal(Emitter::*)(Args...) signal)
Disconnect signal.
void disconnectAllSignals()
Disconnect everything from this emitter signals.

Protected functions

template<class Emitter, class ... Args>
auto emit(Signal(Emitter::*)(Args...) signal, typename Implementation::Identity<Args>::Type... args) -> Signal
Emit signal.

Function documentation

bool Corrade::Interconnect::Emitter::hasSignalConnections() const

Whether the emitter is connected to any slot.

template<class Emitter, class ... Args>
bool Corrade::Interconnect::Emitter::hasSignalConnections(Signal(Emitter::*)(Args...) signal) const

Whether given signal is connected to any slot.

bool Corrade::Interconnect::Emitter::isConnected(const Connection& connection) const

Whether given connection still exists.

Checks if the Connection object returned by connect() still refers to an existing connection. It's the user responsibility to ensure that the connection corresponds to proper Emitter instance.

std::size_t Corrade::Interconnect::Emitter::signalConnectionCount() const

Count of connections to this emitter signals.

template<class Emitter, class ... Args>
std::size_t Corrade::Interconnect::Emitter::signalConnectionCount(Signal(Emitter::*)(Args...) signal) const

Count of slots connected to given signal.

template<class Emitter, class ... Args>
void Corrade::Interconnect::Emitter::disconnectSignal(Signal(Emitter::*)(Args...) signal)

Disconnect signal.

Disconnects all slots connected to given signal. Example usage:

Postman postman;
postman.disconnectSignal(&Postman::messageDelivered);

void Corrade::Interconnect::Emitter::disconnectAllSignals()

Disconnect everything from this emitter signals.

template<class Emitter, class ... Args>
Signal Corrade::Interconnect::Emitter::emit(Signal(Emitter::*)(Args...) signal, typename Implementation::Identity<Args>::Type... args) protected

Emit signal.

Parameters
signal Signal
args Arguments

See class documentation for more information about implementing signals.

template<class EmitterObject, class Emitter, class Functor, class ... Args>
Connection connect(EmitterObject& emitter, Interconnect::Emitter::Signal(Emitter::*)(Args...) signal, Functor&& slot)

Connect signal to function slot.

Parameters
emitter Emitter
signal Signal
slot Slot

Connects given signal to compatible slot. emitter must be subclass of Emitter, signal must be implemented signal and slot can be either a non-member function, a lambda or any other function object. The argument count and types must be exactly the same.

See Emitter class documentation for more information about connections.

template<class EmitterObject, class Emitter, class Receiver, class ReceiverObject, class ... Args>
Connection connect(EmitterObject& emitter, Interconnect::Emitter::Signal(Emitter::*)(Args...) signal, ReceiverObject& receiver, void(Receiver::*)(Args...) slot)

Connect signal to member function slot.

Parameters
emitter Emitter
signal Signal
receiver Receiver
slot Slot

Connects given signal to compatible slot in receiver object. emitter must be subclass of Emitter, signal must be implemented signal, receiver must be subclass of Receiver and slot must be non-constant member function with void as return type. The argument count and types must be exactly the same.

See Emitter class documentation for more information about connections.

bool disconnect(Emitter& emitter, const Connection& connection)

Disconnect a signal/slot connection.

Parameters
emitter Emitter
connection Connection handle returned by connect()

It's the user responsibility to ensure that connection corresponds to given emitter instance. See Emitter class documentation for more information about connections.