如何信号和插槽引擎盖下实现的?(How signal and slots are implement

2019-07-17 16:19发布

这个问题已经问到这个论坛,但我不明白的概念。

我读四周,似乎信号和插槽使用函数指针即信号是一个大函数里面调用所有连接的插槽(函数指针)来实现。 它是否正确? 什么是生成的MOC文件在整个故事中的作用? 我不明白的信号函数是如何知道调用哪个插槽即与插槽的连接到这个信号。

谢谢你的时间

Answer 1:

在Qt的类似解释型语言的方式实现这些东西。 也就是说,它构造一个信号名称映射到函数指针符号表,维护他们并根据需要通过函数名查找函数指针。

每次发出一个信号,即写

emit something();

你实际上调用something()函数,它会自动元对象编译器生成并放置到*.moc文件。 在这个函数中已选中该信号的时刻连接到什么插槽,和相应的插槽功能(你在自己的消息来源实现)是通过符号表依次呼叫(以上述方式)。 和emit ,像其他Qt的特定关键字,只是用C ++预处理器丢弃后*.moc产生。 事实上,在Qt的报头(之一qobjectdefs.h ),存在这样的线:

#define slots 
#define signals protected
#define emit

连接功能( connect )只是修改内保持的符号表*.moc (与文件,并传递给它的参数SIGNAL()和`SLOT宏)也被预处理,以匹配表。

这是一般的想法。 在他或她的另一个答案, ジョージ为我们提供的链接奇趣科技的邮件列表和其他SO问题,关于这个主题。



Answer 2:

我想我应该添加以下。

还有另一个链接的问题 -并有一个很好的文章 ,可以被看作是一个非常详细的扩展它的答案 ; 这里是这篇文章再次 ,改进的(虽然还不够完善)代码语法高亮显示。

这是我短期的它复述,这可能是容易出错)

基本上,当我们插入Q_OBJECT在我们的类定义宏,预处理器它扩展到静态QMetaObject实例声明,一个会由同一个类的所有实例共享:

class ClassName : public QObject // our class definition
{
    static const QMetaObject staticMetaObject; // <--= Q_OBJECT results to this

    // ... signal and slots definitions, other stuff ...

}

这种情况下,反过来,在初始化时将存储的签名"methodname(argtype1,argtype2)" )的信号和槽,什么将允许执行的indexOfMethod()调用,返回,以及通过其方法的指数的签名字符串:

struct Q_CORE_EXPORT QMetaObject
{    
    // ... skip ...
    int indexOfMethod(const char *method) const;
    // ... skip ...
    static void activate(QObject *sender, int signal_index, void **argv);
    // ... skip ...
    struct { // private data
        const QMetaObject *superdata; // links to the parent class, I guess
        const char *stringdata; // basically, "string1\0string2\0..." that contains signatures and other names 
        const uint *data; // the indices for the strings in stringdata and other stuff (e.g. flags)
        // skip
    } d;
};

现在,当moc创建moc_headername.cpp了Qt的类的头文件headername.h ,它把有签名字符串和其他数据所必需的正确初始化d结构,然后写初始化代码为staticMetaObject使用单这个数据。

它的另一个重要的事情是为生成对象的代码的qt_metacall()的方法,即经由长接受一个对象的方法ID和参数的指针数组,并调用方法switch这样的:

int ClassName::qt_metacall(..., int _id, void **_args)
{
    // ... skip ...
    switch (_id) {
        case 0: signalOrSlotMethod1(_args[1], _args[2]); break; // for a method with two args
        case 1: signalOrSlotMethod2(_args[1]); break; // for a method with a single argument
        // ... etc ...
    }
    // ... skip ...
}

最后,对于每一个信号moc产生的实现,它包含了QMetaObject::activate()调用:

void ClassName::signalName(argtype1 arg1, argtype2 arg2, /* ... */)
{
    void *_args[] = { 0, // this entry stands for the return value
                      &arg1, // actually, there's a (void*) type conversion
                      &arg2, // in the C++ style
                      // ...
                    };
    QMetaObject::activate( this, 
                           &staticMetaObject, 
                           0, /* this is the signal index in the qt_metacall() map, I suppose */ 
                           _args
                         );
}

最后, connect()调用翻译字符串方法签名它们整数ID(由所使用的那些qt_metacall()并且保持信号到槽连接的列表; 当信号被发射时, activate()代码经过该列表,并通过它们的调用适当的对象“槽” qt_metacall()方法。

综上所述,静态QMetaObject实例存储“元信息”(方法签名串等),所生成的qt_metacall()方法提供了“一个方法表”,允许任何信号/槽以通过索引调用,产生的信号实现由moc通过使用这些指标activate()最后connect()确实保持信号-槽指数图的列表的工作。

*注:有这个计划用于情况下,当我们希望不同的线程之间传递信号(我怀疑一个人看的并发症blocking_activate()的代码),但我希望的总体思路保持不变)

这是我所链接的文章,这就容易可能是错的非常粗略的认识,所以我不建议去直接读取)

PS。 因为我想提高我的Qt执行的理解 - 请让我知道在我复述任何不一致的!


因为我的其他(前面)的答案是由一些热心的编辑删除了,我会在这里的附加文本(我失踪了在帕维尔Shved的职位纳入一些细节,我怀疑谁删除了答案的人关心。)

@Pavel Shved:

我敢肯定,在某处Qt的头存在一个行:

#define emit

只是为了确认:发现它在由谷歌代码搜索旧Qt代码。 这是很可能的是它仍然存在); 找到的位置路径是:

ftp://ftp.slackware-brasil.com.br > Slackware的-7.1>的contrib> KDE-1.90> QT-2.1.1.tgz> USR> LIB> QT-2.1.1>来源>内核> qobjectdefs.h


另complementory链接: http://lists.trolltech.com/qt-interest/2007-05/thread00691-0.html -看到由Andreas Pakulat答案


这里是另一块的答案: Qt的问题:如何信号和槽工作?



文章来源: How signal and slots are implemented under the hood?