1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef META_API_EVENT_HANDLER_H
16 #define META_API_EVENT_HANDLER_H
17
18 #include <meta/api/deferred_callback.h>
19 #include <meta/api/make_callback.h>
20 #include <meta/base/interface_macros.h>
21 #include <meta/base/namespace.h>
22 #include <meta/interface/intf_task_queue_registry.h>
23 #include <meta/interface/property/intf_property.h>
24
META_BEGIN_NAMESPACE()25 META_BEGIN_NAMESPACE()
26
27 /**
28 * @brief The EventHandler class is a helper class for adding event handlers to events.
29 *
30 * EventHandler takes care of removing the event handler automatically
31 * when the target event changes or when the EventHandler object is destroyed.
32 *
33 * Calling Subscribe when already subscribed will remove the old subscription.
34 */
35 class EventHandler {
36 public:
37 META_NO_COPY(EventHandler)
38 EventHandler() noexcept = default;
39 virtual ~EventHandler()
40 {
41 Unsubscribe();
42 }
43
44 EventHandler(EventHandler&& other) noexcept
45 : event_ { BASE_NS::move(other.event_) }, token_ { BASE_NS::exchange(other.token_, {}) }
46 {}
47
48 EventHandler& operator=(EventHandler&& other) noexcept
49 {
50 if (&other != this) {
51 Unsubscribe();
52 event_ = BASE_NS::move(other.event_);
53 token_ = BASE_NS::exchange(other.token_, {});
54 }
55 return *this;
56 }
57
58 template<typename EventType>
59 EventHandler(const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func,
60 const ITaskQueue::Ptr& queue = nullptr)
61 {
62 Subscribe(event, func, queue);
63 }
64
65 template<typename EventType>
66 EventHandler(
67 const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func, const BASE_NS::Uid& queueId)
68 {
69 Subscribe(event, func, queueId);
70 }
71
72 EventHandler(const BASE_NS::shared_ptr<IEvent>& event, const ICallable::Ptr& func)
73 {
74 TypelessSubscribe(event, func);
75 }
76
77 template<typename EventType, typename Func,
78 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
79 EventHandler(const Event<EventType>& event, Func func, const ITaskQueue::Ptr& queue = nullptr)
80 {
81 Subscribe(event, BASE_NS::move(func), queue);
82 }
83
84 template<typename EventType, typename Func,
85 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
86 EventHandler(
87 const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func, const BASE_NS::Uid& queueId)
88 {
89 Subscribe(event, BASE_NS::move(func), queueId);
90 }
91
92 /**
93 * @brief Check if this handler has subscribed to an event
94 */
95 bool IsValid() const
96 {
97 return !event_.expired();
98 }
99
100 /**
101 * @brief Subscribe to an event.
102 * @param event Event to subscribe to.
103 * @param func Callable to invoke when event is triggered.
104 * @return True if the successfully subscribed.
105 */
106 bool TypelessSubscribe(const IEvent::Ptr& event, const ICallable::Ptr& func)
107 {
108 if (!event) {
109 CORE_LOG_E("Cannot subscribe to null event");
110 return false;
111 }
112 Unsubscribe();
113 token_ = event->AddHandler(func);
114 if (token_) {
115 event_ = event;
116 }
117 return token_;
118 }
119
120 /**
121 * @brief Subscribe to an event.
122 * @param event Event to subscribe to.
123 * @param func Callable to invoke when event is triggered.
124 * @oaram queue Optional queue where 'func' is invoked, if null, invoked directly.
125 * @return True if the successfully subscribed.
126 */
127 template<typename EventType>
128 bool Subscribe(const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func,
129 const ITaskQueue::Ptr& queue = nullptr)
130 {
131 return queue ? TypelessSubscribe(event,
132 MakeDeferred<typename EventType::InterfaceType>(
133 [func](auto&&... args) { func->Invoke(BASE_NS::forward<decltype(args)>(args)...); },
134 queue))
135 : TypelessSubscribe(event, func);
136 }
137
138 /**
139 * @brief Subscribe to an event.
140 * @param event Event to subscribe to.
141 * @param func Callable to invoke when event is triggered.
142 * @param queueId Queue id where 'func' is invoked. Fails if no such queue exists.
143 * @return True if the successfully subscribed.
144 */
145 template<typename EventType>
146 bool Subscribe(
147 const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func, const BASE_NS::Uid& queueId)
148 {
149 auto queue = GetTaskQueueRegistry().GetTaskQueue(queueId);
150 if (!queue) {
151 CORE_LOG_E("Cannot initialize event handler for queue %s: Queue not registered.",
152 BASE_NS::to_string(queueId).c_str());
153 }
154 return queue && Subscribe(event, func, queue);
155 }
156
157 /**
158 * @brief Subscribe to an event. See above Subscribe function for parameters
159 */
160 template<typename EventType, typename Func,
161 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
162 bool Subscribe(const Event<EventType>& event, Func func, const ITaskQueue::Ptr& queue = nullptr)
163 {
164 return Subscribe(event, MakeCallback<EventType>(BASE_NS::move(func)), queue);
165 }
166
167 /**
168 * @brief Subscribe to an event. See above Subscribe function for parameters
169 */
170 template<typename EventType, typename Func,
171 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
172 bool Subscribe(const Event<EventType>& event, Func func, const BASE_NS::Uid& queueId)
173 {
174 return Subscribe(event, MakeCallback<EventType>(BASE_NS::move(func)), queueId);
175 }
176
177 /**
178 * @brief Subscribe to an event. See above Subscribe function for parameters
179 */
180 template<typename EventType, typename Func,
181 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
182 bool Subscribe(const BASE_NS::shared_ptr<IEvent>& event, Func func, const ITaskQueue::Ptr& queue = nullptr)
183 {
184 auto ev = interface_pointer_cast<EventType>(event);
185 return ev && Subscribe<EventType>(event, MakeCallback<EventType>(BASE_NS::move(func)), queue);
186 }
187
188 /**
189 * @brief Subscribe to an event. See above Subscribe function for parameters
190 */
191 template<typename EventType, typename Func,
192 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
193 bool Subscribe(const BASE_NS::shared_ptr<IEvent>& event, Func func, const BASE_NS::Uid& queueId)
194 {
195 auto ev = interface_pointer_cast<EventType>(event);
196 return ev && Subscribe<EventType>(event, MakeCallback<EventType>(BASE_NS::move(func)), queueId);
197 }
198
199 /**
200 * @brief Unsubscribe from the event.
201 */
202 void Unsubscribe()
203 {
204 if (auto e = event_.lock()) {
205 e->RemoveHandler(token_);
206 event_ = nullptr;
207 token_ = {};
208 }
209 }
210
211 protected:
212 IEvent::WeakPtr event_;
213 IEvent::Token token_ {};
214 };
215
216 META_END_NAMESPACE()
217
218 #endif
219