<html><head></head><body><div style="font-family: Verdana;font-size: 12.0px;"><div>After playing with this for the day, here's what I got:</div>

<div>// Application code</div>

<div>
<div>    struct FileResource {<br/>
        QString endpoint;<br/>
        QString sql;<br/>
        std::function<void(QNetworkReply*, const QString&)> func;<br/>
    };</div>

<div>    QList<FileResource> fileResources = {<br/>
        {<br/>
            "images",<br/>
            "SELECT filename FROM images WHERE synced is NULL ORDER BY created ASC",<br/>
            [=] (QNetworkReply* reply, QString filename){<br/>
                if (reply->error() == QNetworkReply::NoError) {<br/>
                    update("images",<br/>
                        {{"synced", QDateTime::currentDateTime()}},<br/>
                        {{"filename",filename}});<br/>
                }<br/>
            }<br/>
        }<br/>
    };</div>

<div><br/>
    for (const auto& resource : fileResources) {<br/>
        QSqlQuery rows(resource.sql);<br/>
        while(rows.next()) {<br/>
            remoteCloud->synchronousPostFile(remoteCloud, resource.endpoint,<br/>
                                             {rows.value("filename").toString()},<br/>
                                             resource.func);<br/>
        }<br/>
    }</div>
</div>

<div> 
<div>
<div>// Library code:</div>

<div>
<div>void RemoteCloud::synchronousPostFile(RemoteCloud *remoteCloud, const QString& endpoint,<br/>
                    const QStringList& filenames,<br/>
                    std::function<void (QNetworkReply *, const QString &)> lambda) {<br/>
    QEventLoop loop;</div>

<div>    for (const auto& filename: filenames) {<br/>
        QFile *file = new QFile(filename);<br/>
        if (file->open(QIODevice::ReadOnly)) {<br/>
            m_filePointers[filename] = file;<br/>
            QNetworkReply * reply = remoteCloud->post(endpoint, mimeFromFilename(filename), file);<br/>
            QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);<br/>
            QObject::connect(reply, &QNetworkReply::finished, reply, [=]{<br/>
                lambda(reply, filename);<br/>
                QFile *file = m_filePointers[filename];<br/>
                if (m_filePointers.remove(filename)) {<br/>
                    file->close();<br/>
                    file->deleteLater();<br/>
                }<br/>
                reply->deleteLater();<br/>
            });<br/>
            loop.exec(); // wait for finished<br/>
        } else {<br/>
            qWarning() << Q_FUNC_INFO << "could not open" << filename;<br/>
            delete file;<br/>
        }<br/>
    }<br/>
}</div>

<div> </div>

<div> </div>

<div>Which, because i call the lambda, I can make sure reply always gets deleted. You can call deleteLater() as many times as you want.  </div>
</div>

<div name="quote" style="margin:10px 5px 5px 10px; padding: 10px 0 10px 10px; border-left:2px solid #C3D9E5; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;">
<div style="margin:0 0 10px 0;"><b>Sent:</b> Wednesday, June 02, 2021 at 11:16 AM<br/>
<b>From:</b> "Jérôme Godbout" <godboutj@amotus.ca><br/>
<b>To:</b> "Jason H" <jhihn@gmx.com>, "interestqt-project.org" <interest@qt-project.org><br/>
<b>Subject:</b> Re: [Interest] QNetworkReply lambdas?</div>

<div name="quoted-content"><!--p.MsoNormal, li.MsoNormal, div.MsoNormal {
        margin: 0.0cm;
        font-size: 11.0pt;
        font-family: Calibri , sans-serif;
}
a:link, span.MsoHyperlink {
        color: blue;
        text-decoration: underline;
}
span.EmailStyle19 {
        font-family: Calibri , sans-serif;
        color: windowtext;
}
*.MsoChpDefault {
        font-size: 10.0pt;
}
div.WordSection1 {
        page: WordSection1;
}
-->
<div>
<div class="WordSection1">
<p class="MsoNormal">Hi, might help, this won’t answer you question directly but might give some helper idea to ease the pain. What we did is a wrapper around QNetworkAccessManager class call RequestManager that own a QNetworkManager. It expose the following methods:</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">QNetworkReply* postRequest(QNetworkRequest& request, const QByteArray& payload);</p>

<p class="MsoNormal">// Do the same for put, update, get…</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">And signal slots:</p>

<p class="MsoNormal">signals:</p>

<p class="MsoNormal">    void replySuccess(QNetworkReply* reply);</p>

<p class="MsoNormal">    void replyError(QNetworkReply* reply);</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">public slots:</p>

<p class="MsoNormal">    void handleFinished(QNetworkReply *networkReply);</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">It manage the return code immediate and signal then valide the answer is a success or a failure. You can store the context or object into a map with your request, the reply will have the request into it so you can load back the needed information when you receive the replySuccess or reply Error signals back.  Make some helper to convert you payload to QByteArray so you can use it with json, text, etc…</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">I also made some ScopeAction class so my NetworkAccess manager reply handler can make sure the delete later is not forgetten:</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">connect(&network_manager, &QNetworkAccessManager::finished, this, &RequestManager::handleFinished);</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">void RequestManager::handleFinished(QNetworkReply *networkReply)</p>

<p class="MsoNormal">{</p>

<p class="MsoNormal">    ScopeAction action_delete([&](){ networkReply->deleteLater(); });</p>

<p class="MsoNormal">… // process the reply error or success here and emit either the replySuccess or replyError signal here, this need to be non queued connection to avoid bad object access.</p>

<p class="MsoNormal">}</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">This helped me a lot to clean my network management for upper layer. So I can simply connect to the reply success or replyError and with the request pointer form the reply you can load the associated data and continue form that point easily.</p>

<p class="MsoNormal"> </p>

<p class="MsoNormal">The async/await would be nice, but I’m not sure this would play well with the Qt event loop and the object memory management. They are also super hard to debug the call stack is lost.</p>

<p class="MsoNormal"> </p>

<div>
<div>
<p class="MsoNormal"><b><span style="font-size: 16.0pt;color: rgb(46,48,49);">Jérôme Godbout, B. Ing.</span></b></p>

<p class="MsoNormal"><br/>
<span style="font-family: Arial , sans-serif;color: rgb(47,64,84);">Software / Firmware Team Lead</span><br/>
<b><span style="font-family: Arial , sans-serif;color: rgb(46,48,49);">O:</span></b><span style="font-family: Arial , sans-serif;color: rgb(46,48,49);"> </span><span style="font-family: Arial , sans-serif;color: black;">(418) 682-3636 ext.: 114  </span></p>

<p class="MsoNormal"><b><span style="font-family: Arial , sans-serif;color: rgb(46,48,49);">C:</span></b><span style="font-family: Arial , sans-serif;color: rgb(46,48,49);"> </span><span style="font-family: Arial , sans-serif;color: black;">(581) 777-0050 </span><br/>
<span style="font-family: Arial , sans-serif;"><a href="mailto:godboutj@dimonoff.com" onclick="parent.window.location.href='mailto:godboutj@dimonoff.com'; return false;" target="_blank"><span style="color: rgb(5,99,193);">godboutj@dimonoff.com</span></a></span></p>

<p class="MsoNormal"><a href="https://www.dimonoff.com/" target="_blank"><span style="font-size: 10.0pt;font-family: Arial , sans-serif;color: black;text-decoration: none;"><img alt="signature_182238435" border="0" height="64" id="Picture_x0020_1" src="cid:image001.png@01D757A0.C5966270" style="width: 2.3854in;height: 0.6666in;" width="229"/></span></a></p>

<p class="MsoNormal"><a href="https://www.dimonoff.com/" target="_blank"><b><span style="font-size: 10.0pt;font-family: Arial , sans-serif;color: rgb(47,64,82);">dimonoff.com</span></b></a></p>

<p class="MsoNormal" style="background: white;"><span style="font-size: 10.0pt;font-family: Arial , sans-serif;color: black;border: none windowtext 1.0pt;padding: 0.0cm;">1015 Avenue Wilfrid-Pelletier, </span></p>

<p class="MsoNormal" style="background: white;"><span style="font-size: 10.0pt;font-family: Arial , sans-serif;color: black;border: none windowtext 1.0pt;padding: 0.0cm;">Québec, QC G1W 0C4, 4e étage</span></p>
</div>
</div>

<p class="MsoNormal"><span> </span></p>

<p class="MsoNormal"><span> </span></p>

<div style="border: none;border-top: solid rgb(181,196,223) 1.0pt;padding: 3.0pt 0.0cm 0.0cm 0.0cm;">
<p class="MsoNormal" style="margin-bottom: 12.0pt;"><b><span style="font-size: 12.0pt;color: black;">From: </span></b><span style="font-size: 12.0pt;color: black;">Interest <interest-bounces@qt-project.org> on behalf of Jason H <jhihn@gmx.com><br/>
<b>Date: </b>Wednesday, June 2, 2021 at 10:41 AM<br/>
<b>To: </b>interestqt-project.org <interest@qt-project.org><br/>
<b>Subject: </b>[Interest] QNetworkReply lambdas?</span></p>
</div>

<div>
<p class="MsoNormal">I'm trying to figure out a more flexible way to do QNetworkReply. Ideally this would be through some async/await but I don't think Qt is "there" yet. (C++20 defines async/await, but there is not a library yet, and even Qt6 doesn't target C++20). So the callback lambda handler looks like the best option for now.<br/>
<br/>
I have a QObject derived class, with a post() function. It takes an endpoint, and object (to be converted to JSON) and a lambda. Example:<br/>
remoteCloud->post("/login", credentials, [=](QNetworkReply* reply){<br/>
        if (reply->error() == QNetworkReply::NoError) {<br/>
                update("cartridge",<br/>
                           {{"synced", QDateTime::currentDateTime()}},<br/>
                           {{"cartridgeId", map["cartridgeId"]}});<br/>
        }<br/>
        reply->deleteLater();<br/>
});<br/>
<br/>
With an implementation of:<br/>
template<typename F><br/>
QNetworkReply *RemoteCloud::post(const QString& endpoint, const QVariantMap& item, F &lambda) {<br/>
        QNetworkReply* reply = m_nam.post(QNetworkRequest(endpoint), JSON(item));<br/>
        connect(reply, &QNetworkReply::finished, lambda);<br/>
        return reply;<br/>
}<br/>
<br/>
<br/>
Note that a lot of the Qt examples use a connect statement like this:<br/>
connect(reply, &QNetworkReply::finished, [request, reply](){ ... });<br/>
<br/>
But when I tried to set this up, I either got static assert errors or l-value errors.<br/>
<br/>
However I am trying to go one better and have my RemoteCloud::post set up the connect with the lambda. Is this possible?<br/>
<br/>
_______________________________________________<br/>
Interest mailing list<br/>
Interest@qt-project.org<br/>
<a href="https://lists.qt-project.org/listinfo/interest" target="_blank">https://lists.qt-project.org/listinfo/interest</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div></div></body></html>