The idea here is that a worker thread creates an object of type "std::function", and then the worker thread passes an event containing this object to the event handler of the main dialog box. Here' s the contents of the header file for the event class:
Code: Select all
#ifndef HPP_WX_EVENT_STD_FUNCTION
#define HPP_WX_EVENT_STD_FUNCTION
#include <functional> // function
#include <wx/event.h> // wxEvent
DECLARE_EVENT_TYPE( g_wxevent_std_function, -1 ) // This line *declares* a global object
class wxEvent_std_function : public wxCommandEvent {
public:
std::function<void(void)> func;
wxEvent_std_function(wxEventType arg_command_type = g_wxevent_std_function, int arg_id = 0)
: wxCommandEvent(arg_command_type, arg_id) { }
/* No need for copy-constructor, the default supplied one will do */
// Required for sending with wxPostEvent()
wxEvent *Clone() const
{
return new wxEvent_std_function(*this);
}
};
typedef void (wxEvtHandler::*wxEventMemberFunctionPointer_std_function)(wxEvent_std_function &);
// This #define simplifies the one below, and makes the syntax less
// ugly if you want to use Connect() instead of an event table.
#define wxEventHandler_std_function(func) \
(wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction) \
wxStaticCastEvent(wxEventMemberFunctionPointer_std_function, &func)
#endif
And here's how a worker thread would use this event to do whatever it wants. Let's say we have two dialogs, the main one is "Dialog_Main", and the child dialog is "Dialog_Waiting". Note that the code intended to be executed by the GUI thread can be kept separate from the source file containing all of the main dialog's event handlers. The following is the source code file for the child dialog:
Code: Select all
#include "dialog_waiting.hpp" // The header file that goes with this source file
#include <thread> // jthread
#include <stop_token> // stop_token
#include "dialog_main.hpp" // Dialog_Main
#include "wxevent_std_function.hpp" // wxEvent_std_function
DEFINE_EVENT_TYPE(g_wxevent_std_function);
Dialog_Waiting::Dialog_Waiting(wxWindow *const parent) : Dialog_Waiting__Auto_Base_Class(parent)
{
Dialog_Main *const dm = dynamic_cast<Dialog_Main*>(parent);
if ( nullptr == dm ) // First make sure that our parent is Dialog_Main
{
this->Destroy();
return;
}
dm->Enable(false); // Disable the main dialog before starting work
// On the next line is a lambda containing code
// intended to be executed by the GUI thread
auto callback = [this,dm](void) -> void
{
for ( auto const &e : some_global_lockfree_container )
{
int const index = dm->m_listBoxHardware->FindString(e.first);
if ( wxNOT_FOUND == index ) continue;
dm->m_listBoxHardware->SetString(index, e.first + " " + e.second);
}
dm->Enable(true); // Re-enable the main dialog after work is finished
this->Destroy(); // Destroy the 'waiting' dialog and go back to the main dialog
};
this->worker_thread = std::jthread([this,dm,callback](std::stop_token) -> void
{
// Do some work in here while periodically checking the stop_token
wxEvent_std_function event;
event.func = callback;
dm->GetEventHandler()->AddPendingEvent(event);
});
}
Have I re-invented the wheel here? Is there already something similar inside the wxWidgets C++ library?
Do you think I've gone about this in a good way? To me this seems like an ideal way to write a lambda on-the-fly in a source file belonging to a different dialog box, and the lambda can make the GUI thread do whatever it wants.
Note however, that since the event class contains an 'std::function', it isn't limited to just lambdas -- it can be used with any kind of functor.
Here's a few links to learn about lambdas, functors and std::function:
Lambdas: https://www.learncpp.com/cpp-tutorial/lambda-captures/
Functors: https://www.quantstart.com/articles/Fun ... -C-Part-1/
Using std::function: https://shaharmike.com/cpp/lambdas-and- ... d-function