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