With GUI development it is not unusual to have several threads of execution running at one time. Normally one of those threads is dedicated to handling the user interface, while the remaining are used for auxiliary tasks. Unfortunately this can cause problems if you need to update the GUI from one of those other threads.
Typically SendMessage (part of the win32 api) is to used to notify the GUI thread, which then looks at the message id and acts accordingly. As you add more messages you need to add more message ids. Then there’s the greater problem of passing data between threads and the associated synchronization problems.
Fortunately there is another way using our old friend the command pattern:
/// update text box text from another thread
void MyGuiClass::UpdateTextBox(const std::string& text) {
class GuiCmdUpdateTextBox : public GuiCmdBase {
public:
GuiCmdUpdateTextBox(MyGuiClass& gui, const std::string& text)
: gui_(gui), text_(text) { }
virtual ~GuiCmdUpdateTextBox() { }
// function operator is called by the GUI thread while processing the message
virtual void operator()(void) {
gui_.TextBox.SetText(text_);
}
private:
MyGuiClass& gui_;
const std::string& text_;
};
// send a message to the GUI thread and block until it has been processed
gui_send_command( GuiCmdUpdateTextBox(*this, text) );
}
Using this method we have both the message sender (gui_send_command) and handler (GuiCmdUpdateTextBox::operator()) within the same function. This is a huge win, considering that we’ve also solved the data passing issue as it is neatly bundled up within the functor. You can see exactly what it is doing and what is needed to do it.
As for the magical wrappers that make all this happen:
/// pure virtual base class for GUI commands
struct GuiCmdBase {
virtual ~GuiCmdBase() { }
virtual void operator()(void) = 0;
};
GuiCmdBase is a straight forward pure virtual functor. By using a virtual function we no longer have to manage message ids.
/// send a GUI command across threads
void gui_send_command(GuiCmdBase& cmd) {
// 1) add item to queue
// 2) notify GUI thread that there is an item on the queue
// 3) wait for command to be processed
}
gui_send_command is responsible for the actual sending and as such is library dependent. You have to queue the command to be processed and then notify the GUI thread. While you could just pass the pointer directly to the GUI thread this can be a very bad idea. Especially in win32 land where other applications can send you messages… In those cases I prefer a simple queue that you can retrieve pointers from.
// GUI thread message processing:
case MSG_GUI_CMD:
{
GuiCmdBase* cmd = get_next_guicmd_from_queue();
if (cmd) (*cmd)();
break;
}
Once notified all the GUI thread has to do is call the provided functor.
I’ve successfully used this technique with win32, Qt, and wxWidgets.