From 02639e5d04f940f37f30dca77dfd240e7edf70d1 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 28 Feb 2018 16:48:46 +0100 Subject: [PATCH] QskMeta classes improved --- src/common/QskMetaCallback.cpp | 213 +++++++++++++++++++++++--------- src/common/QskMetaCallback.h | 36 ++++-- src/common/QskMetaFunction.cpp | 22 ++-- src/common/QskMetaFunction.h | 51 +++++++- src/common/QskMetaInvokable.cpp | 25 ++++ src/common/QskMetaInvokable.h | 97 +++++++++++---- 6 files changed, 336 insertions(+), 108 deletions(-) diff --git a/src/common/QskMetaCallback.cpp b/src/common/QskMetaCallback.cpp index 684b45f9..83a6dbb9 100644 --- a/src/common/QskMetaCallback.cpp +++ b/src/common/QskMetaCallback.cpp @@ -4,11 +4,13 @@ *****************************************************************************/ #include "QskMetaCallback.h" +#include "QskMetaFunction.h" #include #include #include #include +#include QSK_QT_PRIVATE_BEGIN #include @@ -31,42 +33,49 @@ static inline void qskInvokeMethodQueued( QObject* object, QskMetaCallback::QskMetaCallback( const QObject* object, const QMetaMethod& method, Qt::ConnectionType connectionType ): m_object( const_cast< QObject* >( object ) ), - m_method( new QMetaMethod( method ) ), + m_methodData { method.enclosingMetaObject(), method.methodIndex() }, m_type( MetaMethod ), - m_connectionType( static_cast< ushort >( connectionType & ~Qt::UniqueConnection ) ) + m_hasObject( object != nullptr ), + m_connectionType( static_cast< ushort >( connectionType & 0x3 ) ) { } QskMetaCallback::QskMetaCallback( const QObject* object, const QskMetaFunction& function, Qt::ConnectionType connectionType ): m_object( const_cast< QObject* >( object ) ), - m_function( new QskMetaFunction( function ) ), + m_functionData { function.invokable(), function.parameterTypes() }, m_type( MetaFunction ), + m_hasObject( object != nullptr ), m_connectionType( static_cast< ushort >( connectionType & ~Qt::UniqueConnection ) ) { + if ( m_functionData.invokable ) + m_functionData.invokable->ref(); } QskMetaCallback::QskMetaCallback( const QskMetaCallback& other ): m_object( other.m_object ), - m_function(), - m_type( Invalid ), + m_type( other.m_type ), + m_hasObject( other.m_hasObject ), m_connectionType( other.m_connectionType ) { - if ( other.m_type != m_type ) - { - reset(); - m_type = other.m_type; - } - switch( m_type ) { case MetaMethod: - m_method = new QMetaMethod( *other.m_method ); - break; + { + m_methodData.metaObject = other.m_methodData.metaObject; + m_methodData.methodIndex = other.m_methodData.methodIndex; - case MetaFunction: - m_function = new QskMetaFunction( *other.m_function ); break; + } + case MetaFunction: + { + m_functionData.invokable = other.m_functionData.invokable; + if ( m_functionData.invokable ) + m_functionData.invokable->ref(); + + m_functionData.parameterTypes = other.m_functionData.parameterTypes; + break; + } default: break; @@ -75,31 +84,82 @@ QskMetaCallback::QskMetaCallback( const QskMetaCallback& other ): QskMetaCallback::~QskMetaCallback() { - reset(); + if ( ( m_type == MetaFunction ) && m_functionData.invokable ) + m_functionData.invokable->destroyIfLastRef(); } QskMetaCallback& QskMetaCallback::operator=( const QskMetaCallback& other ) { m_object = other.m_object; + m_hasObject = other.m_hasObject; + m_connectionType = other.m_connectionType; - if ( other.m_type != m_type ) + switch( other.m_type ) { - reset(); - m_type = other.m_type; + case MetaMethod: + { + if ( m_type == MetaFunction && m_functionData.invokable ) + m_functionData.invokable->destroyIfLastRef(); + + m_methodData.metaObject = other.m_methodData.metaObject; + m_methodData.methodIndex = other.m_methodData.methodIndex; + + break; + } + case MetaFunction: + { + if ( ( m_type == MetaFunction ) && m_functionData.invokable ) + m_functionData.invokable->destroyIfLastRef(); + + m_functionData.invokable = other.m_functionData.invokable; + + if ( m_functionData.invokable ) + m_functionData.invokable->ref(); + + m_functionData.parameterTypes = other.m_functionData.parameterTypes; + break; + } + + default: + if ( ( m_type == MetaFunction ) && m_functionData.invokable ) + m_functionData.invokable->destroyIfLastRef(); } - if ( other.m_type != Invalid ) - { - if ( other.m_type == MetaMethod ) - m_method = new QMetaMethod( *other.m_method ); - else - m_function = new QskMetaFunction( *other.m_function ); - } + m_type = other.m_type; return *this; } +bool QskMetaCallback::isValid() const +{ + if ( m_hasObject && m_object.isNull() ) + return false; + + switch( m_type ) + { + case MetaMethod: + { + const auto& d = m_methodData; + if ( d.metaObject && ( d.methodIndex >= 0 ) + && ( d.methodIndex < d.metaObject->methodCount() ) ) + { + return true; + } + + return false; + } + + case MetaFunction: + { + return m_functionData.invokable != nullptr; + } + + default: + return false; + } +} + void QskMetaCallback::setConnectionType( Qt::ConnectionType connectionType ) { m_connectionType = connectionType; @@ -107,22 +167,13 @@ void QskMetaCallback::setConnectionType( Qt::ConnectionType connectionType ) void QskMetaCallback::reset() { - switch( m_type ) - { - case MetaMethod: - delete m_method; - m_method = nullptr; - break; + m_object = nullptr; + m_hasObject = false; - case MetaFunction: - delete m_function; - m_function = nullptr; - break; - - default: - break; - } + if ( m_type == MetaFunction && m_functionData.invokable ) + m_functionData.invokable->destroyIfLastRef(); + m_functionData = { nullptr, nullptr }; // for the debugger m_type = Invalid; } @@ -134,17 +185,25 @@ QVector< int > QskMetaCallback::parameterTypes() const { case MetaMethod: { - const int paramCount = m_method->parameterCount(); + const auto& d = m_methodData; + if ( m_methodData.metaObject ) + { +#if 1 + // should be doable without QMetaMethod. TODO ... + const auto method = d.metaObject->method( d.methodIndex ); +#endif + const int paramCount = method.parameterCount(); - paramTypes.reserve( paramCount ); - for ( int i = 0; i < paramCount; i++ ) - paramTypes += m_method->parameterType( i ); + paramTypes.reserve( paramCount ); + for ( int i = 0; i < paramCount; i++ ) + paramTypes += method.parameterType( i ); + } break; } case MetaFunction: { - auto types = m_function->parameterTypes(); + auto types = m_functionData.parameterTypes; if ( types ) { while ( *types ) @@ -162,18 +221,25 @@ QVector< int > QskMetaCallback::parameterTypes() const void QskMetaCallback::invoke( void* args[] ) { + if ( !isValid() ) + return; + auto object = const_cast< QObject* >( m_object.data() ); switch( m_type ) { case MetaMethod: { - qskInvokeMethod( object, *m_method, args, connectionType() ); + qskInvokeMethod( object, m_methodData.metaObject, + m_methodData.methodIndex, args, connectionType() ); + break; } case MetaFunction: { - m_function->invoke( object, args, connectionType() ); + QskMetaFunction function( m_functionData.invokable, m_functionData.parameterTypes ); + function.invoke( object, args, connectionType() ); + break; } @@ -182,30 +248,54 @@ void QskMetaCallback::invoke( void* args[] ) } } - void qskInvokeMethod( QObject* object, const QMetaMethod& method, void* args[], Qt::ConnectionType connectionType ) { - if ( object == nullptr ) + auto metaObject = method.enclosingMetaObject(); + if ( metaObject == nullptr ) return; - auto metaObject = method.enclosingMetaObject(); + const int methodIndex = method.methodIndex() - metaObject->methodOffset(); + qskInvokeMethod( object, metaObject, methodIndex, args, connectionType ); +} - const int methodOffset = metaObject->methodOffset(); - const int methodIndex = method.methodIndex() - methodOffset; - - if ( connectionType == Qt::AutoConnection ) +void qskInvokeMethod( QObject* object, + const QMetaObject* metaObject, int methodIndex, void* args[], + Qt::ConnectionType connectionType ) +{ + if ( ( metaObject == nullptr ) || ( methodIndex < 0 ) + || ( methodIndex > metaObject->methodCount() ) ) { - connectionType = ( object->thread() == QThread::currentThread() ) - ? Qt::DirectConnection : Qt::QueuedConnection; + return; } - if ( connectionType == Qt::DirectConnection ) + int invokeType = connectionType & 0x3; + + if ( invokeType == Qt::AutoConnection ) { + invokeType = ( object && object->thread() != QThread::currentThread() ) + ? Qt::QueuedConnection : Qt::DirectConnection; + } + else if ( invokeType == Qt::BlockingQueuedConnection ) + { + if ( ( object == nullptr ) || object->thread() == QThread::currentThread() ) + { + // We would end up in a deadlock, better do nothing + return; + } + } + + if ( invokeType == Qt::DirectConnection ) + { +#if 1 + if ( object == nullptr ) + return; // do we really need an object here ??? +#endif + if ( metaObject->d.static_metacall ) { - metaObject->d.static_metacall(object, + metaObject->d.static_metacall( object, QMetaObject::InvokeMetaMethod, methodIndex, args ); } else @@ -216,6 +306,13 @@ void qskInvokeMethod( QObject* object, } else { + if ( object == nullptr ) + return; + +#if 1 + // should be doable without QMetaMethod. TODO ... + const auto method = metaObject->method( methodIndex ); +#endif const int paramCount = method.parameterCount(); auto types = static_cast< int* >( malloc( paramCount * sizeof( int ) ) ); diff --git a/src/common/QskMetaCallback.h b/src/common/QskMetaCallback.h index 812424c9..0a5f7a62 100644 --- a/src/common/QskMetaCallback.h +++ b/src/common/QskMetaCallback.h @@ -7,13 +7,14 @@ #define QSK_META_CALLBACK_H 1 #include "QskGlobal.h" -#include "QskMetaFunction.h" -#include #include -#include #include +class QskMetaInvokable; +class QskMetaFunction; +class QMetaMethod; + class QskMetaCallback { public: @@ -59,26 +60,35 @@ private: QPointer< const QObject > m_object; + struct FunctionData + { + QskMetaInvokable* invokable; + const int* parameterTypes; + }; + + struct MethodData + { + const QMetaObject* metaObject; + int methodIndex; + }; + union { - QskMetaFunction* m_function; - QMetaMethod* m_method; + FunctionData m_functionData; + MethodData m_methodData; }; int m_type : 3; + bool m_hasObject : 1; ushort m_connectionType : 3; }; inline QskMetaCallback::QskMetaCallback(): - m_type( Invalid ) + m_type( Invalid ), + m_hasObject( false ) { } -inline bool QskMetaCallback::isValid() const -{ - return m_type > 0; -} - inline QskMetaCallback::Type QskMetaCallback::type() const { return static_cast< Type >( m_type ); @@ -93,6 +103,10 @@ QSK_EXPORT void qskInvokeMethod( QObject* object, const QMetaMethod&, void* args[], Qt::ConnectionType = Qt::AutoConnection ); +QSK_EXPORT void qskInvokeMethod( + QObject* object, const QMetaObject*, int methodIndex, void* args[], + Qt::ConnectionType = Qt::AutoConnection ); + Q_DECLARE_METATYPE( QskMetaCallback ) #endif diff --git a/src/common/QskMetaFunction.cpp b/src/common/QskMetaFunction.cpp index 4214c4e9..9f2d21ea 100644 --- a/src/common/QskMetaFunction.cpp +++ b/src/common/QskMetaFunction.cpp @@ -34,6 +34,15 @@ QskMetaFunction::QskMetaFunction(): { } +QskMetaFunction::QskMetaFunction( + QskMetaInvokable* invokable, const int* parameterTypes ): + m_invokable( invokable ), + m_parameterTypes( parameterTypes ) +{ + if ( m_invokable ) + m_invokable->ref(); +} + QskMetaFunction::QskMetaFunction( const QskMetaFunction& other ): m_invokable( other.m_invokable ), m_parameterTypes( other.m_parameterTypes ) @@ -96,11 +105,6 @@ void QskMetaFunction::init( QskMetaInvokable* invokable, m_parameterTypes = parameterTypes; } -const int* QskMetaFunction::parameterTypes() const -{ - return m_parameterTypes; -} - size_t QskMetaFunction::parameterCount() const { if ( m_parameterTypes ) @@ -134,12 +138,12 @@ void QskMetaFunction::invoke( if ( invokeType == Qt::AutoConnection ) { - invokeType = ( object->thread() == QThread::currentThread() ) - ? Qt::DirectConnection : Qt::QueuedConnection; + invokeType = ( object && object->thread() != QThread::currentThread() ) + ? Qt::QueuedConnection : Qt::DirectConnection; } else if ( invokeType == Qt::BlockingQueuedConnection ) { - if ( object->thread() == QThread::currentThread() ) + if ( ( object == nullptr ) || object->thread() == QThread::currentThread() ) { // We would end up in a deadlock, better do nothing return; @@ -202,3 +206,5 @@ void QskMetaFunction::invoke( } } } + +#include "moc_QskMetaFunction.cpp" diff --git a/src/common/QskMetaFunction.h b/src/common/QskMetaFunction.h index 9185ada7..1e18c78b 100644 --- a/src/common/QskMetaFunction.h +++ b/src/common/QskMetaFunction.h @@ -78,13 +78,51 @@ public: Type functionType() const; -private: +protected: + friend class QskMetaCallback; + + QskMetaFunction( QskMetaInvokable*, const int* ); + void init( QskMetaInvokable*, const int* ); + QskMetaInvokable* invokable() const; + +private: + template< typename T, typename F > + static inline QskMetaInvokable* findInvokable( F function ) + { + QskMetaInvokable* invokable; + +#if QSK_SHARED_META_INVOKABLE + invokable = QskMetaInvokable::find( typeid( T ) ); + if ( invokable ) + { + invokable->ref(); + } + else + { + invokable = new T( function ); + QskMetaInvokable::insert( typeid( T ), invokable ); + } +#else + invokable = new T( function ); +#endif + + return invokable; + } QskMetaInvokable* m_invokable; const int* m_parameterTypes; }; +inline QskMetaInvokable* QskMetaFunction::invokable() const +{ + return m_invokable; +} + +inline const int* QskMetaFunction::parameterTypes() const +{ + return m_parameterTypes; +} template< typename T, QskMetaFunctionTraits::IsMemberFunction< T >* > inline QskMetaFunction::QskMetaFunction( T function ) @@ -95,8 +133,9 @@ inline QskMetaFunction::QskMetaFunction( T function ) const int Argc = Traits::ArgumentCount; using Args = typename List_Left< typename Traits::Arguments, Argc >::Value; - - init( new QskMetaMemberInvokable< T, Args, void >( function ), + using Invokable = QskMetaMemberInvokable< T, Args, void >; + + init( findInvokable( function ), ConnectionTypes< typename Traits::Arguments >::types() ); } @@ -109,8 +148,9 @@ inline QskMetaFunction::QskMetaFunction( T function ) const int Argc = Traits::ArgumentCount; using Args = typename List_Left< typename Traits::Arguments, Argc >::Value; + using Invokable = QskMetaFunctionInvokable< T, Args, void >; - init( new QskMetaFunctionInvokable< T, Args, void >( function ), + init( findInvokable( function ), ConnectionTypes< typename Traits::Arguments >::types() ); } @@ -123,8 +163,9 @@ inline QskMetaFunction::QskMetaFunction( T functor ) const int Argc = Traits::ArgumentCount; using Args = typename List_Left< typename Traits::Arguments, Argc >::Value; + using Invokable = QskMetaFunctorInvokable< T, Argc, Args, void >; - init( new QskMetaFunctorInvokable< T, Argc, Args, void > ( functor ), + init( findInvokable( functor ), ConnectionTypes< typename Traits::Arguments >::types() ); } diff --git a/src/common/QskMetaInvokable.cpp b/src/common/QskMetaInvokable.cpp index 90a3eed5..2f15ff9a 100644 --- a/src/common/QskMetaInvokable.cpp +++ b/src/common/QskMetaInvokable.cpp @@ -35,3 +35,28 @@ int QskMetaInvokable::refCount() const auto that = const_cast< QskMetaInvokable* >( this ); return reinterpret_cast< SlotObject* >( that )->ref.load(); } + +#if QSK_SHARED_META_INVOKABLE + +#include +#include + +static std::unordered_map< std::type_index, QskMetaInvokable* > qskInvokableTab; + +QskMetaInvokable* QskMetaInvokable::find( const std::type_info& info ) +{ + const auto it = qskInvokableTab.find( info ); + return ( it != qskInvokableTab.end() ) ? it->second : nullptr; +} + +void QskMetaInvokable::insert( const std::type_info& info, QskMetaInvokable* invokable ) +{ + qskInvokableTab.emplace( info, invokable ); +} + +void QskMetaInvokable::remove( const std::type_info& info ) +{ + qskInvokableTab.erase( info ); +} + +#endif // QSK_SHARED_META_INVOKABLE diff --git a/src/common/QskMetaInvokable.h b/src/common/QskMetaInvokable.h index 8465268c..ad0a32b3 100644 --- a/src/common/QskMetaInvokable.h +++ b/src/common/QskMetaInvokable.h @@ -11,7 +11,22 @@ // helper classes for QskMetaFunction -class QskMetaInvokable : public QtPrivate::QSlotObjectBase +#ifndef QT_NO_RTTI // we rely on hashing type_info + + /* + When being enabled the same instance of QskMetaInvokable is used + for all QskMetaFunctions having the same function/method/functor - + f.e. &QQuickItem::update. + + Not sure, why QObject::connect does not do the same and always + creates unique QSlotObjectBase objects for each connection. + */ + #ifndef QSK_SHARED_META_INVOKABLE + #define QSK_SHARED_META_INVOKABLE 1 + #endif +#endif + +class QSK_EXPORT QskMetaInvokable : public QtPrivate::QSlotObjectBase { public: typedef void (* InvokeFunction)( @@ -23,39 +38,58 @@ public: int refCount() const; protected: + friend class QskMetaFunction; + explicit QskMetaInvokable( InvokeFunction f ): QSlotObjectBase( f ) { } + +#if QSK_SHARED_META_INVOKABLE + /* + To avoid having more QskMetaInvokables for the same + function we have a hash table, where they are registered + */ + static QskMetaInvokable* find( const std::type_info& info ); + static void insert( const std::type_info&, QskMetaInvokable* ); + static void remove( const std::type_info& ); +#else + +#endif }; -template< typename Func, typename Args, typename R > +template< typename Function, typename Args, typename R > class QskMetaFunctionInvokable : public QskMetaInvokable { public: - typedef QtPrivate::FunctionPointer< Func > FuncType; + typedef QtPrivate::FunctionPointer< Function > FuncType; - explicit QskMetaFunctionInvokable( Func f ): + explicit QskMetaFunctionInvokable( Function function ): QskMetaInvokable( &invoke ), - function(f) + m_function( function ) { } static void invoke(int which, QtPrivate::QSlotObjectBase* invokable, QObject* object, void** args, bool* ) { - auto f = static_cast< QskMetaFunctionInvokable* >( invokable ); + auto invokableFunction = static_cast< QskMetaFunctionInvokable* >( invokable ); switch ( which ) { case Destroy: { - delete f; +#if QSK_SHARE_INVOKABLES + remove( typeid( Function ) ); +#endif + delete invokableFunction; + break; } case Call: { - FuncType::template call< Args, R >( f->function, object, args ); + FuncType::template call< Args, R >( + invokableFunction->m_function, object, args ); break; } case TypeInfo: @@ -68,43 +102,49 @@ public: } } - Func function; + Function m_function; }; -template< typename Func, typename Args, typename R > +template< typename Function, typename Args, typename R > class QskMetaMemberInvokable : public QskMetaInvokable { public: - explicit QskMetaMemberInvokable( Func f ): + explicit QskMetaMemberInvokable( Function function ): QskMetaInvokable( &invoke ), - function(f) + m_function( function ) { } static void invoke( int which, QtPrivate::QSlotObjectBase* invokable, QObject* object, void** args, bool* ret ) { - typedef QtPrivate::FunctionPointer< Func > FuncType; + typedef QtPrivate::FunctionPointer< Function > FuncType; - auto f = static_cast< QskMetaMemberInvokable* >( invokable ); + auto invokableMember = static_cast< QskMetaMemberInvokable* >( invokable ); switch (which) { case Destroy: { - delete f; +#if QSK_SHARE_INVOKABLES + remove( typeid( Function ) ); +#endif + delete invokableMember; + break; } case Call: { FuncType::template call< Args, R >( - f->function, static_cast< typename FuncType::Object* >( object ), args ); + invokableMember->m_function, + static_cast< typename FuncType::Object* >( object ), args ); break; } case Compare: { - *ret = *reinterpret_cast< Func* >( args ) == f->function; + const auto function = *reinterpret_cast< Function* >( args ); + *ret = function == invokableMember->m_function; break; } case TypeInfo: @@ -118,36 +158,41 @@ public: } private: - Func function; + Function m_function; }; -template< typename Func, int N, typename Args, typename R > +template< typename Function, int N, typename Args, typename R > class QskMetaFunctorInvokable : public QskMetaInvokable { public: - typedef QtPrivate::Functor< Func, N > FuncType; + typedef QtPrivate::Functor< Function, N > FuncType; - explicit QskMetaFunctorInvokable( Func f ): + explicit QskMetaFunctorInvokable( Function function ): QskMetaInvokable( &invoke ), - function( std::move( f ) ) + m_function( std::move( function ) ) { } static void invoke( int which, QSlotObjectBase* invokable, QObject* object, void** args, bool* ) { - auto f = static_cast< QskMetaFunctorInvokable* >( invokable ); + auto invokableFunctor = static_cast< QskMetaFunctorInvokable* >( invokable ); switch (which) { case Destroy: { - delete f; +#if QSK_SHARE_INVOKABLES + remove( typeid( Function ) ); +#endif + delete invokableFunctor; + break; } case Call: { - FuncType::template call< Args, R >( f->function, object, args ); + FuncType::template call< Args, R >( + invokableFunctor->m_function, object, args ); break; } case TypeInfo: @@ -161,7 +206,7 @@ public: } private: - Func function; + Function m_function; }; #endif