Skip to content

Instantly share code, notes, and snippets.

@Alia5
Last active May 24, 2022 12:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Alia5/df0390c24b2db41e227baae978f9bb38 to your computer and use it in GitHub Desktop.
Save Alia5/df0390c24b2db41e227baae978f9bb38 to your computer and use it in GitHub Desktop.
C++11 Signals/Slots implementation
#ifndef SIGNALS_H
#define SIGNALS_H
#include <functional>
#include <list>
#include <memory>
template <typename... Args>
class ConnectionItem
{
typedef std::function<void(Args...)> 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;
}
private:
bool connected_ = false;
Slot slot_;
};
template<typename... Args>
class Signal;
template<typename... Args>
class SConnection;
template<typename... Args>
class Connection
{
typedef std::shared_ptr<ConnectionItem<Args...>> Item;
public:
Connection(Item item) : item_(item)
{
}
bool isConnected() const
{
return item_->connected_;
}
private:
Item item_;
void disconnect()
{
item_->disconnect();
}
bool operator==(const Item &item)
{
return item == item_;
}
friend class Signal<Args...>;
friend class SConnection<Args...>;
};
template<typename... Args>
class SConnection
{
typedef Connection<Args...> Connection;
public:
SConnection() : signal_(nullptr)
{
}
SConnection(Signal<Args...>& signal, Connection connection) : signal_(&signal), connection_(connection)
{
hasConnection = true;
}
~SConnection()
{
if (hasConnection && signal_)
{
signal_->disconnect(connection_);
}
}
private:
bool hasConnection = false;
Connection connection_;
Signal<Args...> *signal_;
};
template <typename... Args>
class Signal
{
typedef std::shared_ptr<ConnectionItem<Args...>> Item;
public:
typedef std::function<void(Args...)> Slot;
typedef 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);
}
//Binding with placeholders is tedious...
//Don't want i.e. this: sig.Connect(std::bind(&DummyObject::test, this, std::placeholders::_1, std::placeholders::_2));
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;
}
private:
std::list<Item> items_;
void removeDisconnected()
{
items_.erase(std::remove_if(items_.begin(), items_.end(), [](Item &item) {
return !item->isConnected();
}), items_.end());
}
};
#endif // !SIGNALS_H
/* EXAMPLE USAGE
typedef Signal<std::string, int> Signal;
Signal sig;
void stuff()
{
auto conn = Signal::ScopedConnection(sig, sig.connect([](std::string s, int a){
std::cout << s << a << "\n";
}));
sig("stuff", 0);
}
void differentStuff(std::string s, int a)
{
std::cout "differentStuff: " << s << a << "\n";
}
int main()
{
Signal::Connection conn = sig.connect(differentStuff);
stuff();
sig("The answer: ", 42);
sig.disconnect(conn);
return 0;
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment