c++qt

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


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


Solution

  • 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.