0

I am trying to design a Qt library which gives the output back to the client code using signals, but I can't quite get my head around it, I think something is wrong.

Say the library exposes a single class A as follows:

class A {
public:
    void request(int data);
signals:
    void response(int res);
}

So the client code instantiates an A, connects its signal to a slot, and calls request(). I initially chose to use a signal to return the output because A takes some time to elaborate the response, so I want that call to be non-blocking.

My problem is: what if I need to call request() in many different places in my code, and do different things after I receive my response? I think the question is fundamentally on the correct use of signal/slot design of Qt.

To give a concrete example, and hopefully explain myself further, I temporarily solved the issue setting a boolean before the request() to "remind" me what path of execution to take later:

void doingThis() {
    doingThis = true;
    request(data);
}

...

void doingThat() {
    doingThis = false;
    request(data);
}

...

public mySlot(int res) {
    if (dointThis) {
        ...
    } else {
        ...
    }
}

This is hideous. What am I doing wrong?

user445082
  • 59
  • 1
  • 3
  • 1
    Maybe the conclusion should be that the signal/slots construct is not suitable. Alternatively, you could add an identifier in your request that is broadcast with the response, so that the slot can determine if it should handle the response, but it still feels a bit icky. – Ludo Oct 11 '18 at 09:17
  • Can the response tell what it is responding to, then you can just check in your slot what the response actually is? Now it seems like you could get a response for `doingThis`, but you can make a request for `doingThat` before that response, in which case you would handle the response incorrectly. – thuga Oct 11 '18 at 09:18
  • @Ludo Indeed. I thought about that, I don't like it because in principle the library shouldn't know anything about the client code... – user445082 Oct 11 '18 at 09:27
  • I agree with Ludo. If you pass some random number (identifier) into the `request`, then `A` can emit that same random number back with the `response` signal. Even if you have a bunch of slots connected to that signal, you would make them only handle the signal if the identifier was familiar to them. – Stewart Oct 11 '18 at 09:55
  • 1
    As you're using `Qt` you should note that the functionality you are trying to achieve is very similar to that of [`QNetworkAccessManager`](http://doc.qt.io/qt-5/qnetworkaccessmanager.html). You should probably take a look at its interface and those of its associated classes [`QNetworkRequest`](http://doc.qt.io/qt-5/qnetworkrequest.html) and [`QNetworkReply`](http://doc.qt.io/qt-5/qnetworkreply.html). – G.M. Oct 11 '18 at 11:36
  • What about creating multiple instances of class A? So, every request can be connected to the response slot of a different A object? – m7913d Oct 11 '18 at 14:01

1 Answers1

0

I agree with Ludo who commented on your question.

If you pass some random number (identifier) into the request, then A can emit that same random number back with the response signal. Even if you have a bunch of slots connected to that signal, you would make them only handle the signal if the identifier was familiar to them.

class A {
public:
    void request(int data, int id);
signals:
    void response(int res, int id);
}

void doingThis() {
    request(data, 0xaaaa);
}

...

void doingThat() {
    request(data, 0xbbbb);
}

...

public mySlotA(int res, int id) {
    if (id == 0xaaaa) {
        ...
    } 
}

public mySlotB(int res, int id) {
    if (id == 0xbbbb) {
        ...
    }
}

In the case above, the id is hard-coded to represent where the call came from. However, you could also randomly generate the ID. If you did that, then you'd need to save the randomly generated ID. The advantage is that you could send several different requests from doingThis() and be able to understand which response belongs to each request when they arrive back in your slot.

Stewart
  • 4,356
  • 2
  • 27
  • 59
  • This is a step forward, at least the referential transparency of the slot is saved. But still, as harmless as it is, it seems to me to be a need of the client code creeping into the library design. – user445082 Oct 11 '18 at 10:15
  • I don't think it's client code creeping into the library. It's good design of your library. If your library can accept more than one asynchronous request at a time from one or more sources it needs a way for the user to identify the response. One way would be to assume that requests from the same source are synchronous, then just send responses back to that source and the source will never be confused. But if you can't make that assumption, the library needs to provide some means of identifying the response. – Stewart Oct 11 '18 at 10:27
  • It's a fairly common practice in asynchronous network protocol. I've implemented quite a few interfaces where I've used APIs that work like that. If you also want to keep it simple, provide a default parameter for the id: `void request(int data, int id = 0);`. That means if there is only one client and will not make parallel requests, then the client doesn't need to specify an id. – Stewart Oct 11 '18 at 10:29