From 05e2b91c0199a12ef7456b3ae49d51acbe266fb6 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 2 Mar 2018 14:58:43 +0100 Subject: [PATCH] QskMeta stuff seem to work now, but needs more testing --- playground/invoker/Invoker.cpp | 15 ++- playground/invoker/Invoker.h | 1 + playground/invoker/main.cpp | 54 ++++++--- src/common/QskMetaCallback.cpp | 139 ++-------------------- src/common/QskMetaCallback.h | 15 +-- src/common/QskMetaFunction.cpp | 4 +- src/common/QskMetaFunction.h | 2 +- src/common/QskMetaMethod.cpp | 205 +++++++++++++++++++++++++++++++++ src/common/QskMetaMethod.h | 47 ++++++++ src/src.pro | 2 + 10 files changed, 328 insertions(+), 156 deletions(-) create mode 100644 src/common/QskMetaMethod.cpp create mode 100644 src/common/QskMetaMethod.h diff --git a/playground/invoker/Invoker.cpp b/playground/invoker/Invoker.cpp index 96c6bde0..913b438c 100644 --- a/playground/invoker/Invoker.cpp +++ b/playground/invoker/Invoker.cpp @@ -17,12 +17,20 @@ void Invoker::addCallback( const QObject* object, m_callbacks.append( QskMetaCallback( object, function ) ); } +void Invoker::addCallback( const QObject* object, + const char* methodName ) +{ + m_callbacks.append( QskMetaCallback( object, methodName ) ); +} + void Invoker::invoke( qreal realValue, int intValue, Qt::ConnectionType connectionType ) { + QString s = QString( "S: %1 + %2" ).arg( realValue ).arg( intValue ); + for ( auto& callback : m_callbacks ) { - void* args[3] = { nullptr }; + void* args[4] = { nullptr }; const auto types = callback.parameterTypes(); @@ -41,6 +49,11 @@ void Invoker::invoke( qreal realValue, int intValue, args[i++] = reinterpret_cast< void* >( &realValue ); break; } + case QMetaType::QString: + { + args[i++] = reinterpret_cast< void* >( &s ); + break; + } default: break; } diff --git a/playground/invoker/Invoker.h b/playground/invoker/Invoker.h index 5b2a4a23..9df4b266 100644 --- a/playground/invoker/Invoker.h +++ b/playground/invoker/Invoker.h @@ -19,6 +19,7 @@ public: void addCallback( const QskMetaFunction& ); void addCallback( const QObject*, const QskMetaFunction& ); + void addCallback( const QObject*, const char* methodName ); void invoke( qreal d, int i, Qt::ConnectionType ); diff --git a/playground/invoker/main.cpp b/playground/invoker/main.cpp index fbac5c13..b785120d 100644 --- a/playground/invoker/main.cpp +++ b/playground/invoker/main.cpp @@ -9,44 +9,52 @@ #include #include -static void debugNone1() +void debugNone1() { qDebug() << "None 1"; } -static void debugNone2() +void debugNone2() { qDebug() << "None 2"; } -static void debugValueI1( int i ) +void debugValueI1( int i ) { qDebug() << "I1" << i; } -static void debugValueI2( int i ) +void debugValueI2( int i ) { qDebug() << "I2" << i; } -static void debugValueD( qreal d ) +void debugValueD( qreal d ) { qDebug() << "D" << d; } -static void debugValue( qreal d, int i ) +void debugValue( qreal d, int i ) { qDebug() << d << i; } class MyObject : public QObject { + Q_OBJECT + public: MyObject( QObject* parent = nullptr ): QObject( parent ) { } + Q_INVOKABLE void printInvokable( double d, int i ) const + { + qDebug() << "invokable" << d << i; + } + +public Q_SLOTS: void print0( double d, int i ) const { qDebug() << "print0" << d << i; @@ -71,19 +79,36 @@ public: { qDebug() << i; } + + void printS( const QString& s ) const + { + qDebug() << s; + } }; static auto fs = []( int i, double d ) { qDebug() << i << d; }; -class Application: public QCoreApplication +class Application : public QCoreApplication { public: - Application( int &argc, char *argv[] ): + Application( int& argc, char* argv[] ): QCoreApplication( argc, argv ), m_object( new MyObject() ), m_thread( new QThread( this ) ) { - auto f = [this]( int i, double d ) { qDebug() << i << d << (++m_num); }; +#if 1 + m_invoker.addCallback( m_object, "print0(double,int)" ); + m_invoker.addCallback( m_object, "print1(double,int)" ); + m_invoker.addCallback( m_object, SLOT(print2(int,double)) ); + m_invoker.addCallback( m_object, "print3(double)" ); + m_invoker.addCallback( m_object, "print4(int)" ); + m_invoker.addCallback( m_object, "print4(int)" ); + m_invoker.addCallback( m_object, "printS(QString)" ); + m_invoker.addCallback( m_object, "printInvokable(double,int)" ); +#endif + +#if 1 + auto f = [this]( int i, double d ) { qDebug() << i << d << ( ++m_num ); }; m_invoker.addCallback( m_object, &MyObject::print0 ); m_invoker.addCallback( m_object, &MyObject::print1 ); @@ -99,11 +124,12 @@ public: m_invoker.addCallback( m_object, &MyObject::print2 ); m_invoker.addCallback( m_object, &MyObject::print3 ); m_invoker.addCallback( m_object, &MyObject::print4 ); + m_invoker.addCallback( m_object, &MyObject::printS ); m_invoker.addCallback( m_object, []( double d, int i ) { qDebug() << d << i; } ); - + m_invoker.addCallback( m_object, f ); m_invoker.addCallback( m_object, fs ); - + m_invoker.addCallback( m_object, []( double d ) { qDebug() << d; } ); m_invoker.addCallback( []() { qDebug() << "HERE"; } ); m_invoker.addCallback( []( int i, double d ) { qDebug() << i << d; } ); @@ -111,7 +137,7 @@ public: m_invoker.addCallback( []( int i ) { qDebug() << "I2" << i; } ); m_invoker.addCallback( []( double d ) { qDebug() << "V" << d; } ); m_invoker.addCallback( []( const double& d ) { qDebug() << "R" << d; } ); - +#endif } virtual ~Application() @@ -134,7 +160,7 @@ public: void invokeBlockingQueued() { m_thread->start(); - + m_object->moveToThread( m_thread ); qDebug() << "== Blocking Queued Connections"; @@ -164,3 +190,5 @@ int main( int argc, char* argv[] ) return app.exec(); } + +#include "main.moc" diff --git a/src/common/QskMetaCallback.cpp b/src/common/QskMetaCallback.cpp index ce480b7f..e89f9fe7 100644 --- a/src/common/QskMetaCallback.cpp +++ b/src/common/QskMetaCallback.cpp @@ -5,31 +5,10 @@ #include "QskMetaCallback.h" #include "QskMetaFunction.h" +#include "QskMetaMethod.h" -#include -#include -#include -#include #include -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END - -static inline void qskInvokeMethodQueued( QObject* object, - const QMetaObject* metaObject, ushort methodIndex, - int nargs, int* types, void* args[], QSemaphore* semaphore = nullptr ) -{ - constexpr QObject* sender = nullptr; - constexpr int signalId = -1; - - auto event = new QMetaCallEvent( - metaObject->methodOffset(), methodIndex, metaObject->d.static_metacall, - sender, signalId, nargs, types, args, semaphore ); - - QCoreApplication::postEvent( object, event ); -} - QskMetaCallback::QskMetaCallback( const QObject* object, const QMetaMethod& method, Qt::ConnectionType connectionType ): m_object( const_cast< QObject* >( object ) ), @@ -40,6 +19,12 @@ QskMetaCallback::QskMetaCallback( const QObject* object, { } +QskMetaCallback::QskMetaCallback( const QObject* object, + const char* methodName, Qt::ConnectionType connectionType ): + QskMetaCallback( object, QskMetaMethod::method( object, methodName ), connectionType ) +{ +} + QskMetaCallback::QskMetaCallback( const QObject* object, const QskMetaFunction& function, Qt::ConnectionType connectionType ): m_object( const_cast< QObject* >( object ) ), @@ -230,7 +215,7 @@ void QskMetaCallback::invoke( void* args[] ) { case MetaMethod: { - qskInvokeMethod( object, m_methodData.metaObject, + QskMetaMethod::invoke( object, m_methodData.metaObject, m_methodData.methodIndex, args, connectionType() ); break; @@ -247,111 +232,3 @@ void QskMetaCallback::invoke( void* args[] ) break; } } - -void qskInvokeMethod( QObject* object, - const QMetaMethod& method, void* args[], - Qt::ConnectionType connectionType ) -{ - auto metaObject = method.enclosingMetaObject(); - if ( metaObject == nullptr ) - return; - - const int methodIndex = method.methodIndex() - metaObject->methodOffset(); - qskInvokeMethod( object, metaObject, methodIndex, args, connectionType ); -} - -void qskInvokeMethod( QObject* object, - const QMetaObject* metaObject, int methodIndex, void* args[], - Qt::ConnectionType connectionType ) -{ - if ( ( metaObject == nullptr ) || ( methodIndex < 0 ) - || ( methodIndex > metaObject->methodCount() ) ) - { - return; - } - - 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, - QMetaObject::InvokeMetaMethod, methodIndex, args ); - } - else - { - QMetaObject::metacall( object, - QMetaObject::InvokeMetaMethod, methodIndex, args ); - } - } - 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 ) ) ); - auto arguments = static_cast< void** >( malloc( paramCount * sizeof( void* ) ) ); - - types[0] = QMetaType::UnknownType; // a return type is not possible - arguments[0] = nullptr; - - for ( int i = 1; i < paramCount; i++ ) - { - if ( arguments[i] == nullptr ) - { - Q_ASSERT( arguments[i] != nullptr ); - - free( types ); - free( arguments ); - - return; - } - - types[i] = method.parameterType( i ); - arguments[i] = args[i - 1]; - } - - Q_ASSERT( args[paramCount] == nullptr ); - - if ( connectionType == Qt::QueuedConnection ) - { - qskInvokeMethodQueued( object, - metaObject, methodIndex, paramCount + 1, types, args ); - } - else - { - QSemaphore semaphore; - - qskInvokeMethodQueued( object, - metaObject, methodIndex, paramCount + 1, types, args, &semaphore ); - - semaphore.acquire(); - } - } -} diff --git a/src/common/QskMetaCallback.h b/src/common/QskMetaCallback.h index ebf3ba48..878c3e6c 100644 --- a/src/common/QskMetaCallback.h +++ b/src/common/QskMetaCallback.h @@ -10,12 +10,14 @@ #include #include +#include class QskMetaInvokable; class QskMetaFunction; +class QMetaObject; class QMetaMethod; -class QskMetaCallback +class QSK_EXPORT QskMetaCallback { public: enum Type @@ -37,6 +39,9 @@ public: QskMetaCallback( const QObject*, const QMetaMethod&, Qt::ConnectionType = Qt::AutoConnection ); + QskMetaCallback( const QObject*, const char* methodName, + Qt::ConnectionType = Qt::AutoConnection ); + QskMetaCallback( const QskMetaCallback& ); ~QskMetaCallback(); @@ -99,14 +104,6 @@ inline Qt::ConnectionType QskMetaCallback::connectionType() const return static_cast< Qt::ConnectionType >( m_connectionType ); } -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 f65f9710..999a0b28 100644 --- a/src/common/QskMetaFunction.cpp +++ b/src/common/QskMetaFunction.cpp @@ -117,7 +117,7 @@ void QskMetaFunction::invoke( { // code is not thread safe - TODO ... - QPointer receiver( object ); + QPointer< QObject > receiver( object ); if ( m_invokable == nullptr ) return; @@ -189,6 +189,8 @@ void QskMetaFunction::invoke( // object might have died in the meantime free( types ); free( arguments ); + + return; } qskInvokeFunctionQueued( object, m_invokable, argc, types, arguments ); diff --git a/src/common/QskMetaFunction.h b/src/common/QskMetaFunction.h index 50690080..fc21f791 100644 --- a/src/common/QskMetaFunction.h +++ b/src/common/QskMetaFunction.h @@ -141,7 +141,7 @@ inline QskMetaFunction::QskMetaFunction( T function ) constexpr int Argc = Traits::ArgumentCount; using Args = typename List_Left< typename Traits::Arguments, Argc >::Value; - + m_invokable = new QskMetaFunctionInvokable< T, Args, void >( function ); m_invokable->setParameterTypes( ConnectionTypes< typename Traits::Arguments >::types() ); diff --git a/src/common/QskMetaMethod.cpp b/src/common/QskMetaMethod.cpp new file mode 100644 index 00000000..f550070c --- /dev/null +++ b/src/common/QskMetaMethod.cpp @@ -0,0 +1,205 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskMetaMethod.h" + +#include +#include +#include +#include +#include +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +static inline void qskInvokeMethodQueued( QObject* object, + const QMetaObject* metaObject, ushort methodIndex, + int nargs, int* types, void* args[], QSemaphore* semaphore = nullptr ) +{ + constexpr QObject* sender = nullptr; + constexpr int signalId = -1; + + const int methodOffset = metaObject->methodOffset(); + + auto event = new QMetaCallEvent( + methodOffset, methodIndex - methodOffset, + metaObject->d.static_metacall, + sender, signalId, nargs, types, args, semaphore ); + + QCoreApplication::postEvent( object, event ); +} + +void QskMetaMethod::invoke( QObject* object, + const QMetaMethod& method, void* args[], + Qt::ConnectionType connectionType ) +{ + auto metaObject = method.enclosingMetaObject(); + if ( metaObject == nullptr ) + return; + + const int methodIndex = method.methodIndex() - metaObject->methodOffset(); + invoke( object, metaObject, methodIndex, args, connectionType ); +} + +void QskMetaMethod::invoke( QObject* object, + const QMetaObject* metaObject, int methodIndex, void* argv[], + Qt::ConnectionType connectionType ) +{ + if ( ( metaObject == nullptr ) || ( methodIndex < 0 ) + || ( methodIndex >= metaObject->methodCount() ) ) + { + return; + } + + QPointer< QObject > receiver( object ); + + int invokeType = connectionType & 0x3; + + if ( invokeType == Qt::AutoConnection ) + { + invokeType = ( object && object->thread() != QThread::currentThread() ) + ? Qt::QueuedConnection : Qt::DirectConnection; + } + + switch( invokeType ) + { + case Qt::DirectConnection: + { +#if 1 + if ( receiver.isNull() ) + return; // do we really always need an object here ??? +#endif + + const int index = methodIndex - metaObject->methodOffset(); + + if ( metaObject->d.static_metacall ) + { + metaObject->d.static_metacall( receiver, + QMetaObject::InvokeMetaMethod, index, argv ); + } + else + { + QMetaObject::metacall( receiver, + QMetaObject::InvokeMetaMethod, index, argv ); + } + break; + } + case Qt::BlockingQueuedConnection: + { + if ( receiver.isNull() + || ( receiver->thread() == QThread::currentThread() ) ) + { + // We would end up in a deadlock, better do nothing + return; + } + + QSemaphore semaphore; + + qskInvokeMethodQueued( receiver, metaObject, + methodIndex, 0, nullptr, argv, &semaphore ); + + semaphore.acquire(); + + break; + } + case Qt::QueuedConnection: + { + if ( receiver == nullptr ) + return; + +#if 1 + // should be doable without QMetaMethod. TODO ... + const auto method = metaObject->method( methodIndex ); +#endif + const int argc = method.parameterCount() + 1; + + auto types = static_cast< int* >( malloc( argc * sizeof( int ) ) ); + auto arguments = static_cast< void** >( malloc( argc * sizeof( void* ) ) ); + + /* + The first one is the return type, one that is always + invalid for Queued Connections. + */ + + types[0] = QMetaType::UnknownType; + arguments[0] = nullptr; + + for ( int i = 1; i < argc; i++ ) + { + if ( argv[i] == nullptr ) + { + Q_ASSERT( argv[i] != nullptr ); + receiver = nullptr; + break; + } + + types[i] = method.parameterType( i - 1 ); + arguments[i] = QMetaType::create( types[i], argv[i] ); + } + + if ( receiver.isNull() ) + { + // object might have died in the meantime + free( types ); + free( arguments ); + + return; + } + + qskInvokeMethodQueued( object, + metaObject, methodIndex, argc, types, arguments ); + + break; + } + } +} + +QMetaMethod QskMetaMethod::method( + const QMetaObject* metaObject, const char* methodName ) +{ + if ( metaObject == nullptr || methodName == nullptr ) + return QMetaMethod(); + + constexpr char signalIndicator = '0' + QSIGNAL_CODE; + constexpr char slotIndicator = '0' + QSLOT_CODE; + + int index = -1; + + if( methodName[0] == signalIndicator ) + { + auto signature = QMetaObject::normalizedSignature( methodName + 1 ); + index = metaObject->indexOfSignal( signature ); + } + else if ( methodName[0] == slotIndicator ) + { + auto signature = QMetaObject::normalizedSignature( methodName + 1 ); + index = metaObject->indexOfSlot( signature ); + } + else + { + auto signature = QMetaObject::normalizedSignature( methodName ); + index = metaObject->indexOfMethod( signature ); + } + + return ( index >= 0 ) ? metaObject->method( index ) : QMetaMethod(); +} + +QMetaMethod QskMetaMethod::notifySignal( + const QMetaObject* metaObject, const char* propertyName ) +{ + if ( metaObject == nullptr || propertyName == nullptr ) + return QMetaMethod(); + + const int propertyIndex = metaObject->indexOfProperty( propertyName ); + if ( propertyIndex ) + { + const auto property = metaObject->property( propertyIndex ); + return property.notifySignal(); + } + + return QMetaMethod(); +} diff --git a/src/common/QskMetaMethod.h b/src/common/QskMetaMethod.h new file mode 100644 index 00000000..f1f703b1 --- /dev/null +++ b/src/common/QskMetaMethod.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_META_METHOD_H +#define QSK_META_FUNCTION_H 1 + +#include "QskGlobal.h" + +#include +#include + +class QObject; +class QMetaMethod; +class QMetaObject; + +namespace QskMetaMethod +{ + QSK_EXPORT void invoke( + QObject* object, const QMetaMethod&, void* args[], + Qt::ConnectionType = Qt::AutoConnection ); + + QSK_EXPORT void invoke( + QObject*, const QMetaObject*, int methodIndex, void* args[], + Qt::ConnectionType = Qt::AutoConnection ); + + QSK_EXPORT QMetaMethod method( const QMetaObject*, const char* methodName ); + QSK_EXPORT QMetaMethod method( const QObject*, const char* methodName ); + + QSK_EXPORT QMetaMethod notifySignal( const QMetaObject*, const char* propertyName ); + QSK_EXPORT QMetaMethod notifySignal( const QObject*, const char* propertyName ); +} + +inline QMetaMethod QskMetaMethod::method( + const QObject* object, const char* methodName ) +{ + return object ? method( object->metaObject(), methodName ) : QMetaMethod(); +} + +inline QMetaMethod QskMetaMethod::notifySignal( + const QObject* object, const char* propertyName ) +{ + return object ? notifySignal( object->metaObject(), propertyName ) : QMetaMethod(); +} + +#endif diff --git a/src/src.pro b/src/src.pro index 66c56e2d..d1a471b8 100644 --- a/src/src.pro +++ b/src/src.pro @@ -43,6 +43,7 @@ HEADERS += \ common/QskMetaCallback.h \ common/QskMetaFunction.h \ common/QskMetaInvokable.cpp \ + common/QskMetaMethod.h \ common/QskModule.h \ common/QskNamespace.h \ common/QskObjectCounter.h \ @@ -62,6 +63,7 @@ SOURCES += \ common/QskMetaCallback.cpp \ common/QskMetaFunction.cpp \ common/QskMetaInvokable.cpp \ + common/QskMetaMethod.cpp \ common/QskModule.cpp \ common/QskObjectCounter.cpp \ common/QskSizePolicy.cpp \