Saturday, April 18, 2009

再看 Qt 的 QMetaObject

首先我们看看 Q_OBJECT 展开变成了什么,在 qobjectdefs.h 文件中有
#define Q_OBJECT \
public: \
 Q_OBJECT_CHECK \
 static const QMetaObject staticMetaObject; \
 virtual const QMetaObject *metaObject() const; \
 virtual void *qt_metacast(const char *); \
 QT_TR_FUNCTIONS \
 virtual int qt_metacall(QMetaObject::Call, int, void **); \
private:
首先调用了 Q_OBJECT_CHECK (插入了一个 qt_check_for_QOBJECT_macro 的 template function),然后是全局常量 QMetaObject 对象,因此可以用 QClassname::staticMetaObject 直接访问,另外提供了两个接口函数 metaObject() 用于不同的 class 返回自己的 staticMetaObject、qt_metacast() 用于转换,我们在 moc 产生的文件里面可以找到这两个接口的实现,
const QMetaObject *Counter::metaObject() const
{
 return &staticMetaObject;
}

void *Counter::qt_metacast(const char *_clname)
{
 if (!_clname) return 0;
 if (!strcmp(_clname, qt_meta_stringdata_Counter))
   return static_cast<void*>(const_cast< Counter*>(this));
 return QObject::qt_metacast(_clname);
}
后者很明显,如果需要转换的名字 _clname 是自己的类名,就把自己的指针通过转换成 void* 传回去,否则调用 QOject::qt_metacast(),其实就是看是不是 QObject 了,否则就返回 0 了。另外 QT_TR_FUNCTIONS 是对应的 i18n 的函数,我们后面再看。最后还有一个 qt_metacall 的接口,实现如下
int Counter::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
 _id = QObject::qt_metacall(_c, _id, _a);
 if (_id < 0)
   return _id;
 if (_c == QMetaObject::InvokeMetaMethod) {
   switch (_id) {
   case 0:
     valueChanged((*reinterpret_cast< int(*)>(_a[1])));
     break;
   case 1:
     setValue((*reinterpret_cast< int(*)>(_a[1])));
     break;
   default: ;
   }
   _id -= 2;
 }
 return _id;
}
这个函数起到一个中间作用,可以间接的调用成员方法,我们来仔细看看 QMetaObject,同一个文件里面有该结构的定义,我们只看这一部分,
struct Q_CORE_EXPORT QMetaObject
{
 const char *className() const;
 const QMetaObject *superClass() const;

 QObject *cast(QObject *obj) const;

 // ...

