最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Is there any way in C++ to create a Qt container storing Qt signals and slots? - Stack Overflow

programmeradmin7浏览0评论

When I worked on the network client for my messenger I found that there are a lot of duplicate code in methods created for different requests to API (getting chats of the user, for example) so I decided to create a container that will contain methods for different signals like the request has failed or smth and just call those depending on the given method name but now i don't know how to create such a structure. For example, that's how it looks now:

void HttpClient::getUserChats(int failCounter) { // getting your chats
    QNetworkAccessManager *networkManager = new QNetworkAccessManager();
    QNetworkRequest request = formHttpRequest("api/chats");
    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") {
            ++failCounter;
            if (failCounter == 3) {
                emit getUserChatsFailed();
            }
            else {
                getUserChats(failCounter);
            }
        }
        delete networkManager;
        return ;
    }
    QNetworkReply *reply = networkManager->get(request);
    QObject::connect(reply, &QNetworkReply::finished, this, [reply, networkManager, &failCounter, this]() {
        if (reply->error() == QNetworkReply::HostNotFoundError || reply->error() == QNetworkReply::ConnectionRefusedError) {
            ++failCounter;
            if (failCounter == 3) {
                emit getUserChatsFailed();
            }
            else {
                getUserChats(failCounter);
            }
        }
        else {
            QByteArray dataAsArray = reply->readAll();
            QJsonDocument dataAsDocument = QJsonDocument::fromJson(dataAsArray);
            QJsonArray data = dataAsDocument.array();
            emit getUserChatsProcessed(data);
        }
        reply->deleteLater();
        networkManager->deleteLater();
    });
}

void HttpClient::findChats(QMap<QString, QString> body, int failCounter) { // Searching for users or groups
    QNetworkAccessManager *networkManager = new QNetworkAccessManager();
    QNetworkRequest request = formHttpRequest("api/find", 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") {
            ++failCounter;
            if (failCounter == 3) {
                emit findChatsFailed();
            }
            else {
                findChats(body, failCounter);
            }
        }
        delete networkManager;
        return ;
    }
    QNetworkReply *reply = networkManager->get(request);
    QObject::connect(reply, &QNetworkReply::finished, this, [body, reply, networkManager, &failCounter, this]() {
        if (reply->error() == QNetworkReply::HostNotFoundError || reply->error() == QNetworkReply::ConnectionRefusedError) {
            ++failCounter;
            if (failCounter == 3) {
                emit findChatsFailed();
            }
            else {
                findChats(body, failCounter);
            }
        }
        else {
            QByteArray dataAsArray = reply->readAll();
            QJsonDocument dataAsDocument = QJsonDocument::fromJson(dataAsArray);
            QJsonArray data = dataAsDocument.array();
            emit findChatsProcessed(data);
        }
        reply->deleteLater();
        networkManager->deleteLater();
    });
}

How it logically should be:

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") {
            ++failCounter;
            if (failCounter == 3) {
                emit requestInfo[method].at(0); // zero element of requestInfo is the request failed signal
            }
            else {
                doGet(body, method, failCounter);
            }
        }
        delete networkManager;
        return ;
    }
    QNetworkReply *reply = networkManager->get(request);
    QObject::connect(reply, &QNetworkReply::finished, this, [body, reply, networkManager, &failCounter, this]() {
        if (reply->error() == QNetworkReply::HostNotFoundError || reply->error() == QNetworkReply::ConnectionRefusedError) {
            ++failCounter;
            if (failCounter == 3) {
                emit requestInfo[method].at(0);
            }
            else {
                findChats(body, failCounter);
            }
        }
        else {
            QByteArray dataAsArray = reply->readAll();
            QJsonDocument dataAsDocument = QJsonDocument::fromJson(dataAsArray);
            QJsonArray data = dataAsDocument.array();
            // pseudocode here!
            const char* processedMethod = requestInfo[method].at(1); // first element of requestInfo is the request processed signal
            processedMethod.withArg(data); // give the data as the arg
            emit processedMethod;
        }
        reply->deleteLater();
        networkManager->deleteLater();
    });
}

I've tried to create a QVector<const char*> but it doesn't work

QVector<const char*> vec(&HttpClient::getUserChats, &HttpClient::getUserChatsProcessed, &HttpClient::getUserChatsFailed);

Method signatures:

signals:
    void getUserChatsFailed();
    void getUserChatsProcessed(QJsonArray data);
private:
    void getUserChats(int failCounter);

These methods will possibly have different signature so I can't use concrete signature as the vector type

When I worked on the network client for my messenger I found that there are a lot of duplicate code in methods created for different requests to API (getting chats of the user, for example) so I decided to create a container that will contain methods for different signals like the request has failed or smth and just call those depending on the given method name but now i don't know how to create such a structure. For example, that's how it looks now:

