diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2017-08-17 19:01:40 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2017-08-24 23:54:43 +0000 |
commit | a980250a666bc87e5db006b8668c6af9340915f2 (patch) | |
tree | d9c3f6eca8a9941cf734dca4deea07f272515835 /src/plugins/platforms/cocoa/qcocoahelpers.h | |
parent | f537dc0da288949c4df903c1f2b21156e62fbae5 (diff) |
macOS: Deduplicate QNSWindow/QNSPanel code
By sharing the implementations of the methods between QNSWindow and
QNSPanel we don't need a helper, and can remove duplicated code. This
duplication would expand in the future, as for each method added to
the QNSWindowProtocol, we would have to add forwarding functions
in both QNSWindow and QNSPanel, forwarding to QNSWindowHelper, and
then two more functions in QNSWindow and QNSPanel in case we wanted
to call super from the helper, similar to [QNSWindow superSendEvent].
The only snag is that calls to super are hard-coded to a specific
superclass during complication, so we provide our wrapper for
objc_msgSendSuper that resolves the superclass at runtime.
The helper class QSendSuperHelper provides compile time implicit
instantiation of the right template without having to provide
the return type as a template argument, via operator T and
a fallback for the case of no return type via the destructor.
Change-Id: Iaf13f27675d90f884470f5005270ea0d9d0316f3
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoahelpers.h')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoahelpers.h | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 4478895538..1f4f9cd276 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -55,6 +55,9 @@ #include <QtGui/qpalette.h> #include <QtGui/qscreen.h> +#include <objc/runtime.h> +#include <objc/message.h> + Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView)); QT_BEGIN_NAMESPACE @@ -188,5 +191,131 @@ QT_END_NAMESPACE QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper); +// ------------------------------------------------------------------------- + +// Depending on the ABI of the platform, we may need to use objc_msgSendSuper_stret: +// - http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html +// - https://lists.apple.com/archives/cocoa-dev/2008/Feb/msg02338.html +template <typename T> +struct objc_msgsend_requires_stret +{ static const bool value = +#if defined(Q_PROCESSOR_X86) + // Any return value larger than two registers on i386/x86_64 + sizeof(T) > sizeof(void*) * 2; +#elif defined(Q_PROCESSOR_ARM_32) + // Any return value larger than a single register on arm + sizeof(T) > sizeof(void*); +#elif defined(Q_PROCESSOR_ARM_64) + // Stret not used on arm64 + false; +#endif +}; + +template <> +struct objc_msgsend_requires_stret<void> +{ static const bool value = false; }; + +template <typename ReturnType, typename... Args> +ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args) +{ + static_assert(!objc_msgsend_requires_stret<ReturnType>::value, + "The given return type requires stret on this platform"); + + typedef ReturnType (*SuperFn)(objc_super *, SEL, Args...); + SuperFn superFn = reinterpret_cast<SuperFn>(objc_msgSendSuper); + objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) }; + return superFn(&sup, selector, args...); +} + +template <typename ReturnType, typename... Args> +ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args) +{ + static_assert(objc_msgsend_requires_stret<ReturnType>::value, + "The given return type does not use stret on this platform"); + + typedef void (*SuperStretFn)(ReturnType *, objc_super *, SEL, Args...); + SuperStretFn superStretFn = reinterpret_cast<SuperStretFn>(objc_msgSendSuper_stret); + + objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) }; + ReturnType ret; + superStretFn(&ret, &sup, selector, args...); + return ret; +} + +template<typename... Args> +class QSendSuperHelper { +public: + QSendSuperHelper(id receiver, SEL sel, Args... args) + : m_receiver(receiver), m_selector(sel), m_args(std::make_tuple(args...)), m_sent(false) + { + } + + ~QSendSuperHelper() + { + if (!m_sent) + msgSendSuper<void>(m_args); + } + + template <typename ReturnType> + operator ReturnType() + { +#if defined(QT_DEBUG) + Method method = class_getInstanceMethod(object_getClass(m_receiver), m_selector); + char returnTypeEncoding[256]; + method_getReturnType(method, returnTypeEncoding, sizeof(returnTypeEncoding)); + NSUInteger alignedReturnTypeSize = 0; + NSGetSizeAndAlignment(returnTypeEncoding, nullptr, &alignedReturnTypeSize); + Q_ASSERT(alignedReturnTypeSize == sizeof(ReturnType)); +#endif + m_sent = true; + return msgSendSuper<ReturnType>(m_args); + } + +private: + template <std::size_t... Ts> + struct index {}; + + template <std::size_t N, std::size_t... Ts> + struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {}; + + template <std::size_t... Ts> + struct gen_seq<0, Ts...> : index<Ts...> {}; + + template <typename ReturnType, bool V> + using if_requires_stret = typename std::enable_if<objc_msgsend_requires_stret<ReturnType>::value == V, ReturnType>::type; + + template <typename ReturnType, std::size_t... Is> + if_requires_stret<ReturnType, false> msgSendSuper(std::tuple<Args...>& args, index<Is...>) + { + return qt_msgSendSuper<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...); + } + + template <typename ReturnType, std::size_t... Is> + if_requires_stret<ReturnType, true> msgSendSuper(std::tuple<Args...>& args, index<Is...>) + { + return qt_msgSendSuper_stret<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...); + } + + template <typename ReturnType> + ReturnType msgSendSuper(std::tuple<Args...>& args) + { + return msgSendSuper<ReturnType>(args, gen_seq<sizeof...(Args)>{}); + } + + id m_receiver; + SEL m_selector; + std::tuple<Args...> m_args; + bool m_sent; +}; + +template<typename... Args> +QSendSuperHelper<Args...> qt_objcDynamicSuperHelper(id receiver, SEL selector, Args... args) +{ + return QSendSuperHelper<Args...>(receiver, selector, args...); +} + +// Same as calling super, but the super_class field resolved at runtime instead of compile time +#define qt_objcDynamicSuper(...) qt_objcDynamicSuperHelper(self, _cmd, ##__VA_ARGS__) + #endif //QCOCOAHELPERS_H |