Qt connect函数 functor 的使用_山雨晴空的博客-程序员秘密_qt functor

技术标签: Visual C/C++  Qt  qt  

1. QObject::connect  经典方式是 字符串 链接

声明:

static QMetaObject::Connection connect(
                      const QObject *sender, const char *signal, 
                     const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection ); 

connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));

信号(Signal) 槽(Slot) 链接 使用  主要  有两种:

    template<typename PointerToMemberFunction, typename Functor>
    static QMetaObject::Connection connect
             ( const QObject *sender, PointerToMemberFunction signal, 
               Functor functor);

    template<typename PointerToMemberFunction, typename Functor>
    static QMetaObject::Connection connect
             (const QObject *sender, PointerToMemberFunction signal, 
              const QObject *context, Functor functor, 
              Qt::ConnectionType type = Qt::AutoConnection);

1. 提供 const QObject *receiver 参数

2. 没有  const QObject *receiver 参数

注意: 上面的形式 是方便C++用户理解使用的语法形式。 也是QT文档使用的形式(Q_DOC)

             内部实现的代码是下面的形式。

参看实现  可知: 如果没有 提供 const QObject *receiver 参数, 则使用 sender.

    //connect to a functor
    template <typename Func1, typename Func2>
    static inline typename std::enable_if<
          QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, 
          QMetaObject::Connection>::type
    connect( const typename QtPrivate::FunctionPointer<Func1>::Object *sender, 
             Func1 signal, 
             Func2 slot)
    {
        return connect(sender, signal, sender, slot, Qt::DirectConnection);
    } 
template <typename Func1, typename Func2>

static inline QMetaObject::Connection connect(
    const typename QtPrivate::FunctionPointer<Func1>::Object *sender, 
    Func1 signal,
    const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, 
    Func2 slot,
    Qt::ConnectionType type = Qt::AutoConnection)

对于类的成员函数, 使用的是 模板进行解析,利用的是模板的参数匹配功能,使用的语法解析:

    template<class Obj, typename Ret, typename... Args> 
    struct FunctionPointer<Ret (Obj::*) (Args...)>

    {
        typedef Obj Object;
        typedef List<Args...>  Arguments;
        typedef Ret ReturnType;
        typedef Ret (Obj::*Function) (Args...);
        enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true};
        template <typename SignalArgs, typename R>
        static void call(Function f, Obj *o, void **arg) 
        {
            FunctorCall<
                typename Indexes<ArgumentCount>::Value, 
                SignalArgs, R, 
                Function>
            ::call(f, o, arg);
        }
    };

利用这个模板 将 ClassName::MemberFunction 的函数  分离出:

    Object:   ClassName

   ReturnType  Ret

  Arguments    参数列表

函数调用 和参数传递

void TestSigObj::sglClass(ClassParam _t1, const QString _t2)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
    int signal_index = signalOffset + local_signal_index;


    void *empty_argv[] = { 0 };
    if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
        qt_signal_spy_callback_set.signal_begin_callback(sender, signal_index,
                                                         argv ? argv : empty_argv);
    }

    {
    QMutexLocker locker(signalSlotLock(sender));

    ConnectionListsRef connectionLists = sender->d_func()->connectionLists;
    if (!connectionLists.connectionLists) {
        locker.unlock();
        if (qt_signal_spy_callback_set.signal_end_callback != 0)
            qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);
        return;
    }

    const QObjectPrivate::ConnectionList *list;
    if (signal_index < connectionLists->count())
        list = &connectionLists->at(signal_index);
    else
        list = &connectionLists->allsignals;

    Qt::HANDLE currentThreadId = QThread::currentThreadId();

    do {
        QObjectPrivate::Connection *c = list->first;
        if (!c) continue;
        // We need to check against last here to ensure that signals added
        // during the signal emission are not emitted in this emission.
        QObjectPrivate::Connection *last = list->last;

        do {
            if (!c->receiver)
                continue;

            QObject * const receiver = c->receiver;
            const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId.load();

            // determine if this connection should be sent immediately or
            // put into the event queue
            if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                || (c->connectionType == Qt::QueuedConnection)) {
                queued_activate(sender, signal_index, c, argv ? argv : empty_argv, locker);
                continue;
#ifndef QT_NO_THREAD
            } else if (c->connectionType == Qt::BlockingQueuedConnection) {
                if (receiverInSameThread) {
                    qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
                    "Sender is %s(%p), receiver is %s(%p)",
                    sender->metaObject()->className(), sender,
                    receiver->metaObject()->className(), receiver);
                }
                QSemaphore semaphore;
                QMetaCallEvent *ev = c->isSlotObject ?
                    new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore) :
                    new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore);
                QCoreApplication::postEvent(receiver, ev);
                locker.unlock();
                semaphore.acquire();
                locker.relock();
                continue;
