From 6c9d78c5329cb8d4906ebca451385e783ded83c7 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 4 Mar 2018 13:31:49 +0100 Subject: [PATCH] QskMetaInvokable introduced --- playground/invoker/Invoker.cpp | 2 +- src/common/QskMetaCallback.cpp | 218 ++------------------------- src/common/QskMetaCallback.h | 62 ++------ src/common/QskMetaFunction.cpp | 28 ++++ src/common/QskMetaFunction.h | 23 ++- src/common/QskMetaFunction.hpp | 15 +- src/common/QskMetaInvokable.cpp | 251 ++++++++++++++++++++++++++++++++ src/common/QskMetaInvokable.h | 106 ++++++++++++++ src/src.pro | 2 + 9 files changed, 445 insertions(+), 262 deletions(-) create mode 100644 src/common/QskMetaInvokable.cpp create mode 100644 src/common/QskMetaInvokable.h diff --git a/playground/invoker/Invoker.cpp b/playground/invoker/Invoker.cpp index 913b438c..504013f2 100644 --- a/playground/invoker/Invoker.cpp +++ b/playground/invoker/Invoker.cpp @@ -32,7 +32,7 @@ void Invoker::invoke( qreal realValue, int intValue, { void* args[4] = { nullptr }; - const auto types = callback.parameterTypes(); + const auto types = callback.invokable().parameterTypes(); int i = 1; for ( auto type : types ) diff --git a/src/common/QskMetaCallback.cpp b/src/common/QskMetaCallback.cpp index 87e455de..f287d63a 100644 --- a/src/common/QskMetaCallback.cpp +++ b/src/common/QskMetaCallback.cpp @@ -4,41 +4,12 @@ *****************************************************************************/ #include "QskMetaCallback.h" -#include "QskMetaFunction.h" #include "QskMetaMethod.h" -#include - -namespace -{ - class Function: public QskMetaFunction - { - public: - inline Function( void* functionCall ): - QskMetaFunction( static_cast< FunctionCall* >( functionCall ) ) - { - } - - static inline void ref( void* functionCall ) - { - if ( functionCall ) - static_cast< FunctionCall* >( functionCall )->ref(); - } - - static inline void deref( void* functionCall ) - { - if ( functionCall ) - static_cast< FunctionCall* >( functionCall )->destroyIfLastRef(); - } - }; -} - - QskMetaCallback::QskMetaCallback( const QObject* object, const QMetaMethod& method, Qt::ConnectionType connectionType ): m_object( const_cast< QObject* >( object ) ), - m_methodData { method.enclosingMetaObject(), method.methodIndex() }, - m_type( MetaMethod ), + m_invokable( method ), m_hasObject( object != nullptr ), m_connectionType( static_cast< ushort >( connectionType & 0x3 ) ) { @@ -53,88 +24,14 @@ QskMetaCallback::QskMetaCallback( const QObject* object, QskMetaCallback::QskMetaCallback( const QObject* object, const QskMetaFunction& function, Qt::ConnectionType connectionType ): m_object( const_cast< QObject* >( object ) ), - m_functionData { function.functionCall(), function.parameterTypes() }, - m_type( MetaFunction ), + m_invokable( function ), m_hasObject( object != nullptr ), m_connectionType( static_cast< ushort >( connectionType & ~Qt::UniqueConnection ) ) { - Function::ref( m_functionData.functionCall ); -} - -QskMetaCallback::QskMetaCallback( const QskMetaCallback& other ): - m_object( other.m_object ), - m_type( other.m_type ), - m_hasObject( other.m_hasObject ), - m_connectionType( other.m_connectionType ) -{ - switch( m_type ) - { - case MetaMethod: - { - m_methodData.metaObject = other.m_methodData.metaObject; - m_methodData.methodIndex = other.m_methodData.methodIndex; - - break; - } - case MetaFunction: - { - m_functionData.functionCall = other.m_functionData.functionCall; - Function::ref( m_functionData.functionCall ); - - m_functionData.parameterTypes = other.m_functionData.parameterTypes; - break; - } - - default: - break; - } } QskMetaCallback::~QskMetaCallback() { - if ( m_type == MetaFunction ) - Function::deref( m_functionData.functionCall ); -} - -QskMetaCallback& QskMetaCallback::operator=( const QskMetaCallback& other ) -{ - m_object = other.m_object; - m_hasObject = other.m_hasObject; - - m_connectionType = other.m_connectionType; - - switch( other.m_type ) - { - case MetaMethod: - { - if ( m_type == MetaFunction ) - Function::deref( m_functionData.functionCall ); - - m_methodData.metaObject = other.m_methodData.metaObject; - m_methodData.methodIndex = other.m_methodData.methodIndex; - - break; - } - case MetaFunction: - { - if ( m_type == MetaFunction ) - Function::deref( m_functionData.functionCall ); - - m_functionData.functionCall = other.m_functionData.functionCall; - Function::ref( m_functionData.functionCall ); - - m_functionData.parameterTypes = other.m_functionData.parameterTypes; - break; - } - - default: - if ( m_type == MetaFunction ) - Function::deref( m_functionData.functionCall ); - } - - m_type = other.m_type; - - return *this; } bool QskMetaCallback::isValid() const @@ -142,28 +39,13 @@ 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 !m_invokable.isNull(); +} - return false; - } - - case MetaFunction: - { - return m_functionData.functionCall != nullptr; - } - - default: - return false; - } +void QskMetaCallback::setInvokable( const QskMetaInvokable& invokable ) +{ + // type checking is m_object matches ??? + m_invokable = invokable; } void QskMetaCallback::setConnectionType( Qt::ConnectionType connectionType ) @@ -171,88 +53,8 @@ void QskMetaCallback::setConnectionType( Qt::ConnectionType connectionType ) m_connectionType = connectionType; } -void QskMetaCallback::reset() -{ - m_object = nullptr; - m_hasObject = false; - - if ( m_type == MetaFunction ) - Function::deref( m_functionData.functionCall ); - - m_functionData = { nullptr, nullptr }; // for the debugger - m_type = Invalid; -} - -QVector< int > QskMetaCallback::parameterTypes() const -{ - QVector< int > paramTypes; - - switch( m_type ) - { - case MetaMethod: - { - 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 += method.parameterType( i ); - } - - break; - } - case MetaFunction: - { - auto types = m_functionData.parameterTypes; - if ( types ) - { - while ( *types ) - paramTypes += *types++; - } - break; - } - default: - break; - } - - return paramTypes; -} - - void QskMetaCallback::invoke( void* args[] ) { - if ( !isValid() ) - return; - - auto object = const_cast< QObject* >( m_object.data() ); - - switch( m_type ) - { - case MetaMethod: - { - QskMetaMethod::invoke( object, m_methodData.metaObject, - m_methodData.methodIndex, args, connectionType() ); - - break; - } - case MetaFunction: - { - if ( m_functionData.functionCall ) - { - Function function( m_functionData.functionCall ); - function.invoke( object, args, connectionType() ); - } - - break; - } - - default: - break; - } + if ( isValid() ) + m_invokable.invoke( m_object, args, connectionType() ); } diff --git a/src/common/QskMetaCallback.h b/src/common/QskMetaCallback.h index ee5ffa6b..820617b8 100644 --- a/src/common/QskMetaCallback.h +++ b/src/common/QskMetaCallback.h @@ -7,29 +7,14 @@ #define QSK_META_CALLBACK_H 1 #include "QskGlobal.h" +#include "QskMetaInvokable.h" #include -#include #include -class QskMetaFunction; -class QMetaObject; -class QMetaMethod; - class QSK_EXPORT QskMetaCallback { public: - enum Type - { - Invalid = 0, - - // A QMetaMethod - MetaMethod, - - // A function pointer, for what Qt calls "functor based" callbacks - MetaFunction - }; - QskMetaCallback(); QskMetaCallback( const QObject*, const QskMetaFunction&, @@ -41,61 +26,42 @@ public: QskMetaCallback( const QObject*, const char* methodName, Qt::ConnectionType = Qt::AutoConnection ); - QskMetaCallback( const QskMetaCallback& ); - ~QskMetaCallback(); - QskMetaCallback& operator=( const QskMetaCallback& ); - - Type type() const; bool isValid() const; + bool isStale() const; const QObject* object() const { return m_object; } + void setInvokable( const QskMetaInvokable& ); + const QskMetaInvokable& invokable() const; + void setConnectionType( Qt::ConnectionType ); Qt::ConnectionType connectionType() const; - QVector< int > parameterTypes() const; - void invoke( void* args[] ); private: - void reset(); + QPointer< QObject > m_object; + QskMetaInvokable m_invokable; - QPointer< const QObject > m_object; - - struct FunctionData - { - void* functionCall; - const int* parameterTypes; - }; - - struct MethodData - { - const QMetaObject* metaObject; - int methodIndex; - }; - - union - { - 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_hasObject( false ) { } -inline QskMetaCallback::Type QskMetaCallback::type() const +inline bool QskMetaCallback::isStale() const { - return static_cast< Type >( m_type ); + return m_hasObject && m_object.isNull(); +} + +inline const QskMetaInvokable& QskMetaCallback::invokable() const +{ + return m_invokable; } inline Qt::ConnectionType QskMetaCallback::connectionType() const diff --git a/src/common/QskMetaFunction.cpp b/src/common/QskMetaFunction.cpp index aae39659..8676cbb1 100644 --- a/src/common/QskMetaFunction.cpp +++ b/src/common/QskMetaFunction.cpp @@ -123,6 +123,34 @@ QskMetaFunction& QskMetaFunction::operator=( const QskMetaFunction& other ) return *this; } +bool QskMetaFunction::operator==( const QskMetaFunction& other ) const +{ + if ( m_functionCall == other.m_functionCall ) + return true; + + /* + There is no way to compmare functors/members without + std::type_info, what we don't want to use as it is + another template creating symbols. + + So this implementation can't do much more than finding + out if one instance is a copy from another. + */ + + if ( m_functionCall && other.m_functionCall ) + { + if ( m_functionCall->typeInfo() == Function && + other.m_functionCall->typeInfo() == Function ) + { + // only static functions can be compared + return m_functionCall->compare( + reinterpret_cast< void** >( other.m_functionCall ) ); + } + } + + return false; +} + size_t QskMetaFunction::parameterCount() const { if ( auto types = parameterTypes() ) diff --git a/src/common/QskMetaFunction.h b/src/common/QskMetaFunction.h index 735530cc..61ed9297 100644 --- a/src/common/QskMetaFunction.h +++ b/src/common/QskMetaFunction.h @@ -70,6 +70,11 @@ public: QskMetaFunction& operator=( const QskMetaFunction& ); QskMetaFunction& operator=( QskMetaFunction&& ); + bool operator==( const QskMetaFunction& ) const; + bool operator!=( const QskMetaFunction& ) const; + + explicit operator bool() const; + const int* parameterTypes() const; // including the return type ! @@ -79,6 +84,7 @@ public: Qt::ConnectionType = Qt::AutoConnection ); Type functionType() const; + bool isNull() const; class FunctionCall; FunctionCall* functionCall() const; @@ -90,13 +96,28 @@ private: FunctionCall* m_functionCall; }; -#include "QskMetaFunction.hpp" +inline bool QskMetaFunction::operator!=( const QskMetaFunction& other ) const +{ + return !( *this == other ); +} + +inline QskMetaFunction::operator bool() const +{ + return !isNull(); +} + +inline bool QskMetaFunction::isNull() const +{ + return m_functionCall == nullptr; +} inline QskMetaFunction::FunctionCall* QskMetaFunction::functionCall() const { return m_functionCall; } +#include "QskMetaFunction.hpp" + inline const int* QskMetaFunction::parameterTypes() const { return m_functionCall ? m_functionCall->parameterTypes() : nullptr; diff --git a/src/common/QskMetaFunction.hpp b/src/common/QskMetaFunction.hpp index bde7b046..9b206ceb 100644 --- a/src/common/QskMetaFunction.hpp +++ b/src/common/QskMetaFunction.hpp @@ -63,7 +63,7 @@ namespace QskMetaFunctionCall } static void invoke(int which, QSlotObjectBase* functionCall, - QObject* object, void** args, bool* ) + QObject* object, void** args, bool* ret ) { switch ( which ) { @@ -80,6 +80,13 @@ namespace QskMetaFunctionCall static_cast< MetaCall* >( functionCall )->m_function, object, args ); break; } + case Compare: + { + *ret = reinterpret_cast< MetaCall* >( args )->m_function + == static_cast< MetaCall* >( functionCall )->m_function; + + break; + } case TypeInfo: { *reinterpret_cast< int* >( args ) = QskMetaFunction::Function; @@ -148,14 +155,14 @@ namespace QskMetaFunctionCall { } - static void invoke( int which, QSlotObjectBase* slotObject, + static void invoke( int which, QSlotObjectBase* functionCall, QObject* object, void** args, bool* ) { switch (which) { case Destroy: { - delete static_cast< MetaCall* >( slotObject ); + delete static_cast< MetaCall* >( functionCall ); break; } case Call: @@ -163,7 +170,7 @@ namespace QskMetaFunctionCall typedef Functor< Function, N > FuncType; FuncType::template call< Args, R >( - static_cast< MetaCall* >( slotObject )->m_function, object, args ); + static_cast< MetaCall* >( functionCall )->m_function, object, args ); break; } diff --git a/src/common/QskMetaInvokable.cpp b/src/common/QskMetaInvokable.cpp new file mode 100644 index 00000000..17baf20f --- /dev/null +++ b/src/common/QskMetaInvokable.cpp @@ -0,0 +1,251 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskMetaInvokable.h" +#include "QskMetaFunction.h" +#include "QskMetaMethod.h" + +#include +#include +#include + +namespace +{ + class Function : public QskMetaFunction + { + public: + inline Function( void* functionCall ): + QskMetaFunction( static_cast< FunctionCall* >( functionCall ) ) + { + } + + static inline void ref( void* functionCall ) + { + if ( functionCall ) + static_cast< FunctionCall* >( functionCall )->ref(); + } + + static inline void deref( void* functionCall ) + { + if ( functionCall ) + static_cast< FunctionCall* >( functionCall )->destroyIfLastRef(); + } + }; +} + + +QskMetaInvokable::QskMetaInvokable( const QMetaMethod& method ): + m_methodData { method.enclosingMetaObject(), method.methodIndex() }, + m_type( MetaMethod ) +{ +} + +QskMetaInvokable::QskMetaInvokable( const QObject* object, const char* methodName ): + QskMetaInvokable( QskMetaMethod::method( object, methodName ) ) +{ +} + +QskMetaInvokable::QskMetaInvokable( const QMetaObject* metaObject, const char* methodName ): + QskMetaInvokable( QskMetaMethod::method( metaObject, methodName ) ) +{ +} + +QskMetaInvokable::QskMetaInvokable( const QskMetaFunction& function ): + m_functionData { function.functionCall() }, + m_type( MetaFunction ) +{ + Function::ref( m_functionData.functionCall ); +} + +QskMetaInvokable::QskMetaInvokable( const QskMetaInvokable& other ): + m_type( other.m_type ) +{ + switch( m_type ) + { + case MetaMethod: + { + m_methodData.metaObject = other.m_methodData.metaObject; + m_methodData.methodIndex = other.m_methodData.methodIndex; + + break; + } + case MetaFunction: + { + m_functionData.functionCall = other.m_functionData.functionCall; + Function::ref( m_functionData.functionCall ); + + break; + } + + default: + break; + } +} + +QskMetaInvokable::~QskMetaInvokable() +{ + if ( m_type == MetaFunction ) + Function::deref( m_functionData.functionCall ); +} + +QskMetaInvokable& QskMetaInvokable::operator=( const QskMetaInvokable& other ) +{ + switch( other.m_type ) + { + case MetaMethod: + { + if ( m_type == MetaFunction ) + Function::deref( m_functionData.functionCall ); + + m_methodData.metaObject = other.m_methodData.metaObject; + m_methodData.methodIndex = other.m_methodData.methodIndex; + + break; + } + case MetaFunction: + { + if ( m_type == MetaFunction ) + Function::deref( m_functionData.functionCall ); + + m_functionData.functionCall = other.m_functionData.functionCall; + Function::ref( m_functionData.functionCall ); + + break; + } + + default: + if ( m_type == MetaFunction ) + Function::deref( m_functionData.functionCall ); + } + + m_type = other.m_type; + + return *this; +} + +bool QskMetaInvokable::operator==( const QskMetaInvokable& other ) const +{ + if ( m_type == other.m_type ) + { + if ( m_type == MetaMethod ) + { + return ( m_methodData.metaObject == other.m_methodData.metaObject ) + && ( m_methodData.methodIndex == other.m_methodData.methodIndex ); + } + if ( m_type == MetaFunction ) + { + return m_functionData.functionCall == other.m_functionData.functionCall; + } + } + + return true; +} + +bool QskMetaInvokable::isNull() const +{ + switch( m_type ) + { + case MetaMethod: + { + const auto& d = m_methodData; + if ( d.metaObject && ( d.methodIndex >= 0 ) + && ( d.methodIndex < d.metaObject->methodCount() ) ) + { + return false; + } + + return true; + } + + case MetaFunction: + { + return m_functionData.functionCall == nullptr; + } + + default: + return true; + } +} + +void QskMetaInvokable::reset() +{ + if ( m_type == MetaFunction ) + Function::deref( m_functionData.functionCall ); + + m_type = Invalid; +} + +QVector< int > QskMetaInvokable::parameterTypes() const +{ + QVector< int > paramTypes; + + switch( m_type ) + { + case MetaMethod: + { + 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 += method.parameterType( i ); + } + + break; + } + case MetaFunction: + { + auto types = Function( m_functionData.functionCall ).parameterTypes(); + if ( types ) + { + while ( *types ) + paramTypes += *types++; + } + break; + } + default: + break; + } + + return paramTypes; +} + + +void QskMetaInvokable::invoke( QObject* object, void* args[], + Qt::ConnectionType connectionType ) +{ + if ( isNull() ) + return; + + switch( m_type ) + { + case MetaMethod: + { + QskMetaMethod::invoke( object, m_methodData.metaObject, + m_methodData.methodIndex, args, connectionType ); + + break; + } + case MetaFunction: + { + if ( m_functionData.functionCall ) + { + Function function( m_functionData.functionCall ); + function.invoke( object, args, connectionType ); + } + + break; + } + + default: + break; + } +} diff --git a/src/common/QskMetaInvokable.h b/src/common/QskMetaInvokable.h new file mode 100644 index 00000000..2208e4e8 --- /dev/null +++ b/src/common/QskMetaInvokable.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_META_INVOKABLE_H +#define QSK_META_INVOKABLE_H 1 + +#include "QskGlobal.h" + +#include +#include + +template< typename T > class QVector; + +class QskMetaFunction; +class QMetaObject; +class QMetaMethod; +class QObject; + +class QSK_EXPORT QskMetaInvokable +{ +public: + enum Type + { + Invalid = 0, + + // A QMetaMethod + MetaMethod, + + // A function pointer, for what Qt calls "functor based" callbacks + MetaFunction + }; + + QskMetaInvokable(); + + QskMetaInvokable( const QskMetaFunction& ); + QskMetaInvokable( const QMetaMethod& ); + QskMetaInvokable( const QObject*, const char* methodName ); + QskMetaInvokable( const QMetaObject*, const char* methodName ); + + QskMetaInvokable( const QskMetaInvokable& ); + + ~QskMetaInvokable(); + + QskMetaInvokable& operator=( const QskMetaInvokable& ); + + bool operator==( const QskMetaInvokable& ) const; + bool operator!=( const QskMetaInvokable& ) const; + + explicit operator bool() const; + + Type type() const; + bool isNull() const; + + QVector< int > parameterTypes() const; + + void invoke( QObject*, void* args[], + Qt::ConnectionType = Qt::AutoConnection ); + + void reset(); + +private: + struct FunctionData + { + void* functionCall; + }; + + struct MethodData + { + const QMetaObject* metaObject; + int methodIndex; + }; + + union + { + FunctionData m_functionData; + MethodData m_methodData; + }; + + int m_type : 3; +}; + +inline QskMetaInvokable::QskMetaInvokable(): + m_type( Invalid ) +{ +} + +inline bool QskMetaInvokable::operator!=( const QskMetaInvokable& other ) const +{ + return !( *this == other ); +} + +inline QskMetaInvokable::operator bool() const +{ + return !isNull(); +} + +inline QskMetaInvokable::Type QskMetaInvokable::type() const +{ + return static_cast< Type >( m_type ); +} + +Q_DECLARE_METATYPE( QskMetaInvokable ) + +#endif diff --git a/src/src.pro b/src/src.pro index dfa1e213..64ce6471 100644 --- a/src/src.pro +++ b/src/src.pro @@ -43,6 +43,7 @@ HEADERS += \ common/QskMetaCallback.h \ common/QskMetaFunction.h \ common/QskMetaFunction.hpp \ + common/QskMetaInvokable.h \ common/QskMetaMethod.h \ common/QskModule.h \ common/QskNamespace.h \ @@ -62,6 +63,7 @@ SOURCES += \ common/QskMargins.cpp \ common/QskMetaCallback.cpp \ common/QskMetaFunction.cpp \ + common/QskMetaInvokable.cpp \ common/QskMetaMethod.cpp \ common/QskModule.cpp \ common/QskObjectCounter.cpp \