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 EventHandler(EventHandler&& other) noexcept
44 : event_ { BASE_NS::move(other.event_) }, token_ { BASE_NS::exchange(other.token_, {}) }
45 {}
46 EventHandler& operator=(EventHandler&& other) noexcept
47 {
48 if (&other != this) {
49 Unsubscribe();
50 event_ = BASE_NS::move(other.event_);
51 token_ = BASE_NS::exchange(other.token_, {});
52 }
53 return *this;
54 }
55
56 template<typename EventType>
57 EventHandler(const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func,
58 const ITaskQueue::Ptr& queue = nullptr)
59 {
60 Subscribe(event, func, queue);
61 }
62
63 template<typename EventType>
64 EventHandler(
65 const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func, const BASE_NS::Uid& queueId)
66 {
67 Subscribe(event, func, queueId);
68 }
69
70 EventHandler(const BASE_NS::shared_ptr<IEvent>& event, const ICallable::Ptr& func)
71 {
72 TypelessSubscribe(event, func);
73 }
74
75 template<typename EventType, typename Func,
76 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
77 EventHandler(const Event<EventType>& event, Func func, const ITaskQueue::Ptr& queue = nullptr)
78 {
79 Subscribe(event, BASE_NS::move(func), queue);
80 }
81
82 template<typename EventType, typename Func,
83 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
84 EventHandler(
85 const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func, const BASE_NS::Uid& queueId)
86 {
87 Subscribe(event, BASE_NS::move(func), queueId);
88 }
89
90 /**
91 * @brief Check if this handler has subscribed to an event
92 */
93 bool IsValid() const
94 {
95 return !event_.expired();
96 }
97
98 /**
99 * @brief Subscribe to an event.
100 * @param event Event to subscribe to.
101 * @param func Callable to invoke when event is triggered.
102 * @return True if the successfully subscribed.
103 */
104 bool TypelessSubscribe(const IEvent::Ptr& event, const ICallable::Ptr& func)
105 {
106 if (!event) {
107 CORE_LOG_E("Cannot subscribe to null event");
108 return false;
109 }
110 Unsubscribe();
111 token_ = event->AddHandler(func);
112 if (token_) {
113 event_ = event;
114 }
115 return token_;
116 }
117
118 /**
119 * @brief Subscribe to an event.
120 * @param event Event to subscribe to.
121 * @param func Callable to invoke when event is triggered.
122 * @oaram queue Optional queue where 'func' is invoked, if null, invoked directly.
123 * @return True if the successfully subscribed.
124 */
125 template<typename EventType>
126 bool Subscribe(const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func,
127 const ITaskQueue::Ptr& queue = nullptr)
128 {
129 return queue ? TypelessSubscribe(event,
130 MakeDeferred<typename EventType::InterfaceType>(
131 [func](auto&&... args) { func->Invoke(BASE_NS::forward<decltype(args)>(args)...); },
132 queue))
133 : TypelessSubscribe(event, func);
134 }
135
136 /**
137 * @brief Subscribe to an event.
138 * @param event Event to subscribe to.
139 * @param func Callable to invoke when event is triggered.
140 * @param queueId Queue id where 'func' is invoked. Fails if no such queue exists.
141 * @return True if the successfully subscribed.
142 */
143 template<typename EventType>
144 bool Subscribe(
145 const Event<EventType>& event, const typename EventType::InterfaceTypePtr& func, const BASE_NS::Uid& queueId)
146 {
147 auto queue = GetTaskQueueRegistry().GetTaskQueue(queueId);
148 if (!queue) {
149 CORE_LOG_E("Cannot initialize event handler for queue %s: Queue not registered.",
150 BASE_NS::to_string(queueId).c_str());
151 }
152 return queue && Subscribe(event, func, queue);
153 }
154
155 /**
156 * @brief Subscribe to an event. See above Subscribe function for parameters
157 */
158 template<typename EventType, typename Func,
159 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
160 bool Subscribe(const Event<EventType>& event, Func func, const ITaskQueue::Ptr& queue = nullptr)
161 {
162 return Subscribe(event, MakeCallback<EventType>(BASE_NS::move(func)), queue);
163 }
164
165 /**
166 * @brief Subscribe to an event. See above Subscribe function for parameters
167 */
168 template<typename EventType, typename Func,
169 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
170 bool Subscribe(const Event<EventType>& event, Func func, const BASE_NS::Uid& queueId)
171 {
172 return Subscribe(event, MakeCallback<EventType>(BASE_NS::move(func)), queueId);
173 }
174
175 /**
176 * @brief Subscribe to an event. See above Subscribe function for parameters
177 */
178 template<typename EventType, typename Func,
179 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
180 bool Subscribe(const BASE_NS::shared_ptr<IEvent>& event, Func func, const ITaskQueue::Ptr& queue = nullptr)
181 {
182 auto ev = interface_pointer_cast<EventType>(event);
183 return ev && Subscribe<EventType>(event, MakeCallback<EventType>(BASE_NS::move(func)), queue);
184 }
185
186 /**
187 * @brief Subscribe to an event. See above Subscribe function for parameters
188 */
189 template<typename EventType, typename Func,
190 typename = EnableIfCanInvokeWithArguments<Func, typename EventType::FunctionType>>
191 bool Subscribe(const BASE_NS::shared_ptr<IEvent>& event, Func func, const BASE_NS::Uid& queueId)
192 {
193 auto ev = interface_pointer_cast<EventType>(event);
194 return ev && Subscribe<EventType>(event, MakeCallback<EventType>(BASE_NS::move(func)), queueId);
195 }
196
197 /**
198 * @brief Unsubscribe from the event.
199 */
200 void Unsubscribe()
201 {
202 if (auto e = event_.lock()) {
203 e->RemoveHandler(token_);
204 event_ = nullptr;
205 token_ = {};
206 }
207 }
208
209 protected:
210 IEvent::WeakPtr event_;
211 IEvent::Token token_ {};
212 };
213
214 META_END_NAMESPACE()
215
216 #endif
217