首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于函数指针的QMetaObject invokeMethod语法

基于函数指针的QMetaObject invokeMethod语法
EN

Stack Overflow用户
提问于 2020-07-15 11:38:35
回答 2查看 656关注 0票数 0

我正在为我们正在使用的IPC库编写一个简单的包装器。

我想将这个库中的事件转换为对Qt插槽的调用。

现在我有这样的事情:

代码语言:javascript
复制
void Caller::registerCallback(int id, QObject* reciever, const char* member)
{
    _callbackMap[id] = std::make_pair(reciever, QString(member));
}

bool Caller::call(const SomeData data)
{
    auto reciever = _callbackMap.value(data.id);

    return QMetaObject::invokeMethod(reciever.first, reciever.second.toLocal8Bit(), Qt::QueuedConnection, 
        QGenericReturnArgument(),
        Q_ARG(SomeData, data));
}
代码语言:javascript
复制
void Receiver::someCallback(SomeData data)
{
    qDebug() << data.str;
}
代码语言:javascript
复制
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Caller caller;
    Receiver reciever;

    caller.registerCallback(1, &reciever, "someCallback");

    caller.call(SomeData({ "Hi", 1 }));

    return a.exec();
}
代码语言:javascript
复制
struct SomeData {
    QString str;
    int id;
}; Q_DECLARE_METATYPE(SomeData);

这个很好用。但我不喜欢把回调注册为字符串。我更希望使用如下语法进行编译时检查:

代码语言:javascript
复制
caller.registerCallback(1, &reciever, &Reciever::someCallback);

我知道的实现。

我要注册的插槽始终只有一个参数,没有返回值。

我已经找到了请求,可以解决我的问题,但不幸的是,这一直没有实现。另外,这个问题也帮不了我,因为我无法修补我们正在使用的moc。

那么,这真的不可能与所有的元魔法Qt正在使用?

编辑:

我找到了一个解决方案,当来电者不知道接收者(实际上我需要什么)时,这个解决方案也有效:

代码语言:javascript
复制
//Caller.h

class Caller : public QObject
{
    Q_OBJECT

public:
    Caller(QObject *parent = nullptr);
    ~Caller();

    //void registerCallback(int id, QObject* reciever, const char *member);
    template < class R, typename Func >
    void inline registerCallback(int id, R reciever, Func callback)
    {
        using std::placeholders::_1;
        registerCallbackImpl(id, reciever, std::bind(callback, reciever, _1));
    };

    bool call(const SomeData);

private:
    QMap<int, std::pair<QObject *, std::function<void(SomeData)>> > _callbackMap;

    void registerCallbackImpl(int id, QObject* reciever, std::function<void(SomeData)> callback);
};
代码语言:javascript
复制
//Caller.cpp
void Caller::registerCallbackImpl(int id, QObject* reciever, std::function<void(SomeData)> callback)
{
    _callbackMap[id] = std::make_pair(reciever, callback);
}

bool Caller::call(const SomeData data)
{
    auto reciever = _callbackMap.value(data.id).first;
    auto fn = _callbackMap.value(data.id).second;

    QMetaObject::invokeMethod(reciever, [reciever, fn, data]() {
        std::invoke(fn, data);
        fn(data);
        }, Qt::QueuedConnection);

    return true;
}
代码语言:javascript
复制
//main.cpp

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Caller caller;
    Receiver reciever;

    using std::placeholders::_1;

    caller.registerCallback(2, &reciever, &Receiver::someCallback);

    caller.call(SomeData({ "Hi2", 2 }));

    return a.exec();
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-07-15 14:16:14

这个解决方案依赖于std::invoke和lambda。

变式1:直接使用std::invoke而不是QMetaObject::invoke

变式2:在lambda中使用std::invoke,它被传递给QMetaObject::invoke

变式3:在变量2中使用宏而不是std::invoke。

如果使用QMetaObject::invoke,则可以选择连接类型--直接或排队。在变体1中,调用立即被调用,就像在直接连接中一样。

receiver.h

代码语言:javascript
复制
#ifndef RECEIVER_H
#define RECEIVER_H

#include <QObject>
#include <QDebug>

struct SomeData {
    QString str;
    int id;
};
//Q_DECLARE_METATYPE(SomeData);

class Receiver : public QObject
{
    Q_OBJECT
public:
    explicit Receiver(QObject *parent = nullptr) : QObject(parent) {}

    void doSmth(SomeData data) {
        qDebug() << data.str;
    }

signals:

};

#endif // RECEIVER_H

caller.h

代码语言:javascript
复制
#ifndef CALLER_H
#define CALLER_H

#include <QObject>
#include <QMap>
#include <utility>
#include <map>

#include "receiver.h"
#define CALL_MEMBER_FN(object,ptrToMember)  ((object)->*(ptrToMember))
typedef void (Receiver::*callback)(SomeData);

class Caller : public QObject
{
    Q_OBJECT
public:
    explicit Caller(QObject *parent = nullptr) : QObject(parent) { }

    void registerCallback(int id, Receiver* receiver, callback c)
    {
        auto pair = std::make_pair(receiver, c);
        _callbackMap.emplace(id, pair);
    }

    bool call(const SomeData data)
    {
        auto &receiver = _callbackMap.at(data.id);
        return QMetaObject::invokeMethod(receiver.first, [data, receiver] () {
            // method 1
            std::invoke(receiver.second, receiver.first, data);
            // method 2 (better not to use a MACRO)
            CALL_MEMBER_FN(receiver.first, receiver.second)(data);
        }, Qt::QueuedConnection);
    }

    bool call_invoke(const SomeData data)
    {
        auto &receiver = _callbackMap.at(data.id);
        std::invoke(receiver.second, receiver.first, data);
        return true;
    }

signals:

private:
    std::map<int,std::pair<Receiver*,callback>> _callbackMap;
};

#endif // CALLER_H

main.cpp

代码语言:javascript
复制
#include <QCoreApplication>

#include "receiver.h"
#include "caller.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Caller caller;
    Receiver reciever;

    caller.registerCallback(1, &reciever, &Receiver::doSmth);
    caller.registerCallback(2, &reciever, &Receiver::doSmth);
    caller.call(SomeData({ "Hi", 1 }));
    caller.call_invoke(SomeData({ "Hi2", 2 }));

    return a.exec();
}
票数 0
EN

Stack Overflow用户

发布于 2020-07-15 14:05:09

另一种方法可能是使用合适的std::function来捕获回调,然后使用具有零超时的QTimer::singleShot在正确的上下文中调用回调。

代码语言:javascript
复制
struct SomeData {
    QString str;
    int     id;
};

class Caller {
public:
    using task = std::function<void(SomeData)>;

    void registerCallback (int id, QObject *receiver, task t)
    {
        _callbackMap[id] = std::make_pair(receiver, t);
    }
    bool call (SomeData data)
    {
        auto receiver = _callbackMap.value(data.id);
        QTimer::singleShot(0, receiver.first, [=](){ receiver.second(data); });
        return true;
    }
private:
    QMap<int, std::pair<QObject *, task>> _callbackMap;
};

class Receiver: public QObject {
public:
    void someCallback (SomeData data)
    {
        qDebug() << data.str;
    }
};

那就用..。

代码语言:javascript
复制
Caller caller;
Receiver receiver;

caller.registerCallback(1, &receiver, [&](SomeData d){ receiver.someCallback(d); });

caller.call(SomeData({ "Hi", 1 }));
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62913989

复制
相关文章

相似问题

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