如何在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
相关产品推荐
相关产品推荐

