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