0

I have the following pseudocode as part of a C++ project:

class BasicSender
{
public:
     virtual void confirmation(int id) = 0;
     int sendMessage(string x)
     {
          Message x = createMessageFromString(x);
          if(! canSendMessage(m))
          {
               MessageStore::storeMessageForLater(this, m); // just store it 
          }
          // *** HERE ***
          return m.messageId;
     }
};

void threadFunction()
{
      while(1)
      {
            while(MessageStore::hasMessagesToBeSent())
            {
                  StoredMessage m = MessageStore.getNextUnsentMessage();
                  if(m.basicSender.sendStoredMessage(m.message))
                  {
                        m.confirmation(m.message.messageId);
                  }
            }
      }
}

and the usage of it:

class ConcreteSender : public BasicSender
{
    virtual void confirmation(int id)
    {
        cout << "Yippe " << id  << " is sent";
    }
};


int main() {
    ConcreteSender a;
    int ID = a.sendMessage("test");
    ... other stuff
}

And what I try to achieve:

is it even remotely possible to call the confirmation for succesfully delivered messages method right after sendMessage finished (ie: it returned to the caller and the caller (in main) has the ID value). If I put the method call at the place *** HERE *** the virtual method will get called before sendMessage finished and the user will be confused that the received a confirmation for a message ID he had no clue about. Yes, I have methods which can check if a message was sent or not, and yes, I can create a thread which from time to time pulls the MessageStore to see if the message was delivered or not. But I am more specifically interested if it is possible to somehow chain in the two function calls, in case of success: confirmation called right after sendMessage returns. If yes, how? (Please note, I cannot modify the signature of the functions, third party library :( )

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • 1
    You can't change the code of BasicSender right? What about wrapping it in a new class "BasicSenderEx" with a modified sendMessage method which calls your confirmation when some condition is true? Then you can call confirmation right after sendMessage returns. – user743414 Dec 01 '14 at 14:49
  • 2
    _"is it even remotely possible to call the confirmation for succesfully delivered messages method right after sendMessage finished"_ That doesn't seem right. Why are you trying to confirm that a message has been sent ... before it's been sent? Your question is confusing anyway: if you really wanted to call a function "right after `sendMessage` returns" then you'd simply do that by writing that code on the next line, so clearly you're after something else. – Lightness Races in Orbit Dec 01 '14 at 14:51
  • @LightnessRacesinOrbit at the stage when it reached the ***HERE*** the system knows that the message was succesfully delivered to the other side, however if it calls the confirmation method which is in an object created by the caller without the caller knowing the message id it will confuse the other side. That is why i have to call the method via some mechanism after it delivered the id back to the casket. – Ferenc Deak Dec 01 '14 at 17:26
  • @user743414 i can change the method however not the signature. It is a library used by third parties and i don't want to break their code – Ferenc Deak Dec 01 '14 at 17:27
  • You either want to call your function as soon as you have your ID, or as soon as the message has been delivered. Which is it? – Lightness Races in Orbit Dec 01 '14 at 17:41
  • @LightnessRacesinOrbit i want to call the function as soon as the message has been delivered. The thread function does this for messages that are delivered late, and the caller already knows the message ids. For undelivered messages the client still gets the message id and they periodically check if that message was delivered or not. This is the current functionality. Now the client wants to be called via the confirmation method (new functionality) when a message was succesfully delivered. On the other end the client code tracks the ids and i wonder what will happen if they get a confirmatio – Ferenc Deak Dec 01 '14 at 18:57
  • n.... for an id they did not get via the conventional way. ... ie. the return value of sendMessage – Ferenc Deak Dec 01 '14 at 18:59
  • @fritzone: `sendMessage` completing does not indicate that the message has been delivered. It only indicates that the message has been queued. So your requirement seems wrong. _[edit: ARGHGARGHARGHA you deleted this just as I was writing a long answer.]_ – Lightness Races in Orbit Dec 02 '14 at 09:39
  • @LightnessRacesinOrbit yup.. you're right here. The question is a total mess after I have also clarified here internally some things with the client. I'll delete it. – Ferenc Deak Dec 02 '14 at 09:42
  • @LightnessRacesinOrbit I have undeleted it :) Waiting for your answer! – Ferenc Deak Dec 02 '14 at 11:37
  • @fritzone Thanks :P This is the best I can do and I'm trying to read between the lines somewhat but perhaps this is the missing link you're after. – Lightness Races in Orbit Dec 02 '14 at 12:22
  • Yes, exactly something like I was looking for! Thanks! – Ferenc Deak Dec 02 '14 at 12:31

1 Answers1

1

If you want the ID of the new message in the scope of main, but you want it to indicate the ID of a message that has actually been sent, then you can use a promise/future pair. The ID will be available in main as soon as sendMessage returns, but attempting to read its value will block the main thread until the worker thread has finished actually sending that message.

This will involve changing Message to contain a promise, and changing sendMessage to return a future that references that promise.

class Message
{
public:
   /*...*/
   std::promise<int> messageIdOnceSent;

private:
   /* ...*/
   int messageId;
};

class BasicSender
{
public:
     virtual void confirmation(int id) = 0;
     std::future<int> sendMessage(string x)
     {
          Message x = createMessageFromString(x);
          if(! canSendMessage(m))
          {
               MessageStore::storeMessageForLater(this, m); // just store it 
          }

          // !!
          return std::future<int>(x.messageIdOnceSent);
     }
};

void threadFunction()
{
      while(1)
      {
            while(MessageStore::hasMessagesToBeSent())
            {
                  StoredMessage m = MessageStore.getNextUnsentMessage();
                  if(m.basicSender.sendStoredMessage(m.message))
                  {
                        m.confirmation(m.message.messageId);

                        // !!
                        m.message.messageIdOnceSent.set_value(m.message.messageId);
                  }
            }
      }
}

int main() {
    ConcreteSender a;
    std::future<int> ID = a.sendMessage("test");

    // !!
    std::cout << ID.get() << '\n'; // blocks until message has actually been sent
}

This is the closest solution I can get if you really want the ID "immediately after" sendMessage returns but not before the message has actually been sent.

Generally you don't want to block your main thread, but if your main function is more complex (and, presumably, it is) then this may be what you are looking for.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055