This assumes there is no need to aggregate results from the listeners of a broadcast signal.
Technically I guess the slots are whatever your std::function
holds, so they aren't special. So I guess this is more of a Signals/Connections design.
The "connection" or Signal<T...>::Listener
is the owner of the callback. It's ideal to have this owned by the object that has the callback function or on the object your lambda captures. That way when that object goes out of scope so does the callback, which prevents it from being called anymore.
You could dump listeners into a container on the object and forget about them. Or store specifically named instances of them which is very useful for switching the object you're listening to or disconnecting.
template <typename... FuncArgs>
class Signal
{
using fp = std::function<void(FuncArgs...)>;
std::forward_list<std::weak_ptr<fp> > registeredListeners;
public:
using Listener = std::shared_ptr<fp>;
Listener add(const std::function<void(FuncArgs...)> &cb) {
// passing by address, until copy is made in the Listener as owner.
Listener result(std::make_shared<fp>(cb));
registeredListeners.push_front(result);
return result;
}
void raise(FuncArgs... args) {
registeredListeners.remove_if([&args...](std::weak_ptr<fp> e) -> bool {
if (auto f = e.lock()) {
(*f)(args...);
return false;
}
return true;
});
}
};
usage
Signal<int> bloopChanged;
// ...
Signal<int>::Listener bloopResponse = bloopChanged.add([](int i) { ... });
// or
decltype(bloopChanged)::Listener bloopResponse = ...
// let bloopResponse go out of scope.
// or re-assign it
// or reset the shared_ptr to disconnect it
bloopResponse.reset();