#endif
            }

            QConnectionSenderSwitcher sw;

            if (receiverInSameThread) {
                sw.switchSender(receiver, sender, signal_index);
            }
            if (c->isSlotObject) {
                c->slotObj->ref();
                QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
                locker.unlock();
                obj->call(receiver, argv ? argv : empty_argv);

                // Make sure the slot object gets destroyed before the mutex is locked again, as the
                // destructor of the slot object might also lock a mutex from the signalSlotLock() mutex pool,
                // and that would deadlock if the pool happens to return the same mutex.
                obj.reset();

                locker.relock();
            } else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
                //we compare the vtable to make sure we are not in the destructor of the object.
                const int methodIndex = c->method();
                const int method_relative = c->method_relative;
                const auto callFunction = c->callFunction;
                locker.unlock();
                if (qt_signal_spy_callback_set.slot_begin_callback != 0)
                    qt_signal_spy_callback_set.slot_begin_callback(receiver, methodIndex, argv ? argv : empty_argv);

                callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, methodIndex);
                locker.relock();
            } else {
                const int method = c->method_relative + c->method_offset;
                locker.unlock();

                if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
                    qt_signal_spy_callback_set.slot_begin_callback(receiver,
                                                                method,
                                                                argv ? argv : empty_argv);
                }

                metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, method);

                locker.relock();
            }

            if (connectionLists->orphaned)
                break;
        } while (c != last && (c = c->nextConnectionList) != 0);

        if (connectionLists->orphaned)
            break;
    } while (list != &connectionLists->allsignals &&
        //start over for all signals;
        ((list = &connectionLists->allsignals), true));

    }

    if (qt_signal_spy_callback_set.signal_end_callback != 0)
        qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);

}

same thread

                QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
                locker.unlock();
                obj->call(receiver, argv ? argv : empty_argv);
        inline void call(QObject *r, void **a)  { m_impl(Call,    this, r, a, Q_NULLPTR); }

    template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase
    {
        typedef QtPrivate::Functor<Func, N> FuncType;
        Func function;
        static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
        {
            switch (which) {
            case Destroy:
                delete static_cast<QFunctorSlotObject*>(this_);
                break;
            case Call:
                FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
                break;
            case Compare: // not implemented
            case NumOperations:
                Q_UNUSED(ret);
            }
        }
    public:
        explicit QFunctorSlotObject(const Func &f) : QSlotObjectBase(&impl), function(f) {}
    };

    template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
    struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> {
        static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) {
            (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
        }
    };

2. QTimer::singleShot

支持  lamda  functor.  也有两种形式

 没有  const QObject *receiver 参数 的 实现:

    // singleShot to a functor or function pointer (without context)
    template <typename Duration, typename Func1>
    static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
                                          !std::is_same<const char*, Func1>::value, void>::type
            singleShot(Duration interval, Func1 slot)
    {
        singleShot(interval, defaultTypeFor(interval), nullptr, slot);
    }

可知:  如果没有 提供 const QObject *receiver 参数, 和  QObject::connect 不同的是 使用 nullptr.

receiver  == nullptr  处理函数 是在那个 执行线程  中呢  ? 

实现:

void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
                            const QObject *receiver,
                            QtPrivate::QSlotObjectBase *slotObj)
{
    new QSingleShotTimer(msec, timerType, receiver, slotObj);
}

QSingleShotTimer::QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, QtPrivate::QSlotObjectBase *slotObj)
    : QObject(QAbstractEventDispatcher::instance()), hasValidReceiver(r), receiver(r), slotObj(slotObj)
{
    timerId = startTimer(msec, timerType);
    if (r && thread() != r->thread()) {
        // Avoid leaking the QSingleShotTimer instance in case the application exits before the timer fires
        connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
        setParent(0);
        moveToThread(r->thread());
    }
}

可知:  如果 receiver == nullptr,  父对象是: QAbstractEventDispatcher::instance()

static QAbstractEventDispatcher *instance(QThread *thread = Q_NULLPTR);

QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread)
{
    QThreadData *data = thread ? QThreadData::get2(thread) : QThreadData::current();
    return data->eventDispatcher.load();
}

执行 startTimer 语句, 则会在创建Timer的线程事件队列处理。

void QSingleShotTimer::timerEvent(QTimerEvent *)
{
    // need to kill the timer _before_ we emit timeout() in case the
    // slot connected to timeout calls processEvents()
    if (timerId > 0)
        killTimer(timerId);
    timerId = -1;

    if (slotObj) {
        // If the receiver was destroyed, skip this part
        if (Q_LIKELY(!receiver.isNull() || !hasValidReceiver)) {
            // We allocate only the return type - we previously checked the function had
            // no arguments.
            void *args[1] = { 0 };
            slotObj->call(const_cast<QObject*>(receiver.data()), args);
        }
    } else {
        emit timeout();
    }


}

3. QTimer  

若没有指定 对象 持有者, 则在 当前的执行线程(即  创建这个QTimer 的线程 )中, 启动 timer.

slot 调用流程 (slot 是 Lamda)

    template <int... II, typename... SignalArgs, typename R, typename Function>
    struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> {
        static void call(Function &f, void **arg) {
            f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
        }
    };

arg:   是原始 堆栈列表

slot 是函数,  调用的形式:

    template <int... II, typename... SignalArgs, 
              typename R, typename... SlotArgs, typename SlotRet, 
              class Obj>
    struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, 
                      SlotRet (Obj::*)(SlotArgs...)> 
    {
        static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg)
        {
            (o->*f)(
           (*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...),
           ApplyReturnValue<R>(arg[0]);
        }
    };

类型的校验 是模板参数编译时执行:

        Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<
typename SignalType::Arguments, 
typename SlotType::Arguments>::value),
                          "Signal and slot arguments are not compatible.");


    struct CheckCompatibleArguments<List<Arg1, Tail1...>, List<Arg2, Tail2...>>
    {
        enum { 
value = AreArgumentsCompatible<
    typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value
 && CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>::value };
    };

    struct CheckCompatibleArguments<List<Arg1, Tail1...>, List<Arg2, Tail2...>>
    {
        enum { value = AreArgumentsCompatible<typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value
                    && CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>::value };
    };

class IHHUserActionManager {
public:
    ~IHHUserActionManager(){}
    virtual QObject *getObject() = 0;
    virtual bool initUserActionAppInfo(QString appKey,QString appName,QString appVersion)=0;
    virtual void setUid(QString uid)=0;
    virtual void setUploadSize(int uploadSize)=0;
    virtual void AddActionByQueue(QString eventid,QJsonObject customJsonContent)= 0;
    virtual void AddActionByQueue(QJsonObject ActionObject)= 0;
    virtual void AddActionByQueue(QString eventid,QString customJsonContent)= 0;
};

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/phil_gu/article/details/124216135

智能推荐

【ML入门】李宏毅机器学习笔记05-分类问题1-概率生成模型_BG大龍的博客-程序员秘密

声明:小博主水平有限,本文仅代表作者本人观点,希望大家多多指导。【笔记目录】1 什么是“分类”问题?2 说明一个问题:分类问题是不可以用回归问题来解决的。3 那么,用”分类“的思维,怎么考虑?—— Generative Model4 数学推导Warning of Math1 什么是“分类”问题?李弘毅老师根据【案例】——“宝可梦的各属性值和类型”来预测“新宝可梦的...

golang常用加密解密算法总结(AES、DES、RSA、Sha1MD5)_wade3015的博客-程序员秘密_go语言:sha1算法分析

在项目开发过程中,当操作一些用户的隐私信息,诸如密码、帐户密钥等数据时,往往需要加密后可以在网上传输。这时,需要一些高效地、简单易用的加密算法加密数据,然后把加密后的数据存入数据库或进行其他操作;当需要读取数据时,把加密后的数据取出来,再通过算法解密。关于加密解密当前我们项目中常用的加解密的方式无非三种.对称加密, 加解密都使用的是同一个密钥, 其中的代表就是AES非对加解密, 加解密使...

Android开发人员不得不收集的代码_阿修307的博客-程序员秘密

为方便查找,已进行大致归类,其目录如下所示: App相关→AppUtils.java安装App installApp卸载指定包名的App uninstallApp获取当前App信息 getAppInfo获取所有已安装App信息 getAllAppsInfo根据包名判断App是否安装 isInstallApp打开指定包名的App openAppByPackageName打开指

