1 /*
2     SPDX-FileCopyrightText: 2007 Till Adam <adam@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "supertrait.h"
10 
11 #include <QSharedPointer>
12 
13 #include <memory>
14 #include <type_traits>
15 #include <typeinfo>
16 
17 #include "exceptionbase.h"
18 
19 /// @cond PRIVATE Doxygen 1.7.1 hangs processing this file. so skip it.
20 // for more info, see https://bugzilla.gnome.org/show_bug.cgi?id=531637
21 
22 /* WARNING
23  * The below is an implementation detail of the Item class. It is not to be
24  * considered public API, and subject to change without notice
25  */
26 
27 namespace Akonadi
28 {
29 namespace Internal
30 {
31 template<typename T> struct has_clone_method {
32 private:
33     template<typename S, S *(S::*)() const> struct sfinae {
34     };
35     struct No {
36     };
37     struct Yes {
38         No no[2];
39     };
40     template<typename S> static No test(...);
41     template<typename S> static Yes test(sfinae<S, &S::clone> *);
42 
43 public:
44     static const bool value = sizeof(test<T>(nullptr)) == sizeof(Yes);
45 };
46 
47 template<typename T, bool b> struct clone_traits_helper {
48     // runtime error (commented in) or compile time error (commented out)?
49     // ### runtime error, until we check has_clone_method in the
50     // ### Item::payload<T> impl directly...
cloneclone_traits_helper51     template<typename U> static T *clone(U)
52     {
53         return nullptr;
54     }
55 };
56 
57 template<typename T> struct clone_traits_helper<T, true> {
58     static T *clone(T *t)
59     {
60         return t ? t->clone() : nullptr;
61     }
62 };
63 
64 template<typename T> struct clone_traits : clone_traits_helper<T, has_clone_method<T>::value> {
65 };
66 
67 template<typename T> struct shared_pointer_traits {
68     static const bool defined = false;
69 };
70 
71 template<typename T> struct shared_pointer_traits<QSharedPointer<T>> {
72     static const bool defined = true;
73     using element_type = T;
74 
75     template<typename S> struct make {
76         using type = QSharedPointer<S>;
77     };
78 
79     using next_shared_ptr = std::shared_ptr<T>;
80 };
81 
82 template<typename T> struct shared_pointer_traits<std::shared_ptr<T>> {
83     static const bool defined = true;
84     using element_type = T;
85 
86     template<typename S> struct make {
87         using type = std::shared_ptr<S>;
88     };
89 
90     using next_shared_ptr = QSharedPointer<T>;
91 };
92 
93 template<typename T> struct is_shared_pointer {
94     static const bool value = shared_pointer_traits<T>::defined;
95 };
96 
97 template<typename T> struct identity {
98     using type = T;
99 };
100 
101 template<typename T> struct get_hierarchy_root;
102 
103 template<typename T, typename S> struct get_hierarchy_root_recurse : get_hierarchy_root<S> {
104 };
105 
106 template<typename T> struct get_hierarchy_root_recurse<T, T> : identity<T> {
107 };
108 
109 template<typename T> struct get_hierarchy_root : get_hierarchy_root_recurse<T, typename Akonadi::SuperClass<T>::Type> {
110 };
111 
112 template<typename T> struct get_hierarchy_root<QSharedPointer<T>> {
113     using type = QSharedPointer<typename get_hierarchy_root<T>::type>;
114 };
115 
116 template<typename T> struct get_hierarchy_root<std::shared_ptr<T>> {
117     using type = std::shared_ptr<typename get_hierarchy_root<T>::type>;
118 };
119 
120 /**
121   @internal
122   Payload type traits. Implements specialized handling for polymorphic types and smart pointers.
123   The default one is never used (as isPolymorphic is always false) and only contains safe dummy
124   implementations to make the compiler happy (in practice it will always optimized away anyway).
125 */
126 template<typename T> struct PayloadTrait {
127     /// type of the payload object contained inside a shared pointer
128     using ElementType = T;
129     // the metatype id for the element type, or for pointer-to-element
130     // type, if in a shared pointer
131     static int elementMetaTypeId()
132     {
133         return qMetaTypeId<T>();
134     }
135     /// type of the base class of the payload object inside a shared pointer,
136     /// same as ElementType if there is no super class
137     using SuperElementType = typename Akonadi::SuperClass<T>::Type;
138     /// type of this payload object
139     using Type = T;
140     /// type of the payload to store a base class of this payload
141     /// (eg. a shared pointer containing a pointer to SuperElementType)
142     /// same as Type if there is not super class
143     using SuperType = typename Akonadi::SuperClass<T>::Type;
144     /// indicates if this payload is polymorphic, that it is a shared pointer
145     /// and has a known super class
146     static const bool isPolymorphic = false;
147     /// checks an object of this payload type for being @c null
148     static inline bool isNull(const Type &p)
149     {
150         Q_UNUSED(p)
151         return true;
152     }
153     /// casts to Type from @c U
154     /// throws a PayloadException if casting failed
155     template<typename U> static inline Type castFrom(const U &)
156     {
157         throw PayloadException("you should never get here");
158     }
159     /// tests if casting from @c U to Type is possible
160     template<typename U> static inline bool canCastFrom(const U &)
161     {
162         return false;
163     }
164     /// cast to @c U from Type
165     template<typename U> static inline U castTo(const Type &)
166     {
167         throw PayloadException("you should never get here");
168     }
169     template<typename U> static T clone(const U &)
170     {
171         throw PayloadException("clone: you should never get here");
172     }
173     /// defines the type of shared pointer used (0: none, > 0: std::shared_ptr, QSharedPointer, ...)
174     static const unsigned int sharedPointerId = 0;
175 };
176 
177 /**
178   @internal
179   Payload type trait specialization for QSharedPointer
180   for documentation of the various members, see above
181 */
182 template<typename T> struct PayloadTrait<QSharedPointer<T>> {
183     using ElementType = T;
184     static int elementMetaTypeId()
185     {
186         return qMetaTypeId<T *>();
187     }
188     using SuperElementType = typename Akonadi::SuperClass<T>::Type;
189     using Type = QSharedPointer<ElementType>;
190     using SuperType = QSharedPointer<SuperElementType>;
191     static const bool isPolymorphic = !std::is_same<ElementType, SuperElementType>::value;
192     static inline bool isNull(const Type &p)
193     {
194         return p.isNull();
195     }
196     template<typename U> static inline Type castFrom(const QSharedPointer<U> &p)
197     {
198         const Type sp = qSharedPointerDynamicCast<T, U>(p);
199         if (!sp.isNull() || p.isNull()) {
200             return sp;
201         }
202         throw PayloadException("qSharedPointerDynamicCast failed");
203     }
204     template<typename U> static inline bool canCastFrom(const QSharedPointer<U> &p)
205     {
206         const Type sp = qSharedPointerDynamicCast<T, U>(p);
207         return !sp.isNull() || p.isNull();
208     }
209     template<typename U> static inline QSharedPointer<U> castTo(const Type &p)
210     {
211         const QSharedPointer<U> sp = qSharedPointerDynamicCast<U, T>(p);
212         return sp;
213     }
214     static QSharedPointer<T> clone(const std::shared_ptr<T> &t)
215     {
216         if (T *nt = clone_traits<T>::clone(t.get())) {
217             return QSharedPointer<T>(nt);
218         } else {
219             return QSharedPointer<T>();
220         }
221     }
222     static const unsigned int sharedPointerId = 2;
223 };
224 
225 /**
226   @internal
227   Payload type trait specialization for std::shared_ptr
228   for documentation of the various members, see above
229 */
230 template<typename T> struct PayloadTrait<std::shared_ptr<T>> {
231     using ElementType = T;
232     static int elementMetaTypeId()
233     {
234         return qMetaTypeId<T *>();
235     }
236     using SuperElementType = typename Akonadi::SuperClass<T>::Type;
237     using Type = std::shared_ptr<ElementType>;
238     using SuperType = std::shared_ptr<SuperElementType>;
239     static const bool isPolymorphic = !std::is_same<ElementType, SuperElementType>::value;
240     static inline bool isNull(const Type &p)
241     {
242         return p.get() == nullptr;
243     }
244     template<typename U> static inline Type castFrom(const std::shared_ptr<U> &p)
245     {
246         const Type sp = std::dynamic_pointer_cast<T, U>(p);
247         if (sp.get() != nullptr || p.get() == nullptr) {
248             return sp;
249         }
250         throw PayloadException("std::dynamic_pointer_cast failed");
251     }
252     template<typename U> static inline bool canCastFrom(const std::shared_ptr<U> &p)
253     {
254         const Type sp = std::dynamic_pointer_cast<T, U>(p);
255         return sp.get() != nullptr || p.get() == nullptr;
256     }
257     template<typename U> static inline std::shared_ptr<U> castTo(const Type &p)
258     {
259         const std::shared_ptr<U> sp = std::dynamic_pointer_cast<U, T>(p);
260         return sp;
261     }
262     static std::shared_ptr<T> clone(const QSharedPointer<T> &t)
263     {
264         if (T *nt = clone_traits<T>::clone(t.data())) {
265             return std::shared_ptr<T>(nt);
266         } else {
267             return std::shared_ptr<T>();
268         }
269     }
270     static const unsigned int sharedPointerId = 3;
271 };
272 
273 /**
274  * @internal
275  * Non-template base class for the payload container.
276  */
277 struct PayloadBase {
278     virtual ~PayloadBase() = default;
279     virtual PayloadBase *clone() const = 0;
280     virtual const char *typeName() const = 0;
281 
282 protected:
283     PayloadBase() = default;
284 
285 private:
286     Q_DISABLE_COPY_MOVE(PayloadBase)
287 };
288 
289 /**
290  * @internal
291  * Container for the actual payload object.
292  */
293 template<typename T> struct Payload : public PayloadBase {
294     Payload(const T &p)
295         : payload(p)
296     {
297     }
298 
299     PayloadBase *clone() const override
300     {
301         return new Payload<T>(const_cast<Payload<T> *>(this)->payload);
302     }
303 
304     const char *typeName() const override
305     {
306         return typeid(const_cast<Payload<T> *>(this)).name();
307     }
308 
309     T payload;
310 };
311 
312 /**
313  * @internal
314  * abstract, will therefore always fail to compile for pointer payloads
315  */
316 template<typename T> struct Payload<T *> : public PayloadBase {
317 };
318 
319 /**
320   @internal
321   Basically a dynamic_cast that also works across DSO boundaries.
322 */
323 template<typename T> inline Payload<T> *payload_cast(PayloadBase *payloadBase)
324 {
325     auto p = dynamic_cast<Payload<T> *>(payloadBase);
326     // try harder to cast, workaround for some gcc issue with template instances in multiple DSO's
327     if (!p && payloadBase && strcmp(payloadBase->typeName(), typeid(p).name()) == 0) {
328         p = static_cast<Payload<T> *>(payloadBase);
329     }
330     return p;
331 }
332 
333 } // namespace Internal
334 
335 } // namespace Akonadi
336 
337 /// @endcond
338 
339