/****************************************************************************** * QSkinny - Copyright (C) The authors * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ #include "QskMetaFunction.h" #include #include #include #if QT_CONFIG(thread) #include #endif QSK_QT_PRIVATE_BEGIN #include QSK_QT_PRIVATE_END namespace { using FunctionCall = QskMetaFunction::FunctionCall; // to have access to the private section of QSlotObjectBase struct SlotObject { QAtomicInt ref; FunctionCall::InvokeFunction invoke; const int* parameterTypes; }; static_assert( sizeof( SlotObject ) == sizeof( FunctionCall ), "Bad cast: QskMetaFunction does not match" ); } int QskMetaFunction::FunctionCall::typeInfo() const { auto that = const_cast< FunctionCall* >( this ); int value; reinterpret_cast< SlotObject* >( that )->invoke( TypeInfo, that, nullptr, reinterpret_cast< void** >( &value ), nullptr ); return value; } int QskMetaFunction::FunctionCall::refCount() const { auto that = const_cast< FunctionCall* >( this ); return reinterpret_cast< SlotObject* >( that )->ref.loadRelaxed(); } QskMetaFunction::QskMetaFunction() : m_functionCall( nullptr ) { } QskMetaFunction::QskMetaFunction( FunctionCall* functionCall ) : m_functionCall( functionCall ) { if ( m_functionCall ) m_functionCall->ref(); } QskMetaFunction::QskMetaFunction( const QskMetaFunction& other ) : m_functionCall( other.m_functionCall ) { if ( m_functionCall ) m_functionCall->ref(); } QskMetaFunction::QskMetaFunction( QskMetaFunction&& other ) : m_functionCall( other.m_functionCall ) { other.m_functionCall = nullptr; } QskMetaFunction::~QskMetaFunction() { if ( m_functionCall ) m_functionCall->destroyIfLastRef(); } QskMetaFunction& QskMetaFunction::operator=( QskMetaFunction&& other ) { if ( m_functionCall != other.m_functionCall ) { if ( m_functionCall ) m_functionCall->destroyIfLastRef(); m_functionCall = other.m_functionCall; other.m_functionCall = nullptr; } return *this; } QskMetaFunction& QskMetaFunction::operator=( const QskMetaFunction& other ) { if ( m_functionCall != other.m_functionCall ) { if ( m_functionCall ) m_functionCall->destroyIfLastRef(); m_functionCall = other.m_functionCall; if ( m_functionCall ) m_functionCall->ref(); } 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() == StaticFunction && other.m_functionCall->typeInfo() == StaticFunction ) { // only static functions can be compared return m_functionCall->compare( reinterpret_cast< void** >( other.m_functionCall ) ); } } return false; } int QskMetaFunction::returnType() const { return QMetaType::Void; // TODO ... } size_t QskMetaFunction::parameterCount() const { size_t count = 0; if ( auto types = parameterTypes() ) { while ( types[ count ] != QMetaType::UnknownType ) count++; } return count; } QskMetaFunction::Type QskMetaFunction::functionType() const { if ( m_functionCall == nullptr ) return Invalid; return static_cast< QskMetaFunction::Type >( m_functionCall->typeInfo() ); } void QskMetaFunction::invoke( QObject* object, void* argv[], Qt::ConnectionType connectionType ) { #if 1 /* Since Qt 5.10 we also have QMetaObject::invokeMethod with functor based callbacks. TODO ... */ #endif // code is not thread safe - TODO ... if ( m_functionCall == nullptr ) return; QPointer< QObject > receiver( object ); int invokeType = connectionType & 0x3; if ( invokeType == Qt::AutoConnection ) { invokeType = ( receiver && receiver->thread() != QThread::currentThread() ) ? Qt::QueuedConnection : Qt::DirectConnection; } switch ( invokeType ) { case Qt::DirectConnection: { m_functionCall->call( receiver, argv ); break; } case Qt::BlockingQueuedConnection: { if ( receiver.isNull() || ( receiver->thread() == QThread::currentThread() ) ) { // We would end up in a deadlock, better do nothing return; } #if QT_CONFIG(thread) QSemaphore semaphore; auto event = new QMetaCallEvent( m_functionCall, nullptr, 0, argv, &semaphore ); #else auto event = new QMetaCallEvent( m_functionCall, nullptr, 0, argv, nullptr ); #endif QCoreApplication::postEvent( receiver, event ); #if QT_CONFIG(thread) semaphore.acquire(); #endif break; } case Qt::QueuedConnection: { if ( receiver.isNull() ) return; const auto argc = parameterCount() + 1; // return value + arguments auto event = new QMetaCallEvent( m_functionCall, nullptr, 0, argc ); auto types = event->types(); auto arguments = event->args(); #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) types[0] = QMetaType(); arguments[ 0 ] = nullptr; #else types[0] = 0; arguments[ 0 ] = nullptr; #endif const int* parameterTypes = m_functionCall->parameterTypes(); for ( uint i = 1; i < argc; i++ ) { if ( argv[ i ] == nullptr ) { Q_ASSERT( arguments[ i ] != nullptr ); receiver = nullptr; break; } const auto type = parameterTypes[i - 1]; #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) types[ i ] = QMetaType( type ); arguments[ i ] = QMetaType( type ).create( argv[ i ] ); #else types[ i ] = type; arguments[ i ] = QMetaType::create( type, argv[ i ] ); #endif } if ( receiver ) QCoreApplication::postEvent( receiver, event ); else delete event; break; } } } #include "moc_QskMetaFunction.cpp"