 struct { // private data
   const QMetaObject *superdata;
   const char *stringdata;
   const uint *data;
   const void *extradata;
 } d;
} ;
注意 Couter 类 QMetaObject 的初始化,
const QMetaObject Counter::staticMetaObject = {
 { &QObject::staticMetaObject, qt_meta_stringdata_Counter,
  qt_meta_data_Counter, 0 }
} ;
下面我们着重看看几个与 signal/slot 相关的代码,首先就是 [qt]/src/corelib/kernel/qobject.cpp 文件中关于 QObject::connect() 函数的代码,
bool QObject::connect(const QObject *sender, const char *signal,
                  const QObject *receiver, const char *method,
                  Qt::ConnectionType type)
{
 {
   const void *cbdata[] = { sender, signal, receiver, method, &type };
   if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
     return true;
 }

 // checking sender, receiver, compatability of signal and slot

 QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
 const_cast<QObject*>(sender)->connectNotify(signal - 1);
 return true;
}
这里首先调用了 QInternal 这个 namespace 里面 activateCallbacks 这个函数,然后根据 QMetaObject 信息检查了 sender、receiver 以及对应 signal/slots 的匹配性,此时已经把 signal/slot 字符串转换成为了对应的 index,然后调用 QMetaObject::connect() 完成连接,最后的 QObject::connectNotify() 以及另外的 QObject::disconnectNotify() 其实是一个 signal。QInternal::activaeCallback() 在 [qt]/src/corelib/global/qglobal.cpp 中定义,
bool QInternal::activateCallbacks(Callback cb, void **parameters)
{
 Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");

 QInternal_CallBackTable *cbt = global_callback_table();
 if (cbt && cb < cbt->callbacks.size()) {
   QList<qInternalCallback> callbacks = cbt->callbacks[cb];
   bool ret = false;
   for (int i=0; i<callbacks.size(); ++i)
     ret |= (callbacks.at(i))(parameters);
   return ret;
 }
 return false;
}
这可以看出来调用该函数去检查一个 global_callback_table(),如果查到一个匹配的 signal/slot 就返回 true,否则返回 false,换言之 QObject::connect() 通过这个函数判定需不需要调用 QMetaObject::connect 创建新的连接。这个 callback table 本质是什么呢?同一个文件里面有
struct QInternal_CallBackTable {
QVector<QList<qInternalCallback> > callbacks;
};
所以,这是一个链表的动态数组(Orz... 本来以为就是个链表),而 qInternalCallback 是一个在 [qt]/src/corelib/global/qnamespace.h 中定义的函数指针,
typedef bool (*qInternalCallback)(void **);
现在我们可以猜测到在 QMetaObject::connect() 调用中我们会维护这个 callback table。在 [qt]/src/corelib/kernel/qmetaobject.cpp 我们有幸找到了如下代码:
bool QMetaObject::connect(const QObject *sender, int signal_index,
                      const QObject *receiver, int method_index, int type, int *types)
{
 QObject *s = const_cast<QObject *<(sender);
 QObject *r = const_cast<QObject *<(receiver);

 QOrderedMutexLocker locker(&s->d_func()->threadData->mutex,
                            &r->d_func()->threadData->mutex);

#if defined(Q_CC_HPACC) && defined(QT_ARCH_PARISC)
 QObjectPrivate::Connection c;
 c.receiver = r;
 c.method = method_index;
 c.connectionType = type;
 c.argumentTypes = types;
#else
 QObjectPrivate::Connection c = { r, method_index, type, Q_BASIC_ATOMIC_INITIALIZER(types) };
#endif
 s->d_func()->addConnection(signal_index, &c);
 r->d_func()->refSender(s, signal_index);

 if (signal_index < 0)
   sender->d_func()->connectedSignals = ~0u;
 else if (signal_index < 32)
   sender->d_func()->connectedSignals |= (1 << signal_index);

 return true;
}
这段代码中使用了防止多线程操作引起问题的 mutex,sender 和 receiver 双方都有一个结构来保证这个通讯机制,sender 是通过 addConnection,receiver 通过 refSender(),这里并没有我们猜测的 global callback table。现在我们有两个问题,一个是 sender 通过 addConnection() 和 receiver 通过 refSneder() 记录了一些什么,用什么数据结构储存,另一个是 global callback table 是做什么用的。

我们先来看看 global callback table,不难发现,该 table 是 QInternal 类(没有成员,提供了一个接口)的方法维护的,主要有 QInternal::registerCallback()、QInternal::unregisterCallback()、QInternal::activateCallbacks()、QInternal::callFunction(),意思我想都很清楚,可是 grep 了一圈,似乎只在某些调试部分看到了调用该函数的地方,莫非这是用来调试的代码?

我们来看看 QObject->d_func() 返回的是什么。

#define Q_DECLARE_PRIVATE(Class) \
  inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(d_ptr); } \
  inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(d_ptr); } \
  friend class Class##Private;

#define Q_DECLARE_PRIVATE_D(Dptr, Class) \
  inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(Dptr); } \
  inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(Dptr); } \
  friend class Class##Private;

#define Q_DECLARE_PUBLIC(Class)                                    \
  inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
  inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
  friend class Class;