void HttpClient::getUserChats(int failCounter) { // getting your chats
    QNetworkAccessManager *networkManager = new QNetworkAccessManager();
    QNetworkRequest request = formHttpRequest("api/chats");
    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") {
            ++failCounter;
            if (failCounter == 3) {
                emit getUserChatsFailed();
            }
            else {
                getUserChats(failCounter);
            }
        }
        delete networkManager;
        return ;
    }
    QNetworkReply *reply = networkManager->get(request);
    QObject::connect(reply, &QNetworkReply::finished, this, [reply, networkManager, &failCounter, this]() {
        if (reply->error() == QNetworkReply::HostNotFoundError || reply->error() == QNetworkReply::ConnectionRefusedError) {
            ++failCounter;
            if (failCounter == 3) {
                emit getUserChatsFailed();
            }
            else {
                getUserChats(failCounter);
            }
        }
        else {
            QByteArray dataAsArray = reply->readAll();
            QJsonDocument dataAsDocument = QJsonDocument::fromJson(dataAsArray);
            QJsonArray data = dataAsDocument.array();
            emit getUserChatsProcessed(data);
        }
        reply->deleteLater();
        networkManager->deleteLater();
    });
}

void HttpClient::findChats(QMap<QString, QString> body, int failCounter) { // Searching for users or groups
    QNetworkAccessManager *networkManager = new QNetworkAccessManager();
    QNetworkRequest request = formHttpRequest("api/find", 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") {
            ++failCounter;
            if (failCounter == 3) {
                emit findChatsFailed();
            }
            else {
                findChats(body, failCounter);
            }
        }
        delete networkManager;
        return ;
    }
    QNetworkReply *reply = networkManager->get(request);
    QObject::connect(reply, &QNetworkReply::finished, this, [body, reply, networkManager, &failCounter, this]() {
        if (reply->error() == QNetworkReply::HostNotFoundError || reply->error() == QNetworkReply::ConnectionRefusedError) {
            ++failCounter;
            if (failCounter == 3) {
                emit findChatsFailed();
            }
            else {
                findChats(body, failCounter);
            }
        }
        else {
            QByteArray dataAsArray = reply->readAll();
            QJsonDocument dataAsDocument = QJsonDocument::fromJson(dataAsArray);
            QJsonArray data = dataAsDocument.array();
            emit findChatsProcessed(data);
        }
        reply->deleteLater();
        networkManager->deleteLater();
    });
}

How it logically should be:

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") {
            ++failCounter;
            if (failCounter == 3) {
                emit requestInfo[method].at(0); // zero element of requestInfo is the request failed signal
            }
            else {
                doGet(body, method, failCounter);
            }
        }
        delete networkManager;
        return ;
    }
    QNetworkReply *reply = networkManager->get(request);
    QObject::connect(reply, &QNetworkReply::finished, this, [body, reply, networkManager, &failCounter, this]() {
        if (reply->error() == QNetworkReply::HostNotFoundError || reply->error() == QNetworkReply::ConnectionRefusedError) {
            ++failCounter;
            if (failCounter == 3) {
                emit requestInfo[method].at(0);
            }
            else {
                findChats(body, failCounter);
            }
        }
        else {
            QByteArray dataAsArray = reply->readAll();
            QJsonDocument dataAsDocument = QJsonDocument::fromJson(dataAsArray);
            QJsonArray data = dataAsDocument.array();
            // pseudocode here!
            const char* processedMethod = requestInfo[method].at(1); // first element of requestInfo is the request processed signal
            processedMethod.withArg(data); // give the data as the arg
            emit processedMethod;
        }
        reply->deleteLater();
        networkManager->deleteLater();
    });
}

I've tried to create a QVector<const char*> but it doesn't work

QVector<const char*> vec(&HttpClient::getUserChats, &HttpClient::getUserChatsProcessed, &HttpClient::getUserChatsFailed);

Method signatures:

signals:
    void getUserChatsFailed();
    void getUserChatsProcessed(QJsonArray data);
private:
    void getUserChats(int failCounter);

These methods will possibly have different signature so I can't use concrete signature as the vector type

Share Improve this question edited Mar 20 at 15:37 ikakslozhno asked Mar 20 at 8:59 ikakslozhnoikakslozhno 134 bronze badges 3
  • 1 Which are the type of getUserChats? const char* should be replaced by something like void (HttpClient::*)(/*..*/)/*const*/... – Jarod42 Commented Mar 20 at 9:06
  • 2 IMO first paragraph should contain more information. You should explain why you need this. For now your proposal of solution seems wrong to me, but I'm unable to properly explain it since I do not have enough information what is actual goal. For now it seems like you like to have some kind of signal spy, which can receive any signal and filter them out. Can you show an example of repeating code? – Marek R Commented Mar 20 at 10:01
  • 1 what is the meaning of "it didn't seem to work" ? – 463035818_is_not_an_ai Commented Mar 20 at 11:37
Add a comment  | 

1 Answer 1

Reset to default 4

If you want to store as char-pointer, you need to be using the SIGNAL() and SLOT() macros to obtain names for the signal/slot members.

If you want to store as a function, you'll need some type-erasure to reach a common type that can be used as a template type for a collection. Consider QVector<QMetaMethod> instead.

发布评论

评论列表(0)

  1. 暂无评论