// // Created by jguegant on 1/30/16. // #ifndef REPOSITORY_HPP #define REPOSITORY_HPP #include #include #include #include #include struct DefaultSlotKey; template class Slot; template class Watcher { public: Watcher(Slot& slot): slot_(slot), hasBeenChanged_(false) { } Watcher(const Watcher&) = delete; Watcher & operator=(const Watcher&) = delete; bool hasBeenChanged() const { return hasBeenChanged_; } void triggerChanges() { hasBeenChanged_ = true; } auto get() -> decltype(std::declval>().doGet()) { hasBeenChanged_ = false; // Note: even if there is an update of the value between this line and the getValue one, // we will still have the latest version. // Note 2: atomic_bool automatically use a barrier and the two operations can't be inversed. return slot_.doGet(); } private: Slot& slot_; std::atomic_bool hasBeenChanged_; }; template using WatcherPtr = std::unique_ptr, std::function*)>>; template class Slot { public: using ThisType = Slot; using WatcherType = Watcher; using WatcherTypePtr = std::unique_ptr> ; public: std::shared_ptr doGet() const { return std::atomic_load(&value_); } void doSet(const std::shared_ptr &value) { std::atomic_exchange(&value_, value); signal(); } WatcherTypePtr doGetWatcher() { WatcherTypePtr watcher(new WatcherType(*this), [this](WatcherType* toBeDelete) { this->unregisterWatcher(toBeDelete);}); registerWatcher(watcher.get()); return watcher; } private: void registerWatcher(WatcherType* newWatcher) { std::lock_guard l(watchers_mutex_); watchers_.push_back(newWatcher); } void unregisterWatcher(WatcherType *toBeDelete) { std::lock_guard l(watchers_mutex_); watchers_.erase(std::remove(watchers_.begin(), watchers_.end(), toBeDelete), watchers_.end()); delete toBeDelete; } void signal() { std::lock_guard l(watchers_mutex_); for (auto watcher : watchers_) { watcher->triggerChanges(); } } private: std::vector watchers_; std::shared_ptr value_; std::mutex watchers_mutex_; }; template class Repository : private Slots... { public: template std::shared_ptr get() { static_assert(std::is_base_of, Repository>::value, "Please ensure that this type or this key exists in this repository"); return Slot::doGet(); } template void set(const std::shared_ptr& value) { static_assert(std::is_base_of, Repository>::value, "Please ensure that this type or this key exists in this repository"); Slot::doSet(value); } template void emplace(Args&&... args) { static_assert(std::is_base_of, Repository>::value, "Please ensure that this type or this key exists in this repository"); Slot::doSet(std::make_shared(std::forward(args)...)); } template auto getWatcher() { static_assert(std::is_base_of, Repository>::value, "Please ensure that this type or this key exists in this repository"); return Slot::doGetWatcher(); } }; #endif // REPOSITORY_HPP