首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >QObject通用信号处理程序

QObject通用信号处理程序
EN

Stack Overflow用户
提问于 2012-05-29 19:08:43
回答 2查看 2.4K关注 0票数 7

(对于“信号处理程序”,我指的是插槽,而不是POSIX信号的处理程序。)

我需要“连接”(可能不是直接使用QObject::connect ) all将QObject子类的一个实例(还不知道)的信号发送到另一个QObject的单个插槽。我需要这样做,以便通过网络(对于支持信号的RPC系统)发送信号(带有参数)。

(“还不知道”,意思是我的代码应该尽可能通用。因此,它不包含我在RPC系统中使用的每个类中的每个信号的connect语句,而是提供类似于RPC::connectAllSignals(QObject*);的东西,然后在运行时对所有信号进行扫描并连接它们。)

我想要实现的是:处理所有信号并序列化它们(信号名+参数)。我已经可以序列化这些参数了,但是我不知道如何获得信号名。在谷歌搜索之后,似乎不可能为QObject实例使用类似的东西,比如QObject。所以我需要做一些更复杂的事情。

我当前用于将参数传递给远程端上的目标函数的类型系统仍然限制在某些类型上。(这是因为我需要qt_metacall,它将参数排除为带有“正确类型”的void*类型。我的RPC系统内部只使用几种类型的QVariants,我使用自定义方法将它们转换为正确类型的void*。我听说QVariant::constData太晚了,不能使用它,而且它可能无论如何也不适合;所以如果没有缺点,我会坚持类型转换。)

应该将所有信号映射到的目标插槽看起来类似于以下内容:

代码语言:javascript
复制
void handleSignal(QByteArray signalName, QVariantList arguments);

如果解决方案得到C++03的支持,最好是这样,因此,如果不使用它们是一个很大的缺点,我只想使用各种模板。在本例中,C++11是可以的,所以我也对使用C++11的答案感到高兴。

现在,我的可能的解决方案----解决我正在考虑的问题:

我可以使用对象的QMetaObject扫描对象的所有信号,然后为每个信号创建一个QSignalMapper (或传递所有参数的类似内容)。这很简单,这部分我不需要帮助。正如前面提到的,我已经被限制为某些类型的参数,而且我也可以接受对参数计数的限制。

这听起来像一个肮脏的黑客,但我可以使用某种自定义的、基于模板的信号映射器(在本例中是三个参数):

代码语言:javascript
复制
template<class T1, class T2, class T3>
class MySignalMapper : public QObject
{
    Q_OBJECT
public:
    void setSignalName(QByteArray signalName)
    {
        this->signalName = signalName;
    }
signals:
    void mapped(QByteArray signalName, QVariantList arguments);
public slots:
    void map(T1 arg1, T2 arg2, T3 arg3)
    {
        QVariantList args;
        // QVariant myTypeConverter<T>(T) already implemented:
        args << myTypeConverter(arg1);
        args << myTypeConverter(arg2);
        args << myTypeConverter(arg3);
        emit mapped(signalName, args);
    }
private:
    QByteArray signalName;
};

然后,我可以连接一个名为QMetaMethod (已知为信号)的名为obj的QObject (它可能是为所有受支持的类型和参数计数使用某种脚本生成的)。嗯..。它变脏了!):

代码语言:javascript
复制
    // ...
}
else if(type1 == "int" && type2 == "char" && type3 == "bool")
{
    MySignalMapper<int,char,bool> *sm = new MySignalMapper<int,char,bool>(this);
    QByteArray signalName = method.signature();
    signalName = signalName.left(signalName.indexOf('(')); // remove parameters
    sm->setMember(signalName);

    // prepend "2", like Qt's SIGNAL() macro does:
    QByteArray signalName = QByteArray("2") + method.signature();

    // connect the mapper:
    connect(obj, signalName.constData(),
            sm, SLOT(map(int,char,bool)));
    connect(sm, SIGNAL(mapped(int,char,bool)),
            this, SLOT(handleSignal(const char*,QVariantList)));
}
else if(type1 == ...)
{
    // ...

因为这可能有效,这确实是一个肮脏的解决方案。我需要大量宏来涵盖最多N参数的所有类型组合(其中N大约是3到5,还不知道),或者需要一个简单的脚本来生成所有情况下的代码。问题是,这将是很多情况,因为我支持每个参数大约70种不同类型(10个原始类型+嵌套列表和映射,每种类型的深度为2)。因此,对于N的参数计数限制,有N ^ 70种情况要覆盖!

,对于这个目标是否有完全不同的方法,我忽略了吗?

更新:

我自己解决了这个问题(见答案)。如果您对完整的源代码感兴趣,请参阅我在RPC系统bitbucket上的存储库,我刚刚发布了该系统: bitbucket.org/leemes/qtsimplerpc

EN

回答 2

Stack Overflow用户

发布于 2012-05-29 19:22:12

您可以对每个参数进行通用分派,关于时隙/信号,它们只是字符串,因此伪造它们没有问题。这是关于创建一个模板函数,它将每个参数传递到分派中,并合并所有结果。如果使用c++11,甚至可以有无限数量的参数。

票数 1
EN

Stack Overflow用户

发布于 2016-05-03 09:29:00

出于同样的原因,我正在寻找一个通用信号处理程序,即通过RPC转发信号调用。有一个非常有趣和详细的描述QObject-QMetaObject魔术在一个QtDevDays表示。特别是,他们还描述了检查通用信号以进行调试或与脚本语言进行接口的愿望--因此,这是一个完美的读取。

长话短说:您的解决方案是在moc代码中修改qt_static_metacall。(现在在Qt5?)可以通过子类化基于QObject的类和重写qt_metacall来实现相同的目标,例如:

代码语言:javascript
复制
class QRpcService : public QRpcServiceBase
{
public:
    explicit QRpcService(QTcpServer* server, QObject *parent = 0);
    virtual ~QRpcService();

    virtual int qt_metacall(QMetaObject::Call, int, void**);
private:
    static int s_id_handleRegisteredObjectSignal;
};

魔术捕获全槽只是在基类(这里是void handleRegisteredObjectSignal())中定义的一个虚拟方法,它不需要任何东西,也什么也不做。我在构造函数中查询它的元方法id,并将其存储为static int,以避免每次搜索它。

在这个自定义元处理程序中,您拦截对神奇捕获所有插槽的调用,并检查发送方对象和信号。这提供了将void**参数转换为QVariant列表所需的所有类型信息。

代码语言:javascript
复制
int QRpcService::qt_metacall(QMetaObject::Call c, int id, void **a)
{
    // only handle calls to handleRegisteredObjectSignal
    // let parent qt_metacall do the rest
    if (id != QRpcService::s_id_handleRegisteredObjectSignal)
        return QRpcServiceBase::qt_metacall(c, id, a);

    // inspect sender and signal
    QObject* o = sender();
    QMetaMethod signal = o->metaObject()->method(senderSignalIndex());
    QString signal_name(signal.name());

    // convert signal args to QVariantList
    QVariantList args;
    for (int i = 0; i < signal.parameterCount(); ++i)
        args << QVariant(signal.parameterType(i), a[i+1]);

    // ...
    // do whatever you want with the signal name and arguments
    // (inspect, send via RPC, push to scripting environment, etc.)
    // ...

    return -1;
}

我刚刚处理了该方法中的所有内容,但您也可以重新发出收集到的其他信号中的所有信息,并在运行时附加到该信号中。

如果有人感兴趣,我还用我的解决方案这里建立了一个存储库。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10805174

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档