From 0c1cfa34537fb3eb298905f4ec82e13a1629ee3a Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 26 Feb 2018 09:09:21 +0100 Subject: [PATCH] QskMetaFunction added - similar to QMetaMethod but for what Qt calls "functor based" callbacks --- src/common/QskMetaCall.cpp | 136 +++++++++++++++++++ src/common/QskMetaCall.h | 197 ++++++++++++++++++++++++++++ src/common/QskMetaCallback.cpp | 233 +++++++++++++++++++++++++++++++++ src/common/QskMetaCallback.h | 86 ++++++++++++ src/common/QskMetaFunction.cpp | 89 +++++++++++++ src/common/QskMetaFunction.h | 113 ++++++++++++++++ 6 files changed, 854 insertions(+) create mode 100644 src/common/QskMetaCall.cpp create mode 100644 src/common/QskMetaCall.h create mode 100644 src/common/QskMetaCallback.cpp create mode 100644 src/common/QskMetaCallback.h create mode 100644 src/common/QskMetaFunction.cpp create mode 100644 src/common/QskMetaFunction.h diff --git a/src/common/QskMetaCall.cpp b/src/common/QskMetaCall.cpp new file mode 100644 index 00000000..fd83aa20 --- /dev/null +++ b/src/common/QskMetaCall.cpp @@ -0,0 +1,136 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskMetaCall.h" +#include +#include +#include +#include +#include + +#include + +namespace QskMetaCall +{ + inline Invokable::InvokeFunction invokeCall( const Invokable* invokable ) + { + struct Data { QAtomicInt dummy; Invokable::InvokeFunction invoke; }; + return ( ( Data* )( invokable ) )->invoke; + } + + int Invokable::typeInfo() const + { + int value; + + auto f = invokeCall( this ); + + f( TypeInfo, const_cast< Invokable* >( this ), + nullptr, reinterpret_cast< void** >( &value ), nullptr ); + + return value; + } +} + +void QskMetaCall::invoke( QObject* object, + const QMetaMethod& method, void* args[], + Qt::ConnectionType connectionType ) +{ + auto metaObject = method.enclosingMetaObject(); + + const int methodOffset = metaObject->methodOffset(); + const int methodIndex = method.methodIndex() - methodOffset; + + if ( connectionType == Qt::AutoConnection ) + { + connectionType = ( object->thread() == QThread::currentThread() ) + ? Qt::DirectConnection : Qt::QueuedConnection; + } + + if ( connectionType == Qt::DirectConnection ) + { + if ( metaObject->d.static_metacall ) + { + metaObject->d.static_metacall(object, + QMetaObject::InvokeMetaMethod, methodIndex, args ); + } + else + { + QMetaObject::metacall( object, + QMetaObject::InvokeMetaMethod, methodIndex, args ); + } + } + else + { + const int paramCount = method.parameterCount(); + + auto types = static_cast< int* >( malloc( paramCount * sizeof( int ) ) ); + + types[0] = QMetaType::UnknownType; // return type + + for ( int i = 1; i < paramCount; i++ ) + { + types[i] = method.parameterType( i ); + Q_ASSERT( args[i] != nullptr ); + } + + Q_ASSERT( args[paramCount] == nullptr ); + + if ( connectionType == Qt::QueuedConnection ) + { + QMetaCallEvent* event = new QMetaCallEvent( + methodOffset, methodIndex, metaObject->d.static_metacall, + nullptr, -1, paramCount + 1, types, args ); + + QCoreApplication::postEvent(object, event ); + } + else + { + QSemaphore semaphore; + + // what about argc + types ??? + auto event = new QMetaCallEvent( + methodOffset, methodIndex, metaObject->d.static_metacall, + nullptr, -1, 0, 0, args, &semaphore ); + + QCoreApplication::postEvent( object, event ); + + semaphore.acquire(); + } + } +} + +void QskMetaCall::invoke( QObject* object, + const Invokable& invokable, void* args[], + Qt::ConnectionType connectionType ) +{ + //connectionType &= ~Qt::UniqueConnection; + + auto invokablePtr = const_cast< Invokable* >( &invokable ); + + if ( connectionType == Qt::AutoConnection ) + { + connectionType = ( object->thread() == QThread::currentThread() ) + ? Qt::DirectConnection : Qt::QueuedConnection; + } + + if ( connectionType == Qt::DirectConnection ) + { + invokablePtr->call( object, args ); + } + else + { + if ( connectionType == Qt::QueuedConnection ) + { + auto event = new QMetaCallEvent( + invokablePtr, nullptr, 0, 0, nullptr, args, nullptr ); + + QCoreApplication::postEvent( object, event ); + } + else // Qt::BlockingQueuedConnection ??? + { + // .... + } + } +} diff --git a/src/common/QskMetaCall.h b/src/common/QskMetaCall.h new file mode 100644 index 00000000..a3a93bd6 --- /dev/null +++ b/src/common/QskMetaCall.h @@ -0,0 +1,197 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_META_CALL_H +#define QSK_META_CALL_H 1 + +#include "QskGlobal.h" +#include + +namespace QskMetaCall +{ + class Invokable; + + QSK_EXPORT void invoke( QObject* object, + const QMetaMethod&, void* args[], + Qt::ConnectionType = Qt::AutoConnection ); + + QSK_EXPORT void invoke( QObject*, + const Invokable&, void* args[], + Qt::ConnectionType = Qt::AutoConnection ); +} + +namespace QskMetaCall +{ + using namespace QtPrivate; + + class QSK_EXPORT Invokable : public QSlotObjectBase + { + public: + typedef void (* InvokeFunction)( int which, QSlotObjectBase*, + QObject*, void**, bool* ); + + enum { TypeInfo = NumOperations + 1 }; + + int typeInfo() const; + + protected: + explicit Invokable( InvokeFunction f ): + QSlotObjectBase( f ) + { + } + }; + + template< typename Func, typename Args, typename R > + class FunctionInvokable : public Invokable + { + public: + typedef FunctionPointer< Func > FuncType; + + explicit FunctionInvokable( Func f ): + Invokable( &invoke ), + function(f) + { + } + + static void invoke(int which, QSlotObjectBase* invokable, + QObject* object, void** args, bool* ) + { + auto f = static_cast< FunctionInvokable* >( invokable ); + + switch ( which ) + { + case Destroy: + { + delete f; + break; + } + case Call: + { + FuncType::template call< Args, R >( f->function, object, args ); + break; + } + case TypeInfo: + { + int* typeInfo = reinterpret_cast< int* >( args ); + //*typeInfo = QskMetaFunction::Function; + *typeInfo = 1; + + break; + } + } + } + + Func function; + }; + + template< typename Func, typename Args, typename R > + class MemberFunctionInvokable : public Invokable + { + public: + explicit MemberFunctionInvokable( Func f ): + Invokable( &invoke ), + function(f) + { + } + + static void invoke( int which, QSlotObjectBase* invokable, + QObject* object, void** args, bool* ret ) + { + typedef FunctionPointer< Func > FuncType; + + auto f = static_cast< MemberFunctionInvokable* >( invokable ); + + switch (which) + { + case Destroy: + { + delete f; + break; + } + case Call: + { + FuncType::template call< Args, R >( + f->function, static_cast< typename FuncType::Object* >( object ), args ); + + break; + } + case Compare: + { + *ret = *reinterpret_cast< Func* >( args ) == f->function; + break; + } + case TypeInfo: + { + int* typeInfo = reinterpret_cast< int* >( args ); + *typeInfo = 0; + //*typeInfo = QskMetaFunction::Member; + + break; + } + } + } + + private: + Func function; + }; + + template< typename Func, int N, typename Args, typename R > + class FunctorInvokable : public Invokable + { + public: + typedef Functor< Func, N > FuncType; + + explicit FunctorInvokable( Func f ): + Invokable( &invoke ), + function( std::move( f ) ) + { + } + + static void invoke( int which, QSlotObjectBase* invokable, + QObject* object, void** args, bool* ) + { + auto f = static_cast< FunctorInvokable* >( invokable ); + + switch (which) + { + case Destroy: + { + delete f; + break; + } + case Call: + { + FuncType::template call< Args, R >( f->function, object, args ); + break; + } + case TypeInfo: + { + int* typeInfo = reinterpret_cast< int* >( args ); + *typeInfo = 2; + //*typeInfo = QskMetaFunction::Functor; + + break; + } + } + } + + private: + Func function; + }; + + template< typename T > + using IsMemberFunction = typename std::enable_if< FunctionPointer< T >::IsPointerToMemberFunction, + std::true_type >::type; + + template< typename T > + using IsFunction = typename std::enable_if< !FunctionPointer< T >::IsPointerToMemberFunction + && FunctionPointer< T >::ArgumentCount >= 0, std::true_type >::type; + + template< typename T > + using IsFunctor = typename std::enable_if< !FunctionPointer< T >::IsPointerToMemberFunction + && FunctionPointer< T >::ArgumentCount == -1, std::true_type >::type; +} + +#endif diff --git a/src/common/QskMetaCallback.cpp b/src/common/QskMetaCallback.cpp new file mode 100644 index 00000000..016f00fd --- /dev/null +++ b/src/common/QskMetaCallback.cpp @@ -0,0 +1,233 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskMetaCallback.h" +#include +#include +#include +#include + +#include + +static void qskInvoke( QObject* object, + const QMetaMethod& method, void* args[], Qt::ConnectionType connectionType ) +{ + auto metaObject = method.enclosingMetaObject(); + + const int methodOffset = metaObject->methodOffset(); + const int methodIndex = method.methodIndex() - methodOffset; + + if ( connectionType == Qt::AutoConnection ) + { + connectionType = ( object->thread() == QThread::currentThread() ) + ? Qt::DirectConnection : Qt::QueuedConnection; + } + + if ( connectionType == Qt::DirectConnection ) + { + if ( metaObject->d.static_metacall ) + { + metaObject->d.static_metacall(object, + QMetaObject::InvokeMetaMethod, methodIndex, args ); + } + else + { + QMetaObject::metacall( object, + QMetaObject::InvokeMetaMethod, methodIndex, args ); + } + } + else + { + const int paramCount = method.parameterCount(); + + auto types = static_cast< int* >( malloc( paramCount * sizeof( int ) ) ); + + types[0] = QMetaType::UnknownType; // return type + + for ( int i = 1; i < paramCount; i++ ) + { + types[i] = method.parameterType( i ); + Q_ASSERT( args[i] != nullptr ); + } + + Q_ASSERT( args[paramCount] == nullptr ); + + if ( connectionType == Qt::QueuedConnection ) + { + QMetaCallEvent* event = new QMetaCallEvent( + methodOffset, methodIndex, metaObject->d.static_metacall, + nullptr, -1, paramCount + 1, types, args ); + + QCoreApplication::postEvent(object, event ); + } + else + { + QSemaphore semaphore; + + // what about argc + types ??? + auto event = new QMetaCallEvent( + methodOffset, methodIndex, metaObject->d.static_metacall, + nullptr, -1, 0, 0, args, &semaphore ); + + QCoreApplication::postEvent( object, event ); + + semaphore.acquire(); + } + } +} + +QskMetaCallback::QskMetaCallback( const QObject* object, + const QMetaMethod& method, Qt::ConnectionType connectionType ): + m_object( const_cast< QObject* >( object ) ), + m_method( method ), + m_type( MetaMethod ), + m_connectionType( static_cast< ushort >( connectionType & ~Qt::UniqueConnection ) ) +{ +} + +QskMetaCallback::QskMetaCallback( const QObject* object, + const QskMetaFunction& function, Qt::ConnectionType connectionType ): + m_object( const_cast< QObject* >( object ) ), + m_function( function ), + m_type( MetaFunction ), + m_connectionType( static_cast< ushort >( connectionType & ~Qt::UniqueConnection ) ) +{ +} + +QskMetaCallback::QskMetaCallback( const QskMetaCallback& other ): + m_object( other.m_object ), + m_function(), + m_type( Invalid ), + m_connectionType( other.m_connectionType ) +{ + if ( other.m_type != m_type ) + { + reset(); + m_type = other.m_type; + } + + switch( m_type ) + { + case MetaMethod: + m_method = other.m_method; + break; + + case MetaFunction: + m_function = other.m_function; + break; + + default: + break; + } +} + +QskMetaCallback::~QskMetaCallback() +{ + reset(); +} + +QskMetaCallback& QskMetaCallback::operator=( const QskMetaCallback& other ) +{ + m_object = other.m_object; + m_connectionType = other.m_connectionType; + + if ( other.m_type != m_type ) + { + reset(); + m_type = other.m_type; + } + + switch( m_type ) + { + case MetaMethod: + m_method = other.m_method; + break; + + case MetaFunction: + m_function = other.m_function; + break; + + default: + break; + } + + return *this; +} + +void QskMetaCallback::reset() +{ + switch( m_type ) + { + case MetaMethod: + m_method.~QMetaMethod(); + break; + + case MetaFunction: + m_function.~QskMetaFunction(); + break; + + default: + break; + } + + m_type = Invalid; +} + +QVector< int > QskMetaCallback::parameterTypes() const +{ + QVector< int > paramTypes; + + switch( m_type ) + { + case MetaMethod: + { + const int paramCount = m_method.parameterCount(); + + paramTypes.reserve( paramCount ); + for ( int i = 0; i < paramCount; i++ ) + paramTypes += m_method.parameterType( i ); + + break; + } + case MetaFunction: + { + auto types = m_function.parameterTypes(); + if ( types ) + { + while ( *types ) + paramTypes += *types++; + } + break; + } + default: + break; + } + + return paramTypes; +} + + +void QskMetaCallback::invoke( void* args[] ) +{ + auto object = const_cast< QObject* >( m_object.data() ); + + switch( m_type ) + { + case MetaMethod: + { + if ( object ) + qskInvoke( object, m_method, args, connectionType() ); + break; + } + case MetaFunction: + { + m_function.invoke( object, args, connectionType() ); + break; + } + + default: + break; + } +} diff --git a/src/common/QskMetaCallback.h b/src/common/QskMetaCallback.h new file mode 100644 index 00000000..97be0806 --- /dev/null +++ b/src/common/QskMetaCallback.h @@ -0,0 +1,86 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_META_CALLBACK_H +#define QSK_META_CALLBACK_H 1 + +#include "QskGlobal.h" +#include "QskMetaFunction.h" + +#include +#include +#include + +class 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&, Qt::ConnectionType ); + QskMetaCallback( const QObject*, const QMetaMethod&, Qt::ConnectionType ); + QskMetaCallback( const QskMetaCallback& ); + + ~QskMetaCallback(); + + QskMetaCallback& operator=( const QskMetaCallback& ); + + Type type() const; + bool isValid() const; + + const QObject* object() const { return m_object; } + Qt::ConnectionType connectionType() const; + + QVector< int > parameterTypes() const; + + void invoke( void* args[] ); + +private: + void reset(); + + QPointer< const QObject > m_object; + + union + { + QskMetaFunction m_function; + QMetaMethod m_method; + }; + + int m_type : 3; + ushort m_connectionType : 3; +}; + +inline QskMetaCallback::QskMetaCallback(): + m_type( Invalid ) +{ +} + +inline bool QskMetaCallback::isValid() const +{ + return m_type > 0; +} + +inline QskMetaCallback::Type QskMetaCallback::type() const +{ + return static_cast< Type >( m_type ); +} + +inline Qt::ConnectionType QskMetaCallback::connectionType() const +{ + return static_cast< Qt::ConnectionType >( m_connectionType ); +} + +Q_DECLARE_METATYPE( QskMetaCallback ) + +#endif diff --git a/src/common/QskMetaFunction.cpp b/src/common/QskMetaFunction.cpp new file mode 100644 index 00000000..ebd0299c --- /dev/null +++ b/src/common/QskMetaFunction.cpp @@ -0,0 +1,89 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskGlobal.h" +#include "QskMetaCall.h" +#include "QskMetaFunction.h" + +QskMetaFunction::QskMetaFunction(): + m_invokable( nullptr ), + m_parameterTypes( nullptr ) +{ +} + +QskMetaFunction::QskMetaFunction( const QskMetaFunction& other ): + m_invokable( other.m_invokable ), + m_parameterTypes( other.m_parameterTypes ) +{ + if ( m_invokable ) + m_invokable->ref(); +} + +QskMetaFunction::QskMetaFunction( QskMetaFunction&& other ): + m_invokable( other.m_invokable ), + m_parameterTypes( other.m_parameterTypes ) +{ + other.m_invokable = nullptr; +} + +QskMetaFunction::~QskMetaFunction() +{ + if ( m_invokable ) + m_invokable->destroyIfLastRef(); +} + +QskMetaFunction& QskMetaFunction::operator=( QskMetaFunction&& other ) +{ + if ( m_invokable != other.m_invokable ) + { + if ( m_invokable ) + m_invokable->destroyIfLastRef(); + + m_invokable = other.m_invokable; + other.m_invokable = nullptr; + } + + m_parameterTypes = other.m_parameterTypes; + + return *this; +} + +QskMetaFunction& QskMetaFunction::operator=( const QskMetaFunction& other ) +{ + if ( m_invokable != other.m_invokable ) + { + if ( m_invokable ) + m_invokable->destroyIfLastRef(); + + m_invokable = other.m_invokable; + + if ( m_invokable ) + m_invokable->ref(); + } + + m_parameterTypes = other.m_parameterTypes; + + return *this; +} + +const int* QskMetaFunction::parameterTypes() const +{ + return m_parameterTypes; +} + +QskMetaFunction::Type QskMetaFunction::functionType() const +{ + if ( m_invokable == nullptr ) + return Invalid; + + return static_cast< QskMetaFunction::Type >( m_invokable->typeInfo() ); +} + +void QskMetaFunction::invoke( + QObject* object, void* args[], Qt::ConnectionType connectionType ) +{ + if ( m_invokable ) + QskMetaCall::invoke( object, *m_invokable, args, connectionType ); +} diff --git a/src/common/QskMetaFunction.h b/src/common/QskMetaFunction.h new file mode 100644 index 00000000..2cb518ad --- /dev/null +++ b/src/common/QskMetaFunction.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_META_FUNCTION_H +#define QSK_META_FUNCTION_H 1 + +#include "QskGlobal.h" +#include "QskMetaCall.h" + +#include + +class QSK_EXPORT QskMetaFunction +{ + Q_GADGET + +public: + enum Type + { + Invalid = -1, + + // a non static method of class + Member, + + // a static function, or static method of a class + Function, + + // a functor or lambda + Functor + }; + + Q_ENUM( Type ) + + QskMetaFunction(); + + QskMetaFunction( const QskMetaFunction& ); + QskMetaFunction( QskMetaFunction&& ); + + template< typename T, QskMetaCall::IsMemberFunction< T >* = nullptr > + QskMetaFunction( T ); + + template< typename T, QskMetaCall::IsFunction< T >* = nullptr > + QskMetaFunction( T ); + + template< typename T, QskMetaCall::IsFunctor< T >* = nullptr > + QskMetaFunction( T ); + + ~QskMetaFunction(); + + QskMetaFunction& operator=( const QskMetaFunction& ); + QskMetaFunction& operator=( QskMetaFunction&& ); + + const int* parameterTypes() const; + + void invoke( QObject*, void* args[], + Qt::ConnectionType = Qt::AutoConnection ); + + Type functionType() const; + +private: + QskMetaCall::Invokable* m_invokable; + const int* m_parameterTypes; +}; + + +template< typename T, QskMetaCall::IsMemberFunction< T >* = nullptr > +inline QskMetaFunction::QskMetaFunction( T function ) +{ + using namespace QtPrivate; + using namespace QskMetaCall; + + using Traits = FunctionPointer< T >; + + const int Argc = Traits::ArgumentCount; + using Args = typename List_Left< typename Traits::Arguments, Argc >::Value; + + m_invokable = new MemberFunctionInvokable< T, Args, void >( function ); + m_parameterTypes = ConnectionTypes< typename Traits::Arguments >::types(); +} + +template< typename T, QskMetaCall::IsFunction< T >* = nullptr > +inline QskMetaFunction::QskMetaFunction( T function ) +{ + using namespace QtPrivate; + using namespace QskMetaCall; + + using Traits = FunctionPointer< T >; + + const int Argc = Traits::ArgumentCount; + using Args = typename List_Left< typename Traits::Arguments, Argc >::Value; + + m_invokable = new FunctionInvokable< T, Args, void >( function ); + m_parameterTypes = ConnectionTypes< typename Traits::Arguments >::types(); +} + +template< typename T, QskMetaCall::IsFunctor< T >* = nullptr > +inline QskMetaFunction::QskMetaFunction( T functor ) +{ + using namespace QtPrivate; + + using Traits = FunctionPointer< decltype( &T::operator() ) >; + + const int Argc = Traits::ArgumentCount; + using Args = typename List_Left< typename Traits::Arguments, Argc >::Value; + + m_invokable = new QskMetaCall::FunctorInvokable< T, Argc, Args, void > ( functor ); + m_parameterTypes = ConnectionTypes< typename Traits::Arguments >::types(); +} + +Q_DECLARE_METATYPE( QskMetaFunction ) + +#endif