Skip to content

Instantly share code, notes, and snippets.

@Razzile
Created January 25, 2022 19:07
Show Gist options
  • Save Razzile/a7f424d303dac5d70f898fb35fa19615 to your computer and use it in GitHub Desktop.
Save Razzile/a7f424d303dac5d70f898fb35fa19615 to your computer and use it in GitHub Desktop.
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