event.hpp (3372B)
1 /* ReaPack: Package manager for REAPER 2 * Copyright (C) 2015-2025 Christian Fillion 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef REAPACK_EVENT_HPP 19 #define REAPACK_EVENT_HPP 20 21 #include <functional> 22 #include <future> 23 #include <map> 24 #include <memory> 25 #include <mutex> 26 #include <optional> 27 #include <vector> 28 29 template<typename T> 30 class Event; 31 32 template<typename R, typename... Args> 33 class Event<R(Args...)> { 34 public: 35 using Handler = std::function<R(Args...)>; 36 using ReturnType = std::conditional_t< 37 std::is_void_v<R>, void, std::optional<R> 38 >; 39 40 Event() = default; 41 Event(const Event &) = delete; 42 43 operator bool() const { return !m_handlers.empty(); } 44 void reset() { m_handlers.clear(); } 45 46 Event<R(Args...)> &operator>>(const Handler &func) 47 { 48 m_handlers.push_back(func); 49 return *this; 50 } 51 52 ReturnType operator()(Args... args) const 53 { 54 if constexpr (std::is_void_v<R>) { 55 for(const auto &func : m_handlers) 56 func(std::forward<Args>(args)...); 57 } 58 else { 59 ReturnType ret; 60 for(const auto &func : m_handlers) 61 ret = func(std::forward<Args>(args)...); 62 return ret; 63 } 64 } 65 66 private: 67 std::vector<Handler> m_handlers; 68 }; 69 70 namespace AsyncEventImpl { 71 using MainThreadFunc = std::function<void ()>; 72 73 class Loop { 74 public: 75 Loop(); 76 ~Loop(); 77 78 void push(const MainThreadFunc &, const void *source = nullptr); 79 void forget(const void *source); 80 81 private: 82 static void mainThreadTimer(); 83 void processQueue(); 84 85 std::mutex m_mutex; 86 std::multimap<const void *, MainThreadFunc> m_queue; 87 }; 88 89 class Emitter { 90 public: 91 Emitter(); 92 ~Emitter(); 93 94 void runInMainThread(const MainThreadFunc &) const; 95 96 private: 97 std::shared_ptr<Loop> m_loop; 98 }; 99 }; 100 101 template<typename T> 102 class AsyncEvent; 103 104 template<typename R, typename... Args> 105 class AsyncEvent<R(Args...)> : public Event<R(Args...)> { 106 public: 107 using typename Event<R(Args...)>::ReturnType; 108 109 std::future<ReturnType> operator()(Args... args) const 110 { 111 auto promise = std::make_shared<std::promise<ReturnType>>(); 112 113 // don't wait until the next timer tick to return nothing if there are no 114 // handlers currently subscribed to the event 115 if(!*this) { 116 if constexpr (std::is_void_v<R>) 117 promise->set_value(); 118 else 119 promise->set_value(std::nullopt); 120 121 return promise->get_future(); 122 } 123 124 m_emitter.runInMainThread([=] { 125 if constexpr (std::is_void_v<R>) { 126 Event<R(Args...)>::operator()(args...); 127 promise->set_value(); 128 } 129 else 130 promise->set_value(Event<R(Args...)>::operator()(args...)); 131 }); 132 133 return promise->get_future(); 134 } 135 136 private: 137 AsyncEventImpl::Emitter m_emitter; 138 }; 139 140 #endif