#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
记得 QObject 的定义么?
class Q_CORE_EXPORT QObject
{
  Q_OBJECT
  Q_PROPERTY(QString objectName READ objectName WRITE setObjectName)
  Q_DECLARE_PRIVATE(QObject)
这样 QObject 通过 d_func() 返回 d_ptr 这个指针,这是怎么一回事呢?我们知道这个宏在 QObject 内部定义了一个 QObjectPrivate 的类,并且留下了一个指针
protected:
   QObjectData *d_ptr;
在 QObject 构造的时候创建了这个对象,并在析构的时候释放,由于是 friend class,可以对 QObject 无限制访问,
QObject::QObject(QObject *parent) : d_ptr(new QObjectPrivate)
QObject::~QObject()
{
   Q_D(QObject);
   // clear all signal receivers
   emit destroyed(this);
   // clear all signal senders
   delete d;
   d_ptr = 0;
}
可见真正管理 signal/slots 的是 QObjectPrivate 类,下面是它的主要成员函数,
//constructor and destructor
QObjectPrivate::QObjectPrivate(int version) ;
QObjectPrivate::~QObjectPrivate() ;
int *QObjectPrivate::setDeleteWatch(QObjectPrivate *d, int *w) ;
void QObjectPrivate::resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int deleteWatch) ;
void QObjectPrivate::sendPendingChildInsertedEvents() ;
void QObjectPrivate::removePendingChildInsertedEvents(QObject *child) ;
bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const ;
QObjectList QObjectPrivate::receiverList(const char *signal) const ;
QObjectList QObjectPrivate::senderList() ;
// connection list manipulation
void QObjectPrivate::addConnection(int signal, Connection *c) ;
void QObjectPrivate::removeReceiver(int signal, QObject *receiver) ;
void QObjectPrivate::cleanConnectionLists() ;
// sender list manipulation
void QObjectPrivate::refSender(QObject *sender, int signal) ;
void QObjectPrivate::derefSender(QObject *sender, int signal) ;
void QObjectPrivate::removeSender(QObject *sender, int signal) ;
QObjectPrivate::Sender *QObjectPrivate::setCurrentSender(QObject *receiver, Sender *sender) ;
void QObjectPrivate::resetCurrentSender(QObject *receiver, Sender *currentSender, Sender *previousSender) ;
void QObjectPrivate::clearGuards(QObject *object) ;
那么我们如何存储数据的呢?在 [qt]/src/corelib/kernel/qobject_p.h 里,有该类的声明,
class Q_CORE_EXPORT QObjectPrivate : public QObjectData {
 // ...
public:
 QList pendingChildInsertedEvents;
 struct Sender {
   QObject *sender;
   int signal;
   int ref;
 };
 Sender *currentSender;
 QList<QPointer<QObject> > eventFilters;

 struct Connection {
   QObject *receiver;
   int method;
   uint connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
   QBasicAtomicPointer<int> argumentTypes;
 };
 typedef QList<Connection> ConnectionList;
 QObjectConnectionListVector *connectionLists;

 QList<Sender> senders;
} ;
至此,我们已经找到了 Qt 实现 signal/slot 机制的所有需要知道的东西。

记得下面 moc 生成的代码

void Counter::valueChanged(int _t1)
{
   void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
   QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
这里重要的就是 QMetaObject::activate() 函数,其实它就是依次激活 senders 里面的函数,
void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv) ;
void QMetaObject::activate(QObject *sender, int signal_index, void **argv) ;
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, void **argv) ;
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int from_local_signal_index, int to_local_signal_index, void **argv) ;
这里调用的是第三个,他们最终都是用第一个实现的,下面是实现的基本代码,
void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)
{
   if (sender->d_func()->blockSig)
       return;
   // ...
   for (int signal = from_signal_index;
        (signal >= from_signal_index && signal <= to_signal_index) || (signal == -2);
        (signal == to_signal_index ? signal = -2 : ++signal))
   {
       int count = connectionLists->at(signal).count();
       for (int i = 0; i < count; ++i) {
           // signal type and etc
           receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
           // ...
       }
   //...
   }
   // ...
   --connectionLists->inUse;
   // ...
}

我们来回顾一下,使用 Qt 实现 signal/slots 机制是通过继承 QObject,并在类声明时加入 Q_OBJECT,该宏嵌入一个 QMetaObject 作为整个类实现 Qt 的 RTTI 机制的基础。继承 QObject 的同时使得该类含有一个 d_ptr 指向 QObject 共有的一个友元类 QObjectPrivate,每一个对象都会创建一个 QObjectPrivate 的对象来管理自己 signal/slot。对用户而言,通过声明 signal 其实是实现一个 protected function,它由 moc 生成程序,而 slot 本身就是一般的函数。signal 和 slot 通过调用 QObject::connect()/disconnect() 连接/切断连接,该函数验证连接的合理性(通过 QMetaObject 里面存放的字符串信息,如类名、父类、signal 和 slot 名称和参数),然后调用 QMetaObject::connect() 该方法会使用 mutex 保证操作的线程稳定性,访问两个对象的 d_ptr 指向的 QObjectPrivate 对象,对 sender 添加 connectionLists(一个链表),对 receiver 添加 senders(一个链表)。调用 signal 使用的 emit 其实什么都不是,可以直接调用该 signal 的函数也行,这会调用 QMetaObject::activate() 调用连接上的 slots。

No comments: