Add private API for working with meta-methods in signal index range
authorKent Hansen <kent.hansen@nokia.com>
Wed, 30 May 2012 12:02:55 +0000 (14:02 +0200)
committerQt by Nokia <qt-info@nokia.com>
Fri, 1 Jun 2012 14:04:04 +0000 (16:04 +0200)
Internally, QObject and QMetaObject already leave out non-signal
methods when working with signals. This is possible because the
signals always come before other types of meta-method in the
meta-object data. Ignoring irrelevant methods is faster and can
save memory.

QMetaObject provides internal indexed-based connect() and
disconnect() functions. However, these functions currently take an
absolute method index as the signal specifier, instead of an
absolute _signal_ index. Hence, QMetaObject and friends must convert
from the method index range to the signal index range.

By providing an API that only considers signal indices, clients of
the index-based QMetaObject::connect()/disconnect() can provide the
proper signal index directly. Similarly, for the qtdeclarative
integration (QDeclarativeData hooks) the signal index can be passed
directly. This will eliminate most of the conversions back and forth
between signal index and method index, and some other redundant work
done by qtdeclarative's custom connection implementation.

There are some places where the behavior can't be changed; for
example,  QObject::senderSignalIndex() will still need to return an
index in the method range, since that function is public API.

Changing QMetaObject::connect()/disconnect() to take an index in
the signal range will be done in a separate commit; this commit is
only an enabler for porting existing usage of those functions to
the new behavior.

Change-Id: Icb475b6bbdccc74b4e7ee5bf72b944b47159cebd
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
src/corelib/kernel/qmetaobject.cpp
src/corelib/kernel/qmetaobject.h
src/corelib/kernel/qmetaobject_p.h
tests/auto/corelib/kernel/qmetaobject/qmetaobject.pro
tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp

index 2dbabc7..417c49b 100644 (file)
@@ -798,31 +798,82 @@ int QMetaObjectPrivate::indexOfConstructor(const QMetaObject *m, const QByteArra
     return -1;
 }
 
+/*!
+    \internal
+    \since 5.0
+
+    Returns the signal offset for the class \a m; i.e., the index position
+    of the class's first signal.
+
+    Similar to QMetaObject::methodOffset(), but non-signal methods are
+    excluded.
+*/
+int QMetaObjectPrivate::signalOffset(const QMetaObject *m)
+{
+    Q_ASSERT(m != 0);
+    int offset = 0;
+    for (m = m->d.superdata; m; m = m->d.superdata)
+        offset += priv(m->d.data)->signalCount;
+    return offset;
+}
+
+/*!
+    \internal
+    \since 5.0
+
+    Returns the number of signals for the class \a m, including the signals
+    for the base class.
+
+    Similar to QMetaObject::methodCount(), but non-signal methods are
+    excluded.
+*/
+int QMetaObjectPrivate::absoluteSignalCount(const QMetaObject *m)
+{
+    Q_ASSERT(m != 0);
+    int n = priv(m->d.data)->signalCount;
+    for (m = m->d.superdata; m; m = m->d.superdata)
+        n += priv(m->d.data)->signalCount;
+    return n;
+}
+
+/*!
+    \internal
+    \since 5.0
+
+    Returns the index of the signal method \a m.
+
+    Similar to QMetaMethod::methodIndex(), but non-signal methods are
+    excluded.
+*/
+int QMetaObjectPrivate::signalIndex(const QMetaMethod &m)
+{
+    if (!m.mobj)
+        return -1;
+    return ((m.handle - priv(m.mobj->d.data)->methodData) / 5) + signalOffset(m.mobj);
+}
+
 /*! \internal
-    Returns the signal for the given \a metaObject at \a signal_index.
+    \since 5.0
+
+    Returns the signal for the given meta-object \a m at \a signal_index.
 
     It it different from QMetaObject::method(); the index should not include
     non-signal methods.
-
-    The index must correspond to a signal defined in \ a metaObject itself;
-    it should not be an inherited signal.
 */
