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 
16 #ifndef META_BASE_INTERFACE_TRAITS_H
17 #define META_BASE_INTERFACE_TRAITS_H
18 
19 #include <base/containers/type_traits.h>
20 #include <base/util/uid.h>
21 #include <core/plugin/intf_interface.h>
22 
23 #include <meta/base/type_traits.h>
24 
BASE_BEGIN_NAMESPACE()25 BASE_BEGIN_NAMESPACE()
26 namespace Internals {
27 class ptr_base;
28 }
29 template<typename>
30 class shared_ptr;
31 
32 template<typename>
33 class weak_ptr;
34 BASE_END_NAMESPACE()
35 
META_BEGIN_NAMESPACE()36 META_BEGIN_NAMESPACE()
37 
38 template<class C>
39 class HasGetInterfaceMethod final {
40     template<class T>
41     static BASE_NS::true_type TestSignature(CORE_NS::IInterface* (T::*)(const BASE_NS::Uid&));
42 
43     template<class T>
44     static decltype(TestSignature((CORE_NS::IInterface * (T::*)(const BASE_NS::Uid&)) & T::GetInterface)) Test(
45         std::nullptr_t);
46 
47     template<class T>
48     static BASE_NS::false_type Test(...);
49 
50 public:
51     static constexpr bool value = decltype(Test<C>(nullptr))::value; // NOLINT(readability-identifier-naming)
52 };
53 
54 // NOLINTBEGIN(readability-identifier-naming)
55 /**
56  * @brief Check if type has GetInterface member functions (see IInterface).
57  */
58 template<class C>
59 inline constexpr bool HasGetInterfaceMethod_v = HasGetInterfaceMethod<C>::value;
60 
61 /**
62  * @brief Check if type is an interface, i.e. implements reference counting and GetInterface member functions.
63  */
64 template<class C>
65 inline constexpr bool IsKindOfInterface_v =
66     BASE_NS::is_convertible_v<BASE_NS::remove_const_t<C>*, CORE_NS::IInterface*>;
67 // NOLINTEND(readability-identifier-naming)
68 
69 // type trait for checking if the type has equality comparison
70 template<class T>
71 struct HasEqualOperator {
72     template<class U, class V>
73     static auto Test(U*) -> decltype(BASE_NS::declval<U>() == BASE_NS::declval<V>());
74     template<typename, typename>
75     static auto Test(...) -> BASE_NS::false_type;
76 
77     using type = typename BASE_NS::is_same<bool, decltype(Test<T, T>(nullptr))>::type;
78 };
79 
80 /**
81  * @brief Check if the type can be compared with equality operators.
82  */
83 template<class C>
84 inline constexpr bool HasEqualOperator_v = HasEqualOperator<C>::type::value; // NOLINT(readability-identifier-naming)
85 
86 template<class T>
87 struct HasInEqualOperator {
88     template<class U, class V>
89     static auto Test(U*) -> decltype(BASE_NS::declval<U>() != BASE_NS::declval<V>());
90     template<typename, typename>
91     static auto Test(...) -> BASE_NS::false_type;
92 
93     using type = typename BASE_NS::is_same<bool, decltype(Test<T, T>(nullptr))>::type;
94 };
95 
96 // NOLINTBEGIN(readability-identifier-naming)
97 /**
98  * @brief Check if the type can be compared with in-equality operators.
99  */
100 template<class C>
101 inline constexpr bool HasInEqualOperator_v = HasInEqualOperator<C>::type::value;
102 // NOLINTEND(readability-identifier-naming)
103 
104 // NOLINTBEGIN(readability-identifier-naming)
105 /**
106  * @brief Check if the type is "kind of" smart pointer, i.e. derived from BASE_NS::ptr_base.
107  */
108 template<typename type>
109 constexpr bool IsKindOfPointer_v =
110     BASE_NS::is_convertible_v<BASE_NS::remove_const_t<type&>, BASE_NS::Internals::ptr_base&>;
111 // NOLINTEND(readability-identifier-naming)
112 
113 // NOLINTBEGIN(readability-identifier-naming)
114 /**
115  * @brief Check if the type is a pointer which is convertible to IInterface pointer.
116  */
117 template<typename type>
118 constexpr bool IsKindOfIInterface_v = BASE_NS::is_convertible_v<BASE_NS::remove_const_t<type>, CORE_NS::IInterface*>;
119 // NOLINTEND(readability-identifier-naming)
120 
121 /**
122  * @brief SFINAE construct for checking if entity can be used as property bind function.
123  */
124 template<typename Func, typename Type>
125 using EnableIfProperBindFunction = decltype(BASE_NS::declval<void(const Type&)>()(BASE_NS::declval<Func>()()));
126 
127 template<typename T>
128 T CallTestFunc(const T&);
129 
130 template<typename Func>
131 using EnableIfBindFunction = decltype(CallTestFunc(BASE_NS::declval<Func>()()));
132 
133 template<typename Func, typename InvokeType, typename = void>
134 struct CanInvokeWithArgumentsImpl {
135     constexpr static bool value = false; // NOLINT(readability-identifier-naming)
136 };
137 
138 template<typename Func, typename Ret, typename... Args>
139 struct CanInvokeWithArgumentsImpl<Func, Ret(Args...),
140     decltype(BASE_NS::declval<Func>()(BASE_NS::declval<Args>()...), void())> {
141     constexpr static bool value = true; // NOLINT(readability-identifier-naming)
142 };
143 
144 // NOLINTBEGIN(readability-identifier-naming)
145 /**
146  * @brief Check if callable entity is compatible with given function signature.
147  */
148 template<typename Func, typename InvokeType>
149 constexpr bool CanInvokeWithArguments_v = CanInvokeWithArgumentsImpl<Func, InvokeType>::value;
150 // NOLINTEND(readability-identifier-naming)
151 
152 template<typename Func, typename InvokeType>
153 using EnableIfCanInvokeWithArguments = typename BASE_NS::enable_if_t<CanInvokeWithArguments_v<Func, InvokeType>>;
154 
155 template<typename Type>
156 using ToggleConst = BASE_NS::conditional_t<BASE_NS::is_const_v<Type>, BASE_NS::remove_const_t<Type>, const Type>;
157 
158 template<typename Type>
159 struct ToggleConstSharedPtrImpl;
160 
161 template<typename Type>
162 struct ToggleConstSharedPtrImpl<BASE_NS::shared_ptr<Type>> {
163     using type = BASE_NS::shared_ptr<const Type>;
164 };
165 
166 template<typename Type>
167 struct ToggleConstSharedPtrImpl<BASE_NS::shared_ptr<const Type>> {
168     using type = BASE_NS::shared_ptr<Type>;
169 };
170 
171 template<typename Type>
172 using ToggleConstSharedPtr = typename ToggleConstSharedPtrImpl<Type>::type;
173 
174 /*
175   We want to check if one can cast shared_ptr<BaseProp> to PropPtr. This requires in our case baseclass relation.
176   * (1) BaseProp and PropPtr are both const or non-const
177      - This case is easy, just check directly the conversion.
178   * (2) BaseProp is const and PropPtr is non-const
179      - This is not allowed case since we don't cast away constness
180   * (3) BaseProp is non-const and PropPtr is const
181      - This case is just adding a const
182 
183   Since PropPtr is the derived class, we need to see if we can convert that to the base class:
184     is_convertible<PropPtr, shared_ptr<BaseProp>> works for (1) and (2) but not for (3)
185   We want it to work for (1) and (3) but not (2), so we can achieve this by swapping the constness of the types.
186 */
187 
188 // NOLINTBEGIN(readability-identifier-naming)
189 template<typename BaseProp, typename PropPtr>
190 inline constexpr bool IsCompatibleBaseProperty_v =
191     BASE_NS::is_convertible_v<ToggleConstSharedPtr<PropPtr>, BASE_NS::shared_ptr<ToggleConst<BaseProp>>>;
192 // NOLINTEND(readability-identifier-naming)
193 
194 template<typename BaseProp, typename PropPtr>
195 using EnableIfCompatibleBaseProperty = BASE_NS::enable_if_t<IsCompatibleBaseProperty_v<BaseProp, PropPtr>>;
196 
197 /**
198  * @brief Check if type is shared_ptr
199  */
200 template<typename>
201 struct IsSharedPtr {
202     static constexpr bool value = false; // NOLINT(readability-identifier-naming)
203 };
204 
205 template<typename T>
206 struct IsSharedPtr<BASE_NS::shared_ptr<T>> {
207     static constexpr bool value = true; // NOLINT(readability-identifier-naming)
208     template<typename Type>
209     using rebind = BASE_NS::shared_ptr<Type>;
210 };
211 
212 template<typename>
213 struct IsWeakPtr {
214     static constexpr bool value = false; // NOLINT(readability-identifier-naming)
215 };
216 
217 template<typename T>
218 struct IsWeakPtr<BASE_NS::weak_ptr<T>> {
219     static constexpr bool value = true; // NOLINT(readability-identifier-naming)
220     template<typename Type>
221     using rebind = BASE_NS::weak_ptr<Type>;
222 };
223 
224 template<typename Type>
225 constexpr bool IsWeakPtr_v = IsWeakPtr<Type>::value; // NOLINT(readability-identifier-naming)
226 
227 /**
228  * @brief Check if type is shared_ptr or weak_ptr
229  */
230 template<typename>
231 struct IsSharedOrWeakPtr {
232     static constexpr bool value = false; // NOLINT(readability-identifier-naming)
233 };
234 
235 template<typename T>
236 struct IsSharedOrWeakPtr<BASE_NS::shared_ptr<T>> {
237     static constexpr bool value = true; // NOLINT(readability-identifier-naming)
238     template<typename Type>
239     using rebind = BASE_NS::shared_ptr<Type>;
240     static constexpr bool is_const = BASE_NS::is_const_v<T>; // NOLINT(readability-identifier-naming)
241 };
242 
243 template<typename T>
244 struct IsSharedOrWeakPtr<BASE_NS::weak_ptr<T>> {
245     static constexpr bool value = true; // NOLINT(readability-identifier-naming)
246     template<typename Type>
247     using rebind = BASE_NS::weak_ptr<Type>;
248     static constexpr bool is_const = BASE_NS::is_const_v<T>; // NOLINT(readability-identifier-naming)
249 };
250 
251 template<typename Type>
252 constexpr bool IsConstPtr_v = IsSharedOrWeakPtr<Type>::is_const; // NOLINT(readability-identifier-naming)
253 
254 /**
255  * @brief Check if type is shared_ptr or weak_ptr
256  */
257 template<typename T>
258 constexpr bool IsSharedOrWeakPtr_v = IsSharedOrWeakPtr<T>::value; // NOLINT(readability-identifier-naming)
259 
260 template<typename Type>
261 using InterfaceCheck = META_NS::BoolWrap<IsKindOfIInterface_v<BASE_NS::remove_const_t<typename Type::element_type>*>>;
262 
263 // NOLINTBEGIN(readability-identifier-naming)
264 template<typename Type>
265 constexpr bool IsInterfacePtr_v = IsSharedOrWeakPtr_v<Type> && META_NS::IsDetectedWithValue_v<InterfaceCheck, Type>;
266 // NOLINTEND(readability-identifier-naming)
267 
268 // NOLINTBEGIN(readability-identifier-naming)
269 template<typename Type>
270 constexpr bool IsInterfaceWeakPtr_v = IsInterfacePtr_v<Type> && IsWeakPtr<Type>::value;
271 // NOLINTEND(readability-identifier-naming)
272 
273 template<typename M>
274 struct FuncToSignature;
275 
276 template<typename Ret, typename Class, typename... Args>
277 struct FuncToSignature<Ret (Class::*)(Args...)> {
278     using type = Ret(Args...);
279     constexpr static bool IS_CONST = false;
280 };
281 
282 template<typename Ret, typename Class, typename... Args>
283 struct FuncToSignature<Ret (Class::*)(Args...) const> {
284     using type = Ret(Args...);
285     constexpr static bool IS_CONST = true;
286 };
287 
288 template<typename Ret, typename... Args>
289 struct FuncToSignature<Ret(Args...)> {
290     using type = Ret(Args...);
291     constexpr static bool IS_CONST = false;
292 };
293 
294 template<typename Ret, typename... Args>
295 struct FuncToSignature<Ret(Args...) const> {
296     using type = Ret(Args...);
297     constexpr static bool IS_CONST = true;
298 };
299 
300 template<typename M>
301 struct FuncToSignature : FuncToSignature<decltype(&M::operator())> {};
302 
303 /**
304  * @brief Convert member function types, function types and callable entity types to function signature.
305  * Note: Does not work with multiple overloads of operator().
306  */
307 template<typename M>
308 using FuncToSignature_t = typename FuncToSignature<M>::type;
309 
310 META_END_NAMESPACE()
311 
312 #endif
313