libzypp: zypp-core/ng/base/signals.h File Reference

libzypp 17.38.6

signals.h File Reference
`#include <sigc++/trackable.h>`
`#include <sigc++/signal.h>`
`#include <sigc++/connection.h>`
`#include <sigc++/visit_each.h>`
`#include <sigc++/adaptors/adaptors.h>`
`#include <memory>`
`#include <utility>`
`#include <cassert>`
`#include <zypp-core/base/Exception.h>`
`#include <zypp-core/base/LogControl.h>`

Go to the source code of this file.

## Classes
class | zyppng::Signal< R(T...)>
class | zyppng::MemSignal< SignalHost, ReturnType(Arguments...)>
struct | zyppng::internal::lock_shared< T_functor, Lockers >
class | zyppng::SignalProxy< R(T...)>
| Hides the signals emit function from external code. More...
struct | sigc::visitor< zyppng::internal::lock_shared< T_functor, Lockers... > >

## Namespaces
namespace | zyppng
namespace | zyppng::internal
namespace | sigc

## Typedefs
using | zyppng::connection = sigc::connection
using | zyppng::trackable = sigc::trackable
template<class R, class... T>
using | zyppng::SignalProxyBase = sigc::signal<R(T...)>

## Functions
template<typename T>
auto | zyppng::internal::lock_shared_makeLock (const T &locker)
template<typename Functor, typename ... Obj>
decltype(auto) | zyppng::internal::locking_fun (const Functor &f, const Obj &... o)

## Detailed Description

This file implements the signal and slot concept in libzypp.

Signals and slots are basically a implementation of the observer pattern which makes it possible for objects to work together without them having to know about each other by connecting the signals of one object to the slots of another. Whenever the object owning the signal emits said signal, all connected slots are executed. This is especially helpful in async environments where we need to react on specific events like timers or filedescriptor events that can happen at any time during execution.

Using signals and slots in libzypp requires the developer to make sure that all objects that are used in the signal chain can not be deleted during emission, for example a Socket emitting the closed() signal might result in its deletion. This is usually made sure by taking a reference to the sender object via shared_ptr, e.g. the EventLoop does this for all AbstractEventSource 's that are registered to receive events using shared_from_this.

To have signals in an Object, it is recommended to subclass the Type from zyppng::Base and always have the objects in a std::shared_ptr. zyppng::Base provides the zyppng::Base::connect and zyppng::Base::connectFunc helpers to connect signals and slots. Connections made with those helpers will make sure the receiving objects are properly locked.

The zyppng::Base::connect helper function can be used to connect a signal that is hosted by a zyppng::Base derived type to a slot hosted by a zyppng::Base derived type. No special care needs to be taken to make sure the receiver object is locked since the function is handling all the details about that:

class Receiver : public zyppng::Base
{
public:
static std::shared_ptr<Receiver> create();
void writeOnTimeout() {
std::cout << "Hello World" << std::endl;
}
}
auto evLoop = zyppng::EventLoop::create();
auto sender = zyppng::Timer::create();
auto receiver = Receiver::create();
// invoking the static Base::connect() function will make sure receiver is locked, the sender will be locked by the
// eventloop triggering the timeout
sender->connect( *sender, &zyppng::Timer::sigExpired, *receiver, &Receiver::writeOnTimeout );
sender->start(1000);
evLoop->run();
zyppng::Base
Definition base.h:61
zyppng::EventLoop::create
static Ptr create(GMainContext *ctx=nullptr)
Definition eventloop_glib.cc:29
zyppng::Timer::create
static std::shared_ptr< Timer > create()
Creates a new Timer object, the timer is not started at this point.
Definition timer.cc:52
zyppng::Timer::sigExpired
SignalProxy< void(Timer &t)> sigExpired()
This signal is always emitted when the timer expires.
Definition timer.cc:120

The zyppng::Base::connect helper function can be used to connect a signal that is hosted by a zyppng::Base derived type to a lambda type slot, commonly used when the developer wants to have the code that is invoked by the signal right where the slot is connected, to make the code easier to read or when the slot code is not reused anywhere else.

In this example the receiver object uses a slot to invoke the actual function that handles the event. Since we only connect a lambda that invokes the writeOnTimeout function using a caputured this pointer we need to manually take care of locking the receiver object during the slot invocation by passing it as a extra argument after the lambda. While the code would compile without doing that we introduce a bug that is very hard to track down because the subsequent signal emitted from the receiver object causes it to be deleted, making the code fail afterwards.

class Receiver : public zyppng::Base
{
public:
static std::shared_ptr<Receiver> create();
void connectToTimer ( std::shared_ptr<Timer> &p ) {
p->connectFunc( *sender, &zyppng::Timer::sigExpired, [ this ](){
onTimeout();
}, *this );
}
SignalProxy<void()> sigGotTimeout() {
return _sigGotTimeout;
}
private:
void onTimeout() {
std::cout << "Hello World" << std::endl;
// emits a subsequent signal
_sigGotTimeout.emit();
// calls another function on this after emitting the signal
// in cases where our instance was not locked by the caller and
// a slot that received our signal released the last reference to us
// this will run into a segfault
this->doSomethingElse();
}
void doSomethingElse() {
std::cout << "Something else" << std::endl;
}
Signal<void()> _sigGotTimeout;
}
auto evLoop = zyppng::EventLoop::create();
auto sender = zyppng::Timer::create();
auto receiver = Receiver::create();
// invoking the static Base::connect() function will make sure receiver is locked, the sender will be locked by the
// eventloop triggering the timeout
receiver->connectToTimer( sender );
// release the receiver after the timeout was triggered
receiver->connectFunc( *receiver, &Receiver::sigGotTimeout, [ &receiver ](){
// releasing the receiver Object once the timer was triggered once. If the object
// is not properly referenced during signal emission we will run into a SEGFAULT
receiver.reset();
});
sender->start( 1000 );
evLoop->run();

Definition in file signals.h.