Created
January 25, 2022 19:07
-
-
Save Razzile/a7f424d303dac5d70f898fb35fa19615 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template <typename... Args> | |
class Delegate { | |
typedef std::function<void(Args...)> ListenerFunc; | |
/** | |
* A Handle object represents a single listener in the delegate | |
*/ | |
struct Handle { | |
static inline std::atomic_int global_id_ = 0; | |
Handle(Delegate* parent) : parent_(parent) {} | |
Handle(const Handle& other) = delete; | |
Handle& operator=(const Handle&) = delete; | |
Handle(Handle&& other) : id_(other.id_), parent_(other.parent_) { | |
other.parent_ = nullptr; | |
other.id_ = -1; | |
} | |
Handle& operator=(Handle&& other) { | |
parent_ = other.parent_; | |
id_ = other.id_; | |
other.parent_ = nullptr; | |
other.id_ = -1; | |
return *this; | |
} | |
bool operator==(const Handle& other) const { return id_ == other.id_; } | |
~Handle() { | |
if (parent_) { | |
parent_->RemoveListener(*this); | |
} | |
} | |
operator int() const { return id_; } | |
int id_ = global_id_++; | |
Delegate* parent_; | |
}; | |
struct Listener { | |
ListenerFunc listener_func; | |
int handle_id; | |
}; | |
public: | |
Handle RegisterListener(ListenerFunc const& listener_func) { | |
std::unique_lock guard(lock_); | |
Handle handle(this); | |
Listener l{listener_func, handle.id_}; | |
listeners_.push_back(l); | |
return std::move(handle); | |
} | |
template <class T> | |
Handle RegisterListener(T* obj, void (T::*listener_func)(Args...)) { | |
return RegisterListener([=](Args&&... ts) { | |
return std::invoke(listener_func, obj, std::forward<Args>(ts)...); | |
}); | |
} | |
template <class T> | |
Handle RegisterListener(T const* obj, | |
void (T::*listener_func)(Args...) const) { | |
return RegisterListener([=](Args&&... ts) { | |
return std::invoke(listener_func, obj, std::forward<Args>(ts)...); | |
}); | |
} | |
void RemoveAllListeners() { | |
std::unique_lock guard(lock_); | |
listeners_.clear(); | |
} | |
bool RemoveListener(const Handle& handle) { | |
auto it = std::find_if( | |
listeners_.begin(), listeners_.end(), | |
[&handle](const Listener& l) { return l.handle_id == handle.id_; }); | |
if (it != listeners_.end()) { | |
listeners_.erase(it); | |
return true; | |
} | |
return false; | |
} | |
void operator()(Args&&... args) { | |
std::shared_lock guard(lock_); | |
for (auto& listener : listeners_) { | |
listener.listener_func(std::forward<Args>(args)...); | |
} | |
} | |
private: | |
mutable std::shared_mutex lock_; | |
std::vector<Listener> listeners_; | |
}; | |
using Signal = Delegate<>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment