You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中创建存储Qt信号的容器以消除API请求重复代码?

Qt网络请求中统一管理不同签名的信号方案

问题场景

开发信使应用网络客户端时,多个API请求方法(如getUserChats、findChats)存在大量重复代码,希望通过通用方法doGet统一处理请求流程,并根据请求路径调用对应的失败、处理完成信号。但由于信号签名不同(无参的Failed信号和带QJsonArray参数的Processed信号),无法用固定类型容器存储,需要解决信号的动态调用问题。

解决方案:用结构体封装信号发射逻辑

通过定义结构体存储对应请求的信号发射行为,结合lambda表达式绑定具体信号,实现类型安全的动态调用。

步骤1:定义信号封装结构体

在HttpClient类中定义结构体,封装失败和处理完成信号的发射函数:

#include <functional>
#include <QJsonArray>

struct RequestSignals {
    std::function<void()> emitFailed;
    std::function<void(QJsonArray)> emitProcessed;
};

步骤2:初始化请求-信号映射

在HttpClient的构造函数中,为每个请求路径绑定对应的信号发射逻辑:

#include <QMap>

class HttpClient : public QObject {
    Q_OBJECT
public:
    explicit HttpClient(QObject *parent = nullptr);
    void doGet(QMap<QString, QString> body, QString method, int failCounter = 0);

signals:
    void unauthorized();
    void getUserChatsFailed();
    void getUserChatsProcessed(QJsonArray data);
    void findChatsFailed();
    void findChatsProcessed(QJsonArray data);

private:
    QNetworkRequest formHttpRequest(const QString& path, const QMap<QString, QString>& body = {});
    void setAuthorizationHeader(QNetworkRequest& request);
    QMap<QString, RequestSignals> m_requestSignals; // 请求路径到信号的映射
};

// 构造函数中初始化映射
HttpClient::HttpClient(QObject *parent) : QObject(parent) {
    // 绑定"api/chats"对应的信号
    m_requestSignals["api/chats"] = {
        [this]() { emit getUserChatsFailed(); },
        [this](QJsonArray data) { emit getUserChatsProcessed(data); }
    };
    // 绑定"api/find"对应的信号
    m_requestSignals["api/find"] = {
        [this]() { emit findChatsFailed(); },
        [this](QJsonArray data) { emit findChatsProcessed(data); }
    };
}

步骤3:实现通用请求方法doGet

修改通用方法,通过映射表调用对应信号,同时修复原代码中lambda引用栈变量的问题:

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>

void HttpClient::doGet(QMap<QString, QString> body, QString method, int failCounter) {
    QNetworkAccessManager *networkManager = new QNetworkAccessManager();
    QNetworkRequest request = formHttpRequest(method, body);
    try {
        setAuthorizationHeader(request);
    }
    catch (const std::runtime_error& e) {
        QString what = e.what();
        if (what == "Wrong or exp") {
            emit unauthorized();
        }
        else if (what == "Has no internet connection") {
            int newFailCounter = failCounter + 1;
            if (newFailCounter == 3) {
                if (m_requestSignals.contains(method)) {
                    m_requestSignals[method].emitFailed();
                }
            }
            else {
                doGet(body, method, newFailCounter);
            }
        }
        delete networkManager;
        return;
    }
    QNetworkReply *reply = networkManager->get(request);
    QObject::connect(reply, &QNetworkReply::finished, this, [body, reply, networkManager, failCounter, method, this]() {
        if (reply->error() == QNetworkReply::HostNotFoundError || reply->error() == QNetworkReply::ConnectionRefusedError) {
            int newFailCounter = failCounter + 1;
            if (newFailCounter == 3) {
                if (m_requestSignals.contains(method)) {
                    m_requestSignals[method].emitFailed();
                }
            }
            else {
                doGet(body, method, newFailCounter);
            }
        }
        else {
            QByteArray dataAsArray = reply->readAll();
            QJsonDocument dataAsDocument = QJsonDocument::fromJson(dataAsArray);
            QJsonArray data = dataAsDocument.array();
            if (m_requestSignals.contains(method)) {
                m_requestSignals[method].emitProcessed(data);
            }
        }
        reply->deleteLater();
        networkManager->deleteLater();
    });
}

替代方案:基于Qt元对象系统的反射调用

如果需要更动态的实现,可以利用Qt的元对象系统通过信号名称反射调用,但需要确保签名字符串完全匹配:

示例代码

// 获取信号的元方法
QMetaMethod getFailedMethod(const QString& signalName) {
    int index = metaObject()->indexOfSignal(qPrintable(signalName));
    if (index == -1) {
        qWarning() << "Signal not found:" << signalName;
        return QMetaMethod();
    }
    return metaObject()->method(index);
}

// 在doGet中调用
if (failCounter == 3) {
    QMetaMethod failedMethod = getFailedMethod("getUserChatsFailed()");
    if (failedMethod.isValid()) {
        failedMethod.invoke(this);
    }
}
// 处理完成信号
QMetaMethod processedMethod = getFailedMethod("getUserChatsProcessed(QJsonArray)");
if (processedMethod.isValid()) {
    processedMethod.invoke(this, Q_ARG(QJsonArray, data));
}

这种方式无需提前绑定lambda,但依赖字符串匹配,容易因签名拼写错误导致问题,适合动态添加请求的场景。


内容的提问来源于stack exchange,提问作者ikakslozhno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 19:37:20