Advice sought on signal handling design
I have a class that deals with QNetworkReply instances and emits different signals of the same signature template void signalEmit(instance*):
Code:
class signalHandler
{
signals:
void onRequestComplete (classA* request);
void onRequestComplete (classB* request);
void onRequestComplete (classC* request);
…
}
Where classA, classB, classC etc are all descendants of classRoot and QObject
Selection of the appropritate signal is done like this:
Code:
classRoot* r= reinterpret_cast< classRoot*>(reply->request().originatingObject());
if (dynamic_cast<classA*>(r) != 0)
{
classA * r= reinterpret_cast< classA*>(reply->request().originatingObject());
emit requestIsCompleted (r);
}
else if (dynamic_cast<classB*>(r) != 0)
{
classB * r= reinterpret_cast< classB*>(reply->request().originatingObject());
emit requestIsCompleted (r);
}
…
There are numerous problems in this design. First of all, the giant switch code. Next, types of classA, classB, classC are growing as I am adding more and more network request/response functionality. Usage of templates with QObject-descendant classes are forbidden.
I’d like to be able to “register†classA, classB types with signalHandler and let it deal with routing and emitting appropriate signal without modifying signalHandler for each new classX type.
Any advise will be greatly appreciated.
Re: Advice sought on signal handling design
Have a look at QSignalMapper.
Re: Advice sought on signal handling design
Thanks, high_flyer.
I am evaluating QSignalMapper for the usage in my scenario. I'll post results here.
Re: Advice sought on signal handling design
You could also use double dispatch.
Re: Advice sought on signal handling design
My original question was raised after reviewing my own implementation of webservice requests and handling responses.
Following is a pseudo-code for my current implementation
Code:
class classRoot
: public QObject {};
class classA :public classRoot{};
class classB :public classRoot{};
class classC :public classRoot{};
class requestTransport
: public QObject{
signals:
void onRequestComplete (classA* request);
void onRequestComplete (classB* request);
void onRequestComplete (classC* request);
}
class responseProcessor
: public QObject{
public slots:
void requestCompleted (classA* request);
void requestCompleted (classB* request);
void requestCompleted (classC* request);
};
void main()
{
requestTransport st;
responseProcessor rp;
connect(&st, SIGNAL(onRequestComplete(classA*)), &rp, SLOT(requestCompleted(classA*)));
...
connect(&st, SIGNAL(onRequestComplete(classC*)), &rp, SLOT(requestCompleted(classC*)));
}
The advantage of this design is that there is explicit contract between requestTransport and responseProcessor for each type of request.
The major problem with the design is that it does not scale. Each new request type requires change in requestTransport, responseProcessor and main
in respect to the signal/slot connectivity
I am trying to find out if I can have something like the pseudo-code below. QSignalMapper, as far as I can see, cannot be used for this purpose
Code:
class requestTransport
{
public:
template<typename T> registerRequestType();
template<typename T> addRequest(T* request);
private signals:
void onRequestComplete(classRoot* request);
}
void main()
{
requestTransport st;
responseProcessor rp;
// following three lines register request types with signal Transport. Each call performs QObject::connect or some other
// metaobject call to wire onRequestComplete signal to the appropriate requestCompleted slot in responseProcessor instance
st.registerRequestType<classA> (&rp);
st.registerRequestType<classB> (&rp);
st.registerRequestType<classC> (&rp);
// this line adds new instance of the request to be sent via Transport. Upon completion the appropriate slot in the responseProcessor
// should be called.
st.addRequest<classB>(new classB());
}
The main advantage for me is that for every new type of request I have to only add new slot handler and register request type with the transport
It also adds clarity to the code, making visible the types of requests the transport is processing.
I don't care if it is templates or other technique, I want to know if it is possible to implement it Qt-way.
Any advice/discussion/solution will be greatly appreciated.
Re: Advice sought on signal handling design
As I already said, I'd probably use double-dispatch here. Or maybe some QMetaObject magic.
Re: Advice sought on signal handling design
Wysota, how would you handle the runtime types of double-dispatch ? Use the methods of QObject ?
Re: Advice sought on signal handling design
The original example used dynamic_cast. If you see a dynamic cast, that's usually a candidate for double-dispatch. The "signalHandler" class already looks like it was prepared for double-dispatch, only "the other way round" - the class should have slots and not signals. Then you can use something similar to the visitor pattern to perform double-dispatch.
Re: Advice sought on signal handling design
Solution is found.
It is indeed, as Wysota puts it - "a QMetaObject magic". The issue was discussed and published here, it also includes working example.