-QMetaMethod QMetaObjectPrivate::signal(const QMetaObject *metaObject, int signal_index)
+QMetaMethod QMetaObjectPrivate::signal(const QMetaObject *m, int signal_index)
 {
     QMetaMethod result;
     if (signal_index < 0)
         return result;
-    Q_ASSERT(metaObject != 0);
-
-    int signalOffset = 0;
-    for (const QMetaObject *m = metaObject->d.superdata; m; m = m->d.superdata)
-        signalOffset += priv(m->d.data)->signalCount;
-
-    Q_ASSERT(signal_index >= signalOffset);
-    int signal_index_relative = signal_index - signalOffset;
-    if (signal_index_relative < priv(metaObject->d.data)->signalCount) {
-        result.mobj = metaObject;
-        result.handle = priv(metaObject->d.data)->methodData + 5*signal_index_relative;
+    Q_ASSERT(m != 0);
+    int i = signal_index;
+    i -= signalOffset(m);
+    if (i < 0 && m->d.superdata)
+        return signal(m->d.superdata, signal_index);
+
+    if (i >= 0 && i < priv(m->d.data)->signalCount) {
+        result.mobj = m;
+        result.handle = priv(m->d.data)->methodData + 5*i;
     }
     return result;
 }
index 1c49506..353b99f 100644 (file)
@@ -255,6 +255,7 @@ private:
     int idx;
     QMetaEnum menum;
     friend struct QMetaObject;
+    friend struct QMetaObjectPrivate;
 };
 
 class Q_CORE_EXPORT QMetaClassInfo
index 320b8dd..855a0e0 100644 (file)
@@ -203,7 +203,10 @@ struct QMetaObjectPrivate
                              int argc, const QArgumentType *types);
     static int indexOfConstructor(const QMetaObject *m, const QByteArray &name,
                                   int argc, const QArgumentType *types);
-    static QMetaMethod signal(const QMetaObject *m, int signal_index);
+    Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject *m, int signal_index);
+    Q_CORE_EXPORT static int signalOffset(const QMetaObject *m);
+    Q_CORE_EXPORT static int absoluteSignalCount(const QMetaObject *m);
+    Q_CORE_EXPORT static int signalIndex(const QMetaMethod &m);
     static bool checkConnectArgs(int signalArgc, const QArgumentType *signalTypes,
                                  int methodArgc, const QArgumentType *methodTypes);
     static bool checkConnectArgs(const QMetaMethodPrivate *signal,
index b92c803..a28b8a8 100644 (file)
@@ -1,5 +1,5 @@
 CONFIG += testcase
 CONFIG += parallel_test
 TARGET = tst_qmetaobject
-QT = core gui widgets testlib
+QT = core-private gui widgets testlib
 SOURCES = tst_qmetaobject.cpp
index 5cf28b5..78ca386 100644 (file)
@@ -44,6 +44,9 @@
 #include <qobject.h>
 #include <qmetaobject.h>
 #include <qlabel.h>
+#include <private/qmetaobject_p.h>
+
+Q_DECLARE_METATYPE(const QMetaObject *)
 
 struct MyStruct
 {
@@ -173,6 +176,15 @@ private slots:
 
     void indexOfMethodPMF();
 
+    void signalOffset_data();
+    void signalOffset();
+    void signalCount_data();
+    void signalCount();
+    void signal_data();
+    void signal();
+    void signalIndex_data();
+    void signalIndex();
+
 signals:
     void value6Changed();
     void value7Changed(const QString &);
@@ -1151,5 +1163,130 @@ void tst_QMetaObject::indexOfMethodPMF()
     INDEXOFMETHODPMF_HELPER(QtTestCustomObject, sig_custom, (const CustomString &))
 }
 
+namespace SignalTestHelper
+{
+// These functions use the public QMetaObject/QMetaMethod API to implement
+// the functionality of the internal API, and are used to check the results.
+
+static int signalCount(const QMetaObject *mo)
+{
+    int n = 0;
+    for (int i = 0; i < mo->methodCount(); ++i) {
+        QMetaMethod mm = mo->method(i);
+        if (mm.methodType() == QMetaMethod::Signal)
+            ++n;
+    }
+    return n;
+}
+
+static int signalOffset(const QMetaObject *mo)
+{
+    return mo->superClass() ? signalCount(mo->superClass()) : 0;
+}
+
+static QMetaMethod signal(const QMetaObject *mo, int index)
+{
+    int k = 0;
+    for (int i = 0; i < mo->methodCount(); ++i) {
+        QMetaMethod mm = mo->method(i);
+        if (mm.methodType() != QMetaMethod::Signal)
+            continue;
+        if (k == index)
+            return mm;
+        ++k;
+    }
+    return QMetaMethod();
+}
+
+static int signalIndex(const QMetaMethod &mm)
+{
+    int k = mm.methodIndex();
+    const QMetaObject *mo = mm.enclosingMetaObject();
+    for (int i = 0; i < mm.methodIndex(); ++i) {
+        if (mo->method(i).methodType() != QMetaMethod::Signal)
+            --k;
+    }
+    return k;
+}
+
+} // namespace SignalTestHelper
+
+void tst_QMetaObject::signalOffset_data()
+{
+    QTest::addColumn<const QMetaObject *>("metaObject");
+
+    QTest::newRow("QObject") << &QObject::staticMetaObject;
+    QTest::newRow("tst_QMetaObject") << &tst_QMetaObject::staticMetaObject;
+    QTest::newRow("QtTestObject") << &QtTestObject::staticMetaObject;
+}
+
+void tst_QMetaObject::signalOffset()
+{
+    QFETCH(const QMetaObject *, metaObject);
+    QCOMPARE(QMetaObjectPrivate::signalOffset(metaObject),
+             SignalTestHelper::signalOffset(metaObject));
+}
+
+void tst_QMetaObject::signalCount_data()
+{
+    signalOffset_data();
+}
+
+void tst_QMetaObject::signalCount()
+{
+    QFETCH(const QMetaObject *, metaObject);
+    QCOMPARE(QMetaObjectPrivate::absoluteSignalCount(metaObject),
+             SignalTestHelper::signalCount(metaObject));
+}
+
+void tst_QMetaObject::signal_data()
+{
+    QTest::addColumn<const QMetaObject *>("metaObject");
+    QTest::addColumn<int>("index");
+
+    struct SignalTestDataHelper
+    {
+        static void addSignals(const QMetaObject *mo)
+        {
+            int count = SignalTestHelper::signalCount(mo);
+            for (int i = 0; i < count; ++i) {
+                QMetaMethod mm = SignalTestHelper::signal(mo, i);
+                QByteArray tag(mo->className());
+                tag.append("::");
+                tag.append(mm.methodSignature());
+                QTest::newRow(tag.constData()) << mo << i;
+            }
+        }
+    };
+
+    SignalTestDataHelper::addSignals(&QObject::staticMetaObject);
+    SignalTestDataHelper::addSignals(&tst_QMetaObject::staticMetaObject);
+    SignalTestDataHelper::addSignals(&QtTestObject::staticMetaObject);
+}
+
+void tst_QMetaObject::signal()
+{
+    QFETCH(const QMetaObject *, metaObject);
+    QFETCH(int, index);
+
+    QCOMPARE(QMetaObjectPrivate::signal(metaObject, index),
+             SignalTestHelper::signal(metaObject, index));
+}
+
+void tst_QMetaObject::signalIndex_data()
+{
+    signal_data();
+}
+
+void tst_QMetaObject::signalIndex()
+{
+    QFETCH(const QMetaObject *, metaObject);
+    QFETCH(int, index);
+
+    QMetaMethod mm = SignalTestHelper::signal(metaObject, index);
+    QCOMPARE(QMetaObjectPrivate::signalIndex(mm),
+             SignalTestHelper::signalIndex(mm));
+}
+
 QTEST_MAIN(tst_QMetaObject)
 #include "tst_qmetaobject.moc"