2

无法捕获已作为引用传递的参数QSignalSpy

QSignalSpy spy( myObject, SIGNAL(foo(int&)));

...
int& i=spy.at(0).at(0).value<int&>();

由于 aQVariant不能包含引用成员。简单的逻辑。

但是还有其他解决方案来检查传入的参数吗?

4

2 回答 2

3

从 Qt 5 开始,我们可以简单地connect给一个 lambda 函数,这使得QSignalSpy不必要的使用:

std::vector<Value> values;
QObject::connect(myObject, &MyObject::foo,
    [&](const auto &value)
    { values.emplace_back(value); });

myObject.somethingCausingFoo();

ASSERT_EQ(1u, values.size());
EXPECT_EQ(expectedValue, values.at(0));
于 2016-03-08T09:32:25.070 回答
1

一个“丑陋的解决方案”是破解相当简单的QSignalSpy代码以处理引用传递的参数。int我为参考参数提供了一个最小的工作示例。唯一的变化是initArgsappendArgs功能。

请注意,使用这种方法,您将只能通过引用检查传递的参数的值。你将无法改变它的价值。

initArgs函数中,我们检查是否有参数引用并填充shouldreinterpret列表。

void initArgs(const QMetaMethod &member)
{
    QList<QByteArray> params = member.parameterTypes();
    for (int i = 0; i < params.count(); ++i) {
       int tp = QMetaType::type(params.at(i).constData());
       if (tp == QMetaType::Void)
       {
           qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
                    params.at(i).constData());
           // Check if we have a reference by removing the & from the parameter name
           QString argString(params.at(i).constData());
           argString.remove("&");
           tp = QMetaType::type(argString.toStdString().c_str());
           if (tp != QMetaType::Void)
              shouldReinterpret << true;
        }
        else
            shouldReinterpret << false;
        args << tp;
     }
}

appendArgs函数,我们重新解释通过引用传递的参数:

void appendArgs(void **a)
{
    QList<QVariant> list;
    for (int i = 0; i < args.count(); ++i) {
        QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
    if (shouldReinterpret.at(i))
    {
            switch (type)
        {
        case QMetaType::Int:
            list << QVariant(type, &(*reinterpret_cast<int*>(a[i + 1])));
            break;
                // Do the same for other types  
        }
    }
    else
        list << QVariant(type, a[i + 1]);
    }
    append(list);
}

完整代码供参考:

class MySignalSpy: public QObject, public QList<QList<QVariant> >
{
public:
    MySignalSpy(QObject *obj, const char *aSignal)
    {
#ifdef Q_CC_BOR
        const int memberOffset = QObject::staticMetaObject.methodCount();
#else
        static const int memberOffset = QObject::staticMetaObject.methodCount();
#endif
        Q_ASSERT(obj);
        Q_ASSERT(aSignal);

        if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
            qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
            return;
        }

        QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
        const QMetaObject *mo = obj->metaObject();
        int sigIndex = mo->indexOfMethod(ba.constData());
        if (sigIndex < 0) {
            qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
            return;
        }

        if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
                    Qt::DirectConnection, 0)) {
            qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
            return;
        }
        sig = ba;
        initArgs(mo->method(sigIndex));
    }

    inline bool isValid() const { return !sig.isEmpty(); }
    inline QByteArray signal() const { return sig; }


    int qt_metacall(QMetaObject::Call call, int methodId, void **a)
    {
        methodId = QObject::qt_metacall(call, methodId, a);
        if (methodId < 0)
            return methodId;

        if (call == QMetaObject::InvokeMetaMethod) {
            if (methodId == 0) {
                appendArgs(a);
            }
            --methodId;
        }
        return methodId;
    }

private:
    void initArgs(const QMetaMethod &member)
    {
        QList<QByteArray> params = member.parameterTypes();
        for (int i = 0; i < params.count(); ++i) {
            int tp = QMetaType::type(params.at(i).constData());
            if (tp == QMetaType::Void)
            {
                qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
                         params.at(i).constData());

                QString argString(params.at(i).constData());
                argString.remove("&");
                tp = QMetaType::type(argString.toStdString().c_str());
                if (tp != QMetaType::Void)
                    shouldReinterpret << true;
            }
            else
                shouldReinterpret << false;
            args << tp;
        }
    }

    void appendArgs(void **a)
    {
        QList<QVariant> list;
        for (int i = 0; i < args.count(); ++i) {
            QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
            if (shouldReinterpret.at(i))
            {
                switch (type)
                {
                case QMetaType::Int:
                    int k = (*reinterpret_cast<int*>(a[i + 1]));
                    list << QVariant(type, &k);
                    break;
                }
            }
            else
                list << QVariant(type, a[i + 1]);
        }
        append(list);
    }

    // the full, normalized signal name
    QByteArray sig;
    // holds the QMetaType types for the argument list of the signal
    QList<int> args;
    // Holds the indexes of the arguments that 
    QList<bool> shouldReinterpret;
};
于 2013-01-10T16:00:13.370 回答