PDA

View Full Version : Subclassing QNetworkReply



piotr.dobrogost
7th September 2009, 21:16
I have to deal with two kinds of network requests when using QtWebKit; synchronous and asynchronous. Synchronous requests are those that are sent to download all external resources referenced by some web page. Asynchronous ones are those that are sent after a web page is loaded and are triggered by JavaScript code and/or user's actions. When all synchronous requests are finished and the page is complete QWebPage (or QWebFrame in case of single frame) emits loadFinished() signal. However QtWebKit does not signal when asynchronous request is finished. To know when they are finished I had to subclass QNetworkAccessManager used by QtWebKit.

Now I'm wondering how should I subclass QNetworkReply so that I have two subclasses, one for each kind of request?

Simply declaring

class SynchReply : public QNetworkReply {};
and

class AsynchReply : public QNetworkReply {};
won't work as there's no way of setting its QNetworkReply part after receiving QNetworkReply from QNetworkAccessManager in a call like this
QNetworkReply * reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
from within my subclass of QNetworkAccessManager.

wysota
7th September 2009, 22:55
You have to subclass QNetworkAccessManager and reimplement createRequest(). Then you can return whatever you want.

piotr.dobrogost
8th September 2009, 00:51
You have to subclass QNetworkAccessManager and reimplement createRequest().

I'm already doing this but in a different way.


QNetworkReply *
NetworkAccessManager::createRequest( Operation op, const QNetworkRequest & req, QIODevice * outgoingData)
{
QNetworkReply * reply = QNetworkAccessManager::createRequest(op, req, outgoingData);

if (checkIfAsync(req.url().toString()))
{
QObject::connect(reply, SIGNAL(finished()), this, SLOT(asyncFinishedHandler()));
}

return reply;
}


I would like to rewrite it in such a way I wouldn't have to connect individual replies with my slot anymore. I would like to handle the difference in reply's type in one place, in handler of this signal:

void QNetworkAccessManager::finished ( QNetworkReply * reply ) [signal]


Then you can return whatever you want.

Any customer can have a car painted any colour that he wants so long as it is black. (http://en.wikiquote.org/wiki/Henry_Ford) -- Henry Ford

C++ supports only* covariant return types in overridden virtual functions. (http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science% 29#C.2B.2B) (* - my addition.)

Shortly speaking you can't return whatever you want :)
But the question was how to subclass QNetworkReply?

Having


class MyNetworkReply : public QNetworkReply
{
bool myFlag;
};

how to reuse

QNetworkReply * QNetworkAccessManager::createRequest ( Operation op, const QNetworkRequest & req, QIODevice * outgoingData = 0 ) [virtual protected]
to set QNetworkReply part of MyNetworkReply object?

wysota
8th September 2009, 01:26
Use the decorator or proxy pattern.

piotr.dobrogost
8th September 2009, 11:20
This was the answer that answers my question :)

What would you say to add Visitor to the mix like this


struct ExtendedNetworkReply : public QNetworkReply
{
virtual void doStuff() = 0;
// the rest
};


struct SynchNetworkReply : public ExtendedNetworkReply
{
virtual void doStuff() {};
};


struct AsynchNetworkReply : public ExtendedNetworkReply
{
virtual void doStuff() {};
};

and use doStuff() instead of checking tag? I guess it's more OO style :)

And two more questions;

1. One can avoid all this subclassing using one dynamic property of QNetworkReply (QObject in fact) to tag replies. Which one you think is better and why?

2. If QNetworkReply is meant to be extended by inheritance and used polimorphically why doesn't it have virtual destructor?

piotr.dobrogost
18th December 2010, 19:32
Use the decorator or proxy pattern.

I've found the example of proxy for QNetworkReply here (http://gitorious.org/qtwebkit/performance/blobs/master/host-tools/mirror/main.cpp).

I don't quite understand what's the reason for calling base subobject's methods in the following places:



NetworkReplyProxy::NetworkReplyProxy(QObject* parent, QNetworkReply* reply) {
...
setOperation(m_reply->operation());
setRequest(m_reply->request());
setUrl(m_reply->url());
...
}


// QIODevice proxy...
virtual qint64 bytesAvailable() const {
return m_buffer.size() + QIODevice::bytesAvailable(); // <-- QIODevice::bytesAvailable() ?
}

// not possible...
void setReadBufferSize(qint64 size) {
QNetworkReply::setReadBufferSize(size); // <-- ???
m_reply->setReadBufferSize(size);
}

void errorInternal(QNetworkReply::NetworkError _error)
{
setError(_error, errorString()); // <-- ???
emit error(_error);
}

Shouldn't proxy forward all calls to the original object?

wysota
19th December 2010, 09:42
In the first case bytesAvailable() of QIODevice is called because the latter has its own internal buffer that can contain some of the data. There is a labs article about it somewhere.