signal.h
Go to the documentation of this file.
/*
* Copyright © 2013 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Thomas Voß <thomas.voss@canonical.com>
*/
#ifndef COM_UBUNTU_SIGNAL_H_
#define COM_UBUNTU_SIGNAL_H_
#include <core/connection.h>
#include <functional>
#include <iostream>
#include <list>
#include <mutex>
#include <set>
namespace core
{
template<typename ...Arguments>
class Signal
{
public:
typedef std::function<void(Arguments...)> Slot;
private:
struct SlotWrapper
{
void operator()(Arguments... args)
{
dispatcher(std::bind(slot, args...));
}
Slot slot;
Connection::Dispatcher dispatcher;
Connection connection;
};
public:
inline Signal() noexcept(true) : d(new Private())
{
}
inline ~Signal()
{
std::lock_guard<std::mutex> lg(d->guard);
for (auto slot : d->slot_list)
slot.connection.reset();
}
// Copy construction, assignment and equality comparison are disabled.
Signal(const Signal&) = delete;
Signal& operator=(const Signal&) = delete;
bool operator==(const Signal&) const = delete;
inline Connection connect(const Slot& slot) const
{
// Helpers to initialize an invalid connection.
static const Connection::Disconnector empty_disconnector{};
static const Connection::DispatcherInstaller empty_dispatcher_installer{};
// The default dispatcher immediately executes the function object
// provided as argument on whatever thread is currently running.
static const Connection::Dispatcher default_dispatcher
= [](const std::function<void()>& handler) { handler(); };
Connection conn{empty_disconnector, empty_dispatcher_installer};
std::lock_guard<std::mutex> lg(d->guard);
auto result = d->slot_list.insert(
d->slot_list.end(),
SlotWrapper{slot, default_dispatcher, conn});
// We implicitly share our internal state with the connection here
// by passing in our private bits contained in 'd' to the std::bind call.
// This admittedly uncommon approach allows us to cleanly manage connection
// and signal lifetimes without the need to mark everything as mutable.
conn.d->disconnector = std::bind(
&Private::disconnect_slot_for_iterator,
d,
result);
conn.d->dispatcher_installer = std::bind(
&Private::install_dispatcher_for_iterator,
d,
std::placeholders::_1,
result);
return conn;
}
inline void operator()(Arguments... args)
{
std::lock_guard<std::mutex> lg(d->guard);
for(auto slot : d->slot_list)
{
slot(args...);
}
}
private:
struct Private
{
typedef std::list<SlotWrapper> SlotList;
inline void disconnect_slot_for_iterator(typename SlotList::iterator it)
{
std::lock_guard<std::mutex> lg(guard);
slot_list.erase(it);
}
inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher,
typename SlotList::iterator it)
{
std::lock_guard<std::mutex> lg(guard);
it->dispatcher = dispatcher;
}
std::mutex guard;
SlotList slot_list;
};
std::shared_ptr<Private> d;
};
template<>
class Signal<void>
{
public:
typedef std::function<void()> Slot;
private:
struct SlotWrapper
{
void operator()()
{
dispatcher(slot);
}
Slot slot;
Connection::Dispatcher dispatcher;
Connection connection;
};
public:
inline Signal() noexcept(true) : d(new Private())
{
}
inline ~Signal()
{
std::lock_guard<std::mutex> lg(d->guard);
for (auto slot : d->slot_list)
slot.connection.reset();
}
// Copy construction, assignment and equality comparison are disabled.
Signal(const Signal&) = delete;
Signal& operator=(const Signal&) = delete;
bool operator==(const Signal&) const = delete;
inline Connection connect(const Slot& slot) const
{
// Helpers to initialize an invalid connection.
static const Connection::Disconnector empty_disconnector{};
static const Connection::DispatcherInstaller empty_dispatcher_installer{};
// The default dispatcher immediately executes the function object
// provided as argument on whatever thread is currently running.
static const Connection::Dispatcher default_dispatcher
= [](const std::function<void()>& handler) { handler(); };
Connection conn{empty_disconnector, empty_dispatcher_installer};
std::lock_guard<std::mutex> lg(d->guard);
auto result = d->slot_list.insert(
d->slot_list.end(),
SlotWrapper{slot, default_dispatcher, conn});
// We implicitly share our internal state with the connection here
// by passing in our private bits contained in 'd' to the std::bind call.
// This admittedly uncommon approach allows us to cleanly manage connection
// and signal lifetimes without the need to mark everything as mutable.
conn.d->disconnector = std::bind(
&Private::disconnect_slot_for_iterator,
d,
result);
conn.d->dispatcher_installer = std::bind(
&Private::install_dispatcher_for_iterator,
d,
std::placeholders::_1,
result);
return conn;
}
inline void operator()()
{
std::lock_guard<std::mutex> lg(d->guard);
for(auto slot : d->slot_list)
{
slot();
}
}
private:
struct Private
{
typedef std::list<SlotWrapper> SlotList;
inline void disconnect_slot_for_iterator(typename SlotList::iterator it)
{
std::lock_guard<std::mutex> lg(guard);
slot_list.erase(it);
}
inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher,
typename SlotList::iterator it)
{
std::lock_guard<std::mutex> lg(guard);
it->dispatcher = dispatcher;
}
std::mutex guard;
SlotList slot_list;
};
std::shared_ptr<Private> d;
};
}
#endif // COM_UBUNTU_SIGNAL_H_
core::Signal< void >::~Signal
~Signal()
Definition: signal.h:199
core::Connection::Dispatcher
std::function< void(const std::function< void()> &)> Dispatcher
Definition: connection.h:34
core::Signal< void >::Slot
std::function< void()> Slot
Slot is the function type that observers have to provide to connect to this signal.
Definition: signal.h:176
core::Signal::operator=
Signal & operator=(const Signal &)=delete
core::Signal< void >::Signal
Signal() noexcept(true)
Signal constructs a new instance. Never throws.
Definition: signal.h:195
core::Signal::Signal
Signal() noexcept(true)
Signal constructs a new instance. Never throws.
Definition: signal.h:61
core::Signal
A signal class that observers can subscribe to.
Definition: signal.h:36
core::Signal< void >::connect
Connection connect(const Slot &slot) const
Connects the provided slot to this signal instance.
Definition: signal.h:220
core
Definition: connection.h:25
core::Signal::operator()
void operator()(Arguments... args)
operator () emits the signal with the provided parameters.
Definition: signal.h:132
core::Connection
The Connection class models a signal-slot connection.
Definition: connection.h:31
connection.h
core::Signal< void >::operator()
void operator()()
operator () emits the signal.
Definition: signal.h:263
core::Signal::Slot
std::function< void(Arguments...)> Slot
Slot is the function type that observers have to provide to connect to this signal.
Definition: signal.h:42
core::Signal::operator==
bool operator==(const Signal &) const =delete
core::Signal::connect
Connection connect(const Slot &slot) const
Connects the provided slot to this signal instance.
Definition: signal.h:86
core::Signal::~Signal
~Signal()
Definition: signal.h:65