1 /* 2 * DQt - D bindings for the Qt Toolkit 3 * 4 * GNU Lesser General Public License Usage 5 * This file may be used under the terms of the GNU Lesser 6 * General Public License version 3 as published by the Free Software 7 * Foundation and appearing in the file LICENSE.LGPL3 included in the 8 * packaging of this file. Please review the following information to 9 * ensure the GNU Lesser General Public License version 3 requirements 10 * will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 11 */ 12 module qt.core.objectdefs_impl; 13 extern(C++): 14 15 import qt.config; 16 import qt.core.atomic; 17 import qt.core.object; 18 import qt.helpers; 19 20 /+ #ifndef QOBJECTDEFS_H 21 #error Do not include qobjectdefs_impl.h directly 22 #endif 23 24 #if 0 25 #pragma qt_sync_skip_header_check 26 #pragma qt_sync_stop_processing 27 #endif +/ 28 29 30 extern(C++, "QtPrivate") { 31 /+ template <typename T> struct RemoveRef { typedef T Type; }; 32 template <typename T> struct RemoveRef<T&> { typedef T Type; }; 33 template <typename T> struct RemoveConstRef { typedef T Type; }; 34 template <typename T> struct RemoveConstRef<const T&> { typedef T Type; }; 35 36 /* 37 The following List classes are used to help to handle the list of arguments. 38 It follow the same principles as the lisp lists. 39 List_Left<L,N> take a list and a number as a parameter and returns (via the Value typedef, 40 the list composed of the first N element of the list 41 */ 42 // With variadic template, lists are represented using a variadic template argument instead of the lisp way 43 template <typename...> struct List {}; 44 template <typename Head, typename... Tail> struct List<Head, Tail...> { typedef Head Car; typedef List<Tail...> Cdr; }; 45 template <typename, typename> struct List_Append; 46 template <typename... L1, typename...L2> struct List_Append<List<L1...>, List<L2...>> { typedef List<L1..., L2...> Value; }; 47 template <typename L, int N> struct List_Left { 48 typedef typename List_Append<List<typename L::Car>,typename List_Left<typename L::Cdr, N - 1>::Value>::Value Value; 49 }; 50 template <typename L> struct List_Left<L, 0> { typedef List<> Value; }; 51 // List_Select<L,N> returns (via typedef Value) the Nth element of the list L 52 template <typename L, int N> struct List_Select { typedef typename List_Select<typename L::Cdr, N - 1>::Value Value; }; 53 template <typename L> struct List_Select<L,0> { typedef typename L::Car Value; }; 54 55 /* 56 trick to set the return value of a slot that works even if the signal or the slot returns void 57 to be used like function(), ApplyReturnValue<ReturnType>(&return_value) 58 if function() returns a value, the operator,(T, ApplyReturnValue<ReturnType>) is called, but if it 59 returns void, the builtin one is used without an error. 60 */ 61 template <typename T> 62 struct ApplyReturnValue { 63 void *data; 64 explicit ApplyReturnValue(void *data_) : data(data_) {} 65 }; 66 template<typename T, typename U> 67 void operator,(T &&value, const ApplyReturnValue<U> &container) { 68 if (container.data) 69 *reinterpret_cast<U *>(container.data) = std::forward<T>(value); 70 } +/ 71 /+void operator ,(T)(T, ref const(ApplyReturnValue!(void)) ) {}+/ 72 73 74 /* 75 The FunctionPointer<Func> struct is a type trait for function pointer. 76 - ArgumentCount is the number of argument, or -1 if it is unknown 77 - the Object typedef is the Object of a pointer to member function 78 - the Arguments typedef is the list of argument (in a QtPrivate::List) 79 - the Function typedef is an alias to the template parameter Func 80 - the call<Args, R>(f,o,args) method is used to call that slot 81 Args is the list of argument of the signal 82 R is the return type of the signal 83 f is the function pointer 84 o is the receiver object 85 and args is the array of pointer to arguments, as used in qt_metacall 86 87 The Functor<Func,N> struct is the helper to call a functor of N argument. 88 its call function is the same as the FunctionPointer::call function. 89 */ 90 /+ template<class T> using InvokeGenSeq = typename T::Type; 91 92 template<int...> struct IndexesList { using Type = IndexesList; }; 93 94 template<int N, class S1, class S2> struct ConcatSeqImpl; 95 96 template<int N, int... I1, int... I2> 97 struct ConcatSeqImpl<N, IndexesList<I1...>, IndexesList<I2...>> 98 : IndexesList<I1..., (N + I2)...>{}; 99 100 template<int N, class S1, class S2> 101 using ConcatSeq = InvokeGenSeq<ConcatSeqImpl<N, S1, S2>>; 102 103 template<int N> struct GenSeq; 104 template<int N> using makeIndexSequence = InvokeGenSeq<GenSeq<N>>; 105 106 template<int N> 107 struct GenSeq : ConcatSeq<N/2, makeIndexSequence<N/2>, makeIndexSequence<N - N/2>>{}; 108 109 template<> struct GenSeq<0> : IndexesList<>{}; 110 template<> struct GenSeq<1> : IndexesList<0>{}; 111 112 template<int N> 113 struct Indexes { using Value = makeIndexSequence<N>; }; 114 115 template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; }; 116 117 template <typename, typename, typename, typename> struct FunctorCall; 118 template <int... II, typename... SignalArgs, typename R, typename Function> 119 struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> { 120 static void call(Function &f, void **arg) { 121 f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); 122 } 123 }; 124 template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> 125 struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> { 126 static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) { 127 (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); 128 } 129 }; 130 template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> 131 struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const> { 132 static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg) { 133 (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); 134 } 135 }; 136 #if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510 137 template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> 138 struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) noexcept> { 139 static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) { 140 (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); 141 } 142 }; 143 template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> 144 struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> { 145 static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) { 146 (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); 147 } 148 }; 149 #endif 150 151 template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)> 152 { 153 typedef Obj Object; 154 typedef List<Args...> Arguments; 155 typedef Ret ReturnType; 156 typedef Ret (Obj::*Function) (Args...); 157 enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; 158 template <typename SignalArgs, typename R> 159 static void call(Function f, Obj *o, void **arg) { 160 FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); 161 } 162 }; 163 template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const> 164 { 165 typedef Obj Object; 166 typedef List<Args...> Arguments; 167 typedef Ret ReturnType; 168 typedef Ret (Obj::*Function) (Args...) const; 169 enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; 170 template <typename SignalArgs, typename R> 171 static void call(Function f, Obj *o, void **arg) { 172 FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); 173 } 174 }; 175 176 template<typename Ret, typename... Args> struct FunctionPointer<Ret (*) (Args...)> 177 { 178 typedef List<Args...> Arguments; 179 typedef Ret ReturnType; 180 typedef Ret (*Function) (Args...); 181 enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false}; 182 template <typename SignalArgs, typename R> 183 static void call(Function f, void *, void **arg) { 184 FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, arg); 185 } 186 }; 187 188 #if defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510 189 template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) noexcept> 190 { 191 typedef Obj Object; 192 typedef List<Args...> Arguments; 193 typedef Ret ReturnType; 194 typedef Ret (Obj::*Function) (Args...) noexcept; 195 enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; 196 template <typename SignalArgs, typename R> 197 static void call(Function f, Obj *o, void **arg) { 198 FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); 199 } 200 }; 201 template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const noexcept> 202 { 203 typedef Obj Object; 204 typedef List<Args...> Arguments; 205 typedef Ret ReturnType; 206 typedef Ret (Obj::*Function) (Args...) const noexcept; 207 enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; 208 template <typename SignalArgs, typename R> 209 static void call(Function f, Obj *o, void **arg) { 210 FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); 211 } 212 }; 213 214 template<typename Ret, typename... Args> struct FunctionPointer<Ret (*) (Args...) noexcept> 215 { 216 typedef List<Args...> Arguments; 217 typedef Ret ReturnType; 218 typedef Ret (*Function) (Args...) noexcept; 219 enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false}; 220 template <typename SignalArgs, typename R> 221 static void call(Function f, void *, void **arg) { 222 FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, arg); 223 } 224 }; 225 #endif 226 227 template<typename Function, int N> struct Functor 228 { 229 template <typename SignalArgs, typename R> 230 static void call(Function &f, void *, void **arg) { 231 FunctorCall<typename Indexes<N>::Value, SignalArgs, R, Function>::call(f, arg); 232 } 233 }; +/ 234 235 // Traits to detect if there is a conversion between two types, 236 // and that conversion does not include a narrowing conversion. 237 struct NarrowingDetector(T) { T[1] t; } // from P0608 238 239 struct IsConvertibleWithoutNarrowing(From, To, Enable) { 240 /+ std:: +/false_type base0; 241 alias base0 this; 242 } 243 244 /+ template <typename From, typename To> 245 struct IsConvertibleWithoutNarrowing<From, To, 246 std::void_t< decltype( NarrowingDetector<To>{ {std::declval<From>()} } ) > 247 > : std::true_type {}; +/ 248 249 // Check for the actual arguments. If they are exactly the same, 250 // then don't bother checking for narrowing; as a by-product, 251 // this solves the problem of incomplete types (which must be supported, 252 // or they would error out in the trait above). 253 struct AreArgumentsConvertibleWithoutNarrowingBase(From, To, Enable) { 254 /+ std:: +/false_type base0; 255 alias base0 this; 256 } 257 258 /+ template <typename From, typename To> 259 struct AreArgumentsConvertibleWithoutNarrowingBase<From, To, 260 std::enable_if_t< 261 std::disjunction_v<std::is_same<From, To>, IsConvertibleWithoutNarrowing<From, To>> 262 > 263 > : std::true_type {}; 264 265 /* 266 Logic that check if the arguments of the slot matches the argument of the signal. 267 To be used like this: 268 static_assert(CheckCompatibleArguments<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::value) 269 */ 270 template<typename A1, typename A2> struct AreArgumentsCompatible { 271 static int test(const typename RemoveRef<A2>::Type&); 272 static char test(...); 273 static const typename RemoveRef<A1>::Type &dummy(); 274 enum { value = sizeof(test(dummy())) == sizeof(int) }; 275 #ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT 276 using AreArgumentsConvertibleWithoutNarrowing = AreArgumentsConvertibleWithoutNarrowingBase<std::decay_t<A1>, std::decay_t<A2>>; 277 static_assert(AreArgumentsConvertibleWithoutNarrowing::value, "Signal and slot arguments are not compatible (narrowing)"); 278 #endif 279 }; 280 template<typename A1, typename A2> struct AreArgumentsCompatible<A1, A2&> { enum { value = false }; }; 281 template<typename A> struct AreArgumentsCompatible<A&, A&> { enum { value = true }; }; 282 // void as a return value 283 template<typename A> struct AreArgumentsCompatible<void, A> { enum { value = true }; }; 284 template<typename A> struct AreArgumentsCompatible<A, void> { enum { value = true }; }; 285 template<> struct AreArgumentsCompatible<void, void> { enum { value = true }; }; 286 287 template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; }; 288 template <> struct CheckCompatibleArguments<List<>, List<>> { enum { value = true }; }; 289 template <typename List1> struct CheckCompatibleArguments<List1, List<>> { enum { value = true }; }; 290 template <typename Arg1, typename Arg2, typename... Tail1, typename... Tail2> 291 struct CheckCompatibleArguments<List<Arg1, Tail1...>, List<Arg2, Tail2...>> 292 { 293 enum { value = AreArgumentsCompatible<typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value 294 && CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>::value }; 295 }; 296 297 /* 298 Find the maximum number of arguments a functor object can take and be still compatible with 299 the arguments from the signal. 300 Value is the number of arguments, or -1 if nothing matches. 301 */ 302 template <typename Functor, typename ArgList> struct ComputeFunctorArgumentCount; 303 304 template <typename Functor, typename ArgList, bool Done> struct ComputeFunctorArgumentCountHelper 305 { enum { Value = -1 }; }; 306 template <typename Functor, typename First, typename... ArgList> 307 struct ComputeFunctorArgumentCountHelper<Functor, List<First, ArgList...>, false> 308 : ComputeFunctorArgumentCount<Functor, 309 typename List_Left<List<First, ArgList...>, sizeof...(ArgList)>::Value> {}; 310 311 template <typename Functor, typename... ArgList> struct ComputeFunctorArgumentCount<Functor, List<ArgList...>> 312 { 313 template <typename D> static D dummy(); 314 template <typename F> static auto test(F f) -> decltype(((f.operator()((dummy<ArgList>())...)), int())); 315 static char test(...); 316 enum { 317 Ok = sizeof(test(dummy<Functor>())) == sizeof(int), 318 Value = Ok ? int(sizeof...(ArgList)) : int(ComputeFunctorArgumentCountHelper<Functor, List<ArgList...>, Ok>::Value) 319 }; 320 }; 321 322 /* get the return type of a functor, given the signal argument list */ 323 template <typename Functor, typename ArgList> struct FunctorReturnType; 324 template <typename Functor, typename ... ArgList> struct FunctorReturnType<Functor, List<ArgList...>> { 325 template <typename D> static D dummy(); 326 typedef decltype(dummy<Functor>().operator()((dummy<ArgList>())...)) Value; 327 }; +/ 328 329 // internal base class (interface) containing functions required to call a slot managed by a pointer to function. 330 extern(C++, class) struct QSlotObjectBase { 331 private: 332 QAtomicInt m_ref; 333 // don't use virtual functions here; we don't want the 334 // compiler to create tons of per-polymorphic-class stuff that 335 // we'll never need. We just use one function pointer. 336 alias ImplFn = ExternCPPFunc!(void function(int which, QSlotObjectBase* this_, QObject receiver, void** args, bool* ret)); 337 const(ImplFn) m_impl; 338 protected: 339 enum Operation { 340 Destroy, 341 Call, 342 Compare, 343 344 NumOperations 345 } 346 public: 347 /+ explicit +/this(ImplFn fn) 348 { 349 this.m_ref = QAtomicInt(1); 350 this.m_impl = fn; 351 } 352 353 // pragma(inline, true) int ref_()/+ noexcept+/ { return m_ref.ref_(); } 354 /* pragma(inline, true) void destroyIfLastRef()/+ noexcept+/ 355 { if (!m_ref.deref()) m_impl(Operation.Destroy, &this, null, null, null); } 356 */ 357 pragma(inline, true) bool compare(void** a) { bool ret = false; m_impl(Operation.Compare, &this, null, a, &ret); return ret; } 358 /+ inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); } +/ 359 protected: 360 ~this() {} 361 private: 362 /+ Q_DISABLE_COPY_MOVE(QSlotObjectBase) +/ 363 @disable this(this); 364 /+this(ref const(QSlotObjectBase));+//+ref QSlotObjectBase operator =(ref const(QSlotObjectBase));+/ } 365 366 // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject 367 // Args and R are the List of arguments and the return type of the signal to which the slot is connected. 368 /+ template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase 369 { 370 typedef QtPrivate::FunctionPointer<Func> FuncType; 371 Func function; 372 static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) 373 { 374 switch (which) { 375 case Destroy: 376 delete static_cast<QSlotObject*>(this_); 377 break; 378 case Call: 379 FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a); 380 break; 381 case Compare: 382 *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function; 383 break; 384 case NumOperations: ; 385 } 386 } 387 public: 388 explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} 389 }; 390 // implementation of QSlotObjectBase for which the slot is a functor (or lambda) 391 // N is the number of arguments 392 // Args and R are the List of arguments and the return type of the signal to which the slot is connected. 393 template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase 394 { 395 typedef QtPrivate::Functor<Func, N> FuncType; 396 Func function; 397 static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) 398 { 399 switch (which) { 400 case Destroy: 401 delete static_cast<QFunctorSlotObject*>(this_); 402 break; 403 case Call: 404 FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a); 405 break; 406 case Compare: // not implemented 407 case NumOperations: 408 Q_UNUSED(ret); 409 } 410 } 411 public: 412 explicit QFunctorSlotObject(Func f) : QSlotObjectBase(&impl), function(std::move(f)) {} 413 }; 414 415 // typedefs for readability for when there are no parameters 416 template <typename Func> 417 using QSlotObjectWithNoArgs = QSlotObject<Func, 418 QtPrivate::List<>, 419 typename QtPrivate::FunctionPointer<Func>::ReturnType>; 420 421 template <typename Func, typename R> 422 using QFunctorSlotObjectWithNoArgs = QFunctorSlotObject<Func, 0, QtPrivate::List<>, R>; 423 424 template <typename Func> 425 using QFunctorSlotObjectWithNoArgsImplicitReturn = QFunctorSlotObjectWithNoArgs<Func, typename QtPrivate::FunctionPointer<Func>::ReturnType>; +/ 426 } 427 428 extern(D) struct DQtStaticSlotObject(Params...) 429 { 430 QSlotObjectBase base; 431 alias Dg = void delegate(Params); 432 Dg dg; 433 434 extern(C++) static void impl(int which, QSlotObjectBase *this_, QObject r, void **a, bool *ret) 435 { 436 import core.stdcpp.new_; 437 switch (which) { 438 case QSlotObjectBase.Operation.Destroy: 439 cpp_delete(cast(DQtStaticSlotObject*)(this_)); 440 break; 441 case QSlotObjectBase.Operation.Call: 442 (cast(DQtStaticSlotObject*)(this_)).dg(); 443 break; 444 case QSlotObjectBase.Operation.Compare: // not implemented 445 case QSlotObjectBase.Operation.NumOperations: 446 //Q_UNUSED(ret); 447 default: 448 } 449 } 450 public: 451 this(Dg dg) 452 { 453 base = QSlotObjectBase(&impl); 454 this.dg = dg; 455 } 456 } 457 458 extern(D) struct DQtMemberSlotObject(T, alias F, Params...) if(is(T: QObject)) 459 { 460 QSlotObjectBase base; 461 462 /* Need to use a custom mangling, because the template parameters 463 * may not have a C++ mangling. */ 464 pragma(mangle, DQtMemberSlotObject.mangleof ~ "__impl") 465 extern(C++) static void impl(int which, QSlotObjectBase *this_, QObject r, void **a, bool *ret) 466 { 467 import core.stdcpp.new_; 468 import std.traits; 469 switch (which) { 470 case QSlotObjectBase.Operation.Destroy: 471 cpp_delete(cast(DQtMemberSlotObject*)(this_)); 472 break; 473 case QSlotObjectBase.Operation.Call: 474 mixin("extern(" ~ functionLinkage!F ~ ") ReturnType!F delegate(Parameters!F) dg;"); 475 dg.ptr = cast(void*)r; 476 dg.funcptr = &F; 477 mixin((){ 478 import std.conv; 479 string r; 480 r = "dg("; 481 foreach(i; 0..Params.length) 482 { 483 r ~= text("*cast(Params[", i, "]*)a[", i + 1, "], "); 484 } 485 r ~= ");"; 486 return r; 487 }()); 488 break; 489 case QSlotObjectBase.Operation.Compare: // not implemented 490 case QSlotObjectBase.Operation.NumOperations: 491 //Q_UNUSED(ret); 492 default: 493 } 494 } 495 public: 496 @disable this(); 497 this(int dummy) 498 { 499 base = QSlotObjectBase(&impl); 500 } 501 }