Location>code7788 >text

Programming Exercise: Writing a Listener Pattern Class

Popularity:481 ℃/2025-01-18 20:31:21
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_