数据结构-1_MR_Fclef的博客-程序员秘密

2016/01/29 13:56 一.算法的有穷性    1.序列Hailstone(n)={1}                                        n                                          {n}UHailstone(n/2)        n偶数                               

使用Docker-compose自动部署WordPress博客系统_DivingKitten的博客-程序员秘密

写在前面系统环境:Ubuntu 18.04 LTSDocker版本:19.03.8很多web教材都是以博客系统为例,讲解web的实践方法,在学习后,要制作出一个优秀的博客系统仍需要下不少功夫,那么,我们想要在短时间里构建起一个人的博客网站该该怎么办呢?当然是使用开源的博客系统,通过开源的博客系统,就可以在最短时间搭建出一个自己的博客,但是,还是存在部署环境的相关问题,这个时候Docker的...

Spring Boot微信开发(基于SDK-WxJava),实现了简单的消息处理、生成二维码及扫码事件处理_Guevara的笔记的博客-程序员秘密

# Spring Boot微信开发,实现了简单的消息处理、生成二维码及扫码事件处理(基于SDK-WxJava)本 Demo 基于 Spring Boot 构建,实现微信公众号后端开发功能。本项目修改自 WxJava 的 Demo 演示程序 weixin-java-mp-demo-springboot,更多官方 Demo 请[查阅此处](https://github.com/Wechat-Group/WxJava/blob/master/demo.md)。

随便推点

Android 调用原生系统的拍照,录音,录像并保存为文件_2015从头再来的博客-程序员秘密_安卓原生intent保存文件

声明requestCode常量:public strait final int TACK_RECORD =10000;public strait final int TACK_PHOTO =10001;public strait final int TACK_VIDEO =10002;1.拍照代码:Intent openCameraIntent = new Intent(Med...

Java中比较常见的主流框架及相关技术合集_嗨,您好的博客-程序员秘密_主流框架技术

SpringMVC+Spring+MyBatisSpringMVC+Struts2+HibernateSpringboot+MyBatisSpringboot比较易用,不需要配置,但追求完美还是用SSM的框架比较好。SSH是比较重量级的因为Hibernate是重量级SQL框架,而MyBatis比较轻量级。现在SSH用的比较少了,更多的是SSM以及Spring这些。常见主流框架可以到相关的网站上去了解一下Java相关的开发岗位,都有详细的任职要求说明,这些都是企...

HBase的学习笔记(1)_白居不易.的博客-程序员秘密_chad walters

HBase的学习笔记(1)1. 概述1.1 HBase的发展史historystarted by chad walters and jim2006.11 G release paper on BigTable2007.2 inital HBase prototype created as Hadoop contrib2007.10 First useable Hbase2008.1 Hadoop become Apache top-level project and Hbase becom

51Nod大数加法_熬夜的Alan Walker的博客-程序员秘密_51nod 大数加法

给出2个大整数A,B,计算A+B的结果。输入格式第1行:大数A 第2行:大数B (A,B的长度 &lt;= 10000 需注意:A B有可能为负数)输出格式输出A + B输入样例68932147586468711654886输出样例537643802472参考程序#include &lt;bits/stdc++.h&gt;using namespace std;char ch[10010];int a[10010], b[10010]...

Delphi的移动文件方法(转)/删除文件:/文件的复制_weixin_34378767的博客-程序员秘密

RenameFile,DeleteFile,MoveFileDelphi的移动文件方法uses  ShellApi;procedure ShellFileOperation(fromFile: string; toFile: string; Flags: Integer);var  shellinfo: TSHFileOpStructA;begin   with shellinfo d...

PCIe AER的INJECT机制_one_bits的博客-程序员秘密_aer_inject

一、模块背景调试PCIe AER错误恢复代码非常困难,因为它很难触发真正的硬件错误。基于软件的错误注入可用于伪造各种PCIe错误。首先,应该在内核中启用PCIe AER软件错误注入配置,即以下应位于.config中的项目。CONFIG_PCIEAER_INJECT = y或CONFIG_PCIEAER_INJECT = m使用新内核重新启动或insmod模块后,名为设备文件/ dev / aer_inject 会被创建。然后,需要一个名为aer-inject的用户空间工具,该工具可以从此获取:g

推荐文章

热门文章

相关标签