ifndef __OBSERVABLE_H___
#define __OBSERVABLE_H___
#include <functional>
#include <mutex>
#include <unordered_map>
#include <cassert>
namespace cise {
/**
* @class Observable
* @brief Observable object in the observer pattern, allowing observers to register, unregister and receive notifications.
*
* @tparam Event The type of observed event, which can be any hashable type (such as enumeration, integer, string, etc.).
*/template<typename Event>
class Observable final {
public:
using Handle = int; // Observer handle typepublic:
Observable() : nextHandle_(0) {}
/**
* @brief Add observer.
*
* @param event The key value of the observation event.
* @param callback The callback function of the observer.
* @return Returns the handle of the observer for subsequent logout.
*/
HandleaddObserver(Event event, std::function<void()> callback) {
std::lock_guard<std::mutex> lock(mutex_);
// Check if Handle overflowsassert(nextHandle_ < std::numeric_limits<Handle>::max() && "Handle overflow: maximum number of observers reached.");
auto& handlers = observers_[event];
handlers[nextHandle_] = std::move(callback);
return nextHandle_++;
}
/**
* @brief Removes observers for specified events and handles.
*
* @param event The key value of the observation event.
* @param handle The handle of the observer.
*/void removeObserver(Event event, Handle handle) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = observers_.find(event);
if (it != observers_.end()) {
it->second.erase(handle);
}
}
/**
* @brief Removes the observers of the specified handle from all events.
*
* @param handle The handle of the observer.
*/void removeObserver(Handle handle) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto& [event, handlers] : observers_) {
handlers.erase(handle);
}
}
/**
* @brief Notifies all observers observing the specified event.
*
* @param event The key value of the observation event.
* @note If an observer's callback function throws an exception, the exception will be passed directly to the caller.
*/void notifyObservers(Event event) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = observers_.find(event);
if (it != observers_.end()) {
for (auto& [handle, callback] : it->second) {
callback(); // Exceptions will be passed directly to the caller}
}
}
private:
// Use std::unordered_map instead of std::map, because 90% of scenarios do not require orderly traversal of observers by event key value, which helps improve performance.
std::unordered_map<Event, std::unordered_map<Handle, std::function<void()>>> observers_