|
#ifndef SIG_H_ |
|
#define SIG_H_ |
|
|
|
#include <functional> |
|
#include <list> |
|
#include <memory> |
|
|
|
namespace sigslot |
|
{ |
|
#if __cplusplus < 201703L |
|
template <class ForwardIterator, class UnaryPredicate> |
|
ForwardIterator remove_if(ForwardIterator first, ForwardIterator last, |
|
UnaryPredicate pred) |
|
{ |
|
ForwardIterator result = first; |
|
|
|
while (first!=last) { |
|
if (!pred(*first)) { |
|
|
|
*result = std::move(*first); |
|
++result; |
|
} |
|
++first; |
|
} |
|
return result; |
|
} |
|
#endif |
|
|
|
template <typename... Args> |
|
class ConnectionItem |
|
{ |
|
typedef std::function<void(Args...)> Slot; |
|
bool _connected = false; |
|
Slot _slot; |
|
|
|
public: |
|
ConnectionItem(Slot slot) : _slot(slot) |
|
{ |
|
_connected = true; |
|
} |
|
|
|
~ConnectionItem() |
|
{ |
|
_connected = false; |
|
} |
|
|
|
void operator()(Args... args) |
|
{ |
|
if (_connected && _slot) |
|
_slot(args...); |
|
} |
|
|
|
bool isConnected() const |
|
{ |
|
return _connected; |
|
} |
|
|
|
void disconnect() |
|
{ |
|
_connected = false; |
|
} |
|
}; |
|
|
|
template<typename... Args> |
|
class Signal; |
|
|
|
template<typename... Args> |
|
class SConnection; |
|
|
|
template<typename... Args> |
|
class Connection |
|
{ |
|
typedef std::shared_ptr<ConnectionItem<Args...>> Item; |
|
Item _item; |
|
|
|
void disconnect() |
|
{ |
|
_item->disconnect(); |
|
} |
|
|
|
bool operator==(const Item &item) |
|
{ |
|
return item == _item; |
|
} |
|
|
|
friend class Signal<Args...>; |
|
friend class SConnection<Args...>; |
|
|
|
public: |
|
Connection(Item item) : _item(item) |
|
{ |
|
} |
|
|
|
bool isConnected() const |
|
{ |
|
return _item->connected; |
|
} |
|
}; |
|
|
|
template<typename... Args> |
|
class SConnection |
|
{ |
|
typedef sigslot::Connection<Args...> Connection; |
|
bool hasConnection = false; |
|
Signal<Args...> *_signal; |
|
|
|
Connection _connection; |
|
|
|
public: |
|
SConnection() : _signal(nullptr) |
|
{ |
|
} |
|
|
|
SConnection(Signal<Args...>& signal, Connection connection) : _signal(&signal), _connection(connection) |
|
{ |
|
hasConnection = true; |
|
} |
|
|
|
~SConnection() |
|
{ |
|
if(hasConnection && _signal) |
|
_signal->disconnect(_connection); |
|
} |
|
}; |
|
|
|
template <typename... Args> |
|
class Signal |
|
{ |
|
typedef std::shared_ptr<ConnectionItem<Args...>> Item; |
|
std::list<Item> _items; |
|
|
|
void removeDisconnected() |
|
{ |
|
#if __cplusplus < 201703L |
|
_items.erase(sigslot::remove_if(_items.begin(), _items.end(), [](Item &item) { |
|
#else |
|
_items.erase(std::remove_if(_items.begin(), _items.end(), [](Item &item) { |
|
#endif |
|
return !item->isConnected(); |
|
}), _items.end()); |
|
} |
|
|
|
public: |
|
typedef std::function<void(Args...)> Slot; |
|
typedef sigslot::Connection<Args...> Connection; |
|
typedef SConnection<Args...> ScopedConnection; |
|
|
|
Signal() |
|
{ |
|
|
|
} |
|
|
|
~Signal() |
|
{ |
|
for(const auto & item : _items) |
|
item->disconnect(); |
|
|
|
_items.clear(); |
|
} |
|
|
|
template <typename SlotF> |
|
Connection connect(SlotF&& slot) |
|
{ |
|
Item item = std::make_shared<ConnectionItem<Args...>>(std::forward<SlotF>(slot)); |
|
_items.push_back(item); |
|
return Connection(item); |
|
} |
|
|
|
// Variadics are always better than placeholders. |
|
template<class O, typename R, typename ... A> |
|
Connection connect(O* o, R(O::*f)(A...)) |
|
{ |
|
Item item = std::make_shared<ConnectionItem<Args...>>(std::forward<Slot>([=](A... args) { return (o->*f)(args...); })); |
|
_items.push_back(item); |
|
return Connection(item); |
|
} |
|
|
|
void operator()(Args... args) |
|
{ |
|
for(const auto & item : _items) |
|
(*item)(args...); |
|
|
|
} |
|
|
|
bool disconnect(Connection &connection) |
|
{ |
|
for(const auto & item : _items) { |
|
if(connection == item) { |
|
connection.disconnect(); |
|
removeDisconnected(); |
|
|
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
}; |
|
} |
|
|
|
#endif |
Signal.h now builds error-free. Although Peter (the original author) wrote it to be usable under stl 11 his driver program can only be build under 17 due to his use of std::remove_if.