Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Rapptz
Created August 17, 2014 10:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rapptz/8fc1b71f86dc8ae49492 to your computer and use it in GitHub Desktop.
Save Rapptz/8fc1b71f86dc8ae49492 to your computer and use it in GitHub Desktop.
Signal and Slots C++11
// C++11 signal/slots
#include <type_traits>
#include <functional>
#include <map>
#include <limits>
namespace signals {
struct connection_key {
private:
connection_key() = default;
public:
template<typename T, typename U, typename V> friend struct signal;
};
template<bool B>
using Enabler = typename std::enable_if<B, int>::type;
template<typename Function, typename Signature>
struct is_callable : std::false_type {
static_assert(std::is_function<Signature>::value, "Signature must be a function type");
};
struct is_callable_impl {
template<typename Function, typename Return, typename... Args,
typename R = decltype(std::declval<Function>()(std::declval<Args>()...))>
static std::is_same<Return, R> test(int);
template<typename...>
static std::false_type test(...);
};
template<typename Function, typename Return, typename... Args>
struct is_callable<Function, Return(Args...)> : public decltype(is_callable_impl::test<Function, Return, Args...>(0)) {};
template<typename Signature>
struct connection {
static_assert(std::is_function<Signature>::value, "connection expects a function signature");
using function_type = std::function<Signature>;
private:
bool enabled_ = true;
function_type callback;
public:
template<typename Function, Enabler<!std::is_same<typename std::decay<Function>::type, connection>::value> = 0>
connection(Function&& f): callback(f) {}
connection(const connection&) = default;
connection(connection&&) = default;
template<typename... Args>
auto invoke(connection_key, const Args&... args) -> decltype(callback(args...)) {
return callback(args...);
}
void disable() {
enabled_ = false;
}
void enable() {
enabled_ = true;
}
bool enabled() const noexcept {
return enabled_ && static_cast<bool>(callback);
}
void disconnect() {
callback = nullptr;
}
};
struct default_compare {
template<typename T, Enabler<!std::is_enum<T>::value> = 0>
bool operator()(const T& lhs, const T& rhs) const noexcept {
return lhs < rhs;
}
template<typename T, Enabler<std::is_enum<T>::value> = 0>
bool operator()(const T& lhs, const T& rhs) const noexcept {
using U = typename std::underlying_type<T>::type;
return static_cast<U>(lhs) < static_cast<U>(rhs);
}
};
template<typename Signature>
struct return_type {};
template<typename Return, typename... Args>
struct return_type<Return(Args...)> {
using type = Return;
};
template<typename Group>
struct default_group {
private:
struct id { using type = Group; };
using integral_type = typename std::conditional<std::is_enum<Group>::value, std::underlying_type<Group>, id>::type;
public:
using value_type = typename integral_type::type;
static constexpr Group value = static_cast<Group>(std::numeric_limits<value_type>::max());
};
template<typename Signature, typename Group = int, typename Compare = default_compare>
struct signal {
static_assert(std::is_function<Signature>::value, "signal expects a function signature");
using connection_type = connection<Signature>;
using connection_reference = connection_type&;
using result_type = typename return_type<Signature>::type;
using slot_type = typename connection_type::function_type;
private:
std::multimap<Group, connection_type, Compare> connections;
public:
signal() = default;
template<typename Function>
connection_reference connect(Function&& f) {
return connect(default_group<Group>::value, std::forward<Function>(f));
}
template<typename Function>
connection_reference connect(Group g, Function&& f) {
auto&& c = connections.emplace(g, std::forward<Function>(f));
return c->second;
}
template<typename... Args, typename U = result_type, Enabler<!std::is_void<U>::value> = 0>
result_type emit_to(Group g, const Args&... args) const {
auto&& r = connections.equal_range(g);
result_type result;
for(; r.first != r.second; ++r.first) {
auto&& f = r.first->second;
if(f.enabled()) {
result = f.invoke(connection_key{}, args...);
}
}
return result;
}
template<typename Callback, typename... Args, typename U = result_type,
Enabler<!std::is_void<U>::value and is_callable<Callback, void(U)>::value> = 0>
void emit_to(Group g, Callback callback, const Args&... args) const {
auto&& r = connections.equal_range(g);
for(; r.first != r.second; ++r.first) {
auto&& f = r.first->second;
if(f.enabled()) {
callback(f.invoke(connection_key{}, args...));
}
}
}
template<typename... Args, typename U = result_type, Enabler<std::is_void<U>::value> = 0>
result_type emit_to(Group g, const Args&... args) {
auto&& r = connections.equal_range(g);
for(; r.first != r.second; ++r.first) {
auto&& f = r.first->second;
if(f.enabled()) {
f.invoke(connection_key{}, args...);
}
}
}
template<typename... Args, typename U = result_type, Enabler<!std::is_void<U>::value> = 0>
result_type emit(const Args&... args) {
result_type result;
for(auto&& p : connections) {
auto&& f = p.second;
if(f.enabled()) {
result = f.invoke(connection_key{}, args...);
}
}
return result;
}
template<typename Callback, typename... Args, typename U = result_type,
Enabler<!std::is_void<U>::value and is_callable<Callback, void(U)>::value> = 0>
void emit(Callback callback, const Args&... args) {
for(auto&& p : connections) {
auto&& f = p.second;
if(f.enabled()) {
callback(f.invoke(connection_key{}, args...));
}
}
}
template<typename... Args, typename U = result_type, Enabler<std::is_void<U>::value> = 0>
result_type emit(const Args&... args) {
for(auto&& p : connections) {
auto&& f = p.second;
if(f.enabled()) {
f.invoke(connection_key{}, args...);
}
}
}
};
} // signals
#include <iostream>
class Button
{
typedef signals::signal<void(int x, int y)> OnClick;
public:
typedef OnClick::slot_type OnClickSlotType;
OnClick::connection_type doOnClick(const OnClickSlotType& slot) {
return onClick.connect(slot);
}
void simulateClick() {
onClick.emit(52, 38);
}
private:
OnClick onClick;
};
void printCoordinates(long x, long y)
{
std::cout << "(" << x << ", " << y << ")\n";
}
int main() {
Button button;
button.doOnClick(printCoordinates);
button.simulateClick();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment