Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What kind of C++ statement(s) is the wxWidgets event table?

Tags:

c++

I just started learning wxWidgets, and I came across a group of lines of code which looks like this:

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(Minimal_Quit,  MyFrame::OnQuit)
    EVT_MENU(Minimal_About, MyFrame::OnAbout)
wxEND_EVENT_TABLE()

wxWidgets calls it "event tables". I'd like to know what kind of statement this is, because I've gone through a couple of C++ tutorials, and I've not seen anything like this. They look like function calls, but without semicolons. I know it has something to do with MACROS, but I don't really get how this works. Is this kind of statement a thing with MACROS, or it's a general thing in C++ I've not yet encountered?

like image 857
Emmanuel Avatar asked Jan 26 '23 22:01

Emmanuel


2 Answers

  1. You are correct. wxBEGIN_EVENT_TABLE is an example of a "macro":

  2. So what's a "macro"? Here's a reasonable definition:

https://gcc.gnu.org/onlinedocs/cpp/Macros.html

A macro is a fragment of code which has been given a name. Whenever the name is used, it is replaced by the contents of the macro. There are two kinds of macros. They differ mostly in what they look like when they are used. Object-like macros resemble data objects when used, function-like macros resemble function calls.

  1. In this case, wxBEGIN_EVENT_TABLE, in conjunction with wxEND_EVENT_TABLE, "expands" to a list of wx "Event handlers":

https://docs.wxwidgets.org/3.0/group__group__funcmacro__events.html#

    #define wxBEGIN_EVENT_TABLE   (       theClass,
          baseClass 
  )       

Use this macro in a source file to start listing static event handlers for a specific class.

Use wxEND_EVENT_TABLE() to terminate the event-declaration block.

  1. "Macros" were introduced in the very earliest assembly languages. They simply do "text substitution" - modify the source code the actual compiler sees.

    Macros (and the macro preprocessor) were an integral part of the original "C" language, carried forward into C++ (and many other high-level languages).

    You can read more about C/C++ macros here:

    https://www.programiz.com/c-programming/c-preprocessor-macros

'Hope that helps

like image 165
paulsm4 Avatar answered Feb 11 '23 20:02

paulsm4


Event tables are normally some type of a struct which contains an event message identifier along with a pointer to the function that is to handle the event message.

This type of data structure is fairly commonly used among a number of GUI frameworks. For instance the Microsoft MFC framework uses it.

Event tables are actual data structures and are not part of the C++ programming language. You use C++ to define the event tables.

Some of the parts needed to make this approach work are:

  • a table structure to allow for searching for a message identifier to find its associated handler

  • a way for the framework to find the event table so that it can use it to do the message identifier lookup and call the correct function with the correct interface

  • a way to know the first and last entries in the event table

A simple version of a framework table element when hand coded may look like the following. This would be an element that is used to create an array of event table elements, one per event message identifier.

typedef struct {
    int  msgId;   // the identifier for the message type
    void (*handler)(int msgId, void *msgData);   // function pointer to handler
} SimpleEventTable;

An incomplete example of this being used in source code without macros would be something along the lines of the following. In this example MSG_ID_ONE and MSG_ID_TWO are defined with int values while handler1 and handler2 are functions that will handle those messages:

void handler1 (int msgId, void *msgData)
{
    // do things with the data associated with message identifier MSG_ID_ONE
}

void handler2 (int msgId, void *msgData)
{
    // do things with the data associated with message identifier MSG_ID_TWO
}

SimpleEventTable myTable[] = {
    {MSG_ID_ONE, handler1},
    {MSG_ID_TWO, handler2},
    {0, NULL}
};

The framework then uses the event table to determine if the code is handling a particular message identifier or not and if the application is handling the message identifier what function to call to handle it. The framework will provide a default handler which in many cases does nothing other than to indicate that the message has been handled.

Most frameworks, wanting to make it easier to do what amounts to boilerplate source code will provide a set of Preprocessor macros that makes the event table easier to create.

Example macros from MFC

I do not have access to wxWidgets however the Microsoft MFC framework provides something similar to the wxWidgets framework though there is a lot more stuffed into MFC than window management.

An event table using MFC looks like the following which is actually quite similar to the framework you are using. This message map is part of the implementation file for an MFC window class. In this case the application class CFrameworkWndDoc is derived from the MFC class CWindowDocument which is part of the MFC framework.

BEGIN_MESSAGE_MAP(CFrameworkWndDoc, CWindowDocument)
    ON_WM_CHAR()
    ON_WM_TIMER()
    ON_MESSAGE(WU_EVS_DFLT_LOAD, OnDefaultWinLoad)
    ON_MESSAGE(WM_APP_SHOW_HIDE_GROUP, OnShowHideGroupMsgRcvd)
END_MESSAGE_MAP()

Along with the above message map that is in the implementation source file, the .c file, there is also the use of the DECLARE_MESSAGE_MAP() define in the class definition, typically located in a header file, so that there is a linkage between the class and the message map.

The DECLARE_MESSAGE_MAP() macro just puts into the class definition the necessary declarations that ties the actual message map in the implementation file to the class.

#define DECLARE_MESSAGE_MAP() \
protected: \
    static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
    virtual const AFX_MSGMAP* GetMessageMap() const; \

The MFC include files have definitions like the following. First of all the two definitions for the beginning and ending of the event table or what MFC calls the message map. As you can see this creates a new member to the MFC class which is called by the MFC framework in order to get access to the message map when the MFC framework is processing a message. The DECLARE_MESSAGE_MAP() macro in the class definition declares the functions which are generated by the following macros.

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
    PTM_WARNING_DISABLE \
    const AFX_MSGMAP* theClass::GetMessageMap() const \
        { return GetThisMessageMap(); } \
    const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
    { \
        typedef theClass ThisClass;                        \
        typedef baseClass TheBaseClass;                    \
        static const AFX_MSGMAP_ENTRY _messageEntries[] =  \
        {

#define END_MESSAGE_MAP() \
        {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
    }; \
        static const AFX_MSGMAP messageMap = \
        { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
        return &messageMap; \
    }                                 \
    PTM_WARNING_RESTORE

Then there are a number of macros that are used to simplify the building of the event table. These used by MFC are much more complicated than the simple example above as these are designed to be used with the MFC windows classes and to be inserted into a source code file and managed by the Visual Studio development environment.

Also notice that the MFC message map technology uses a set of special identifiers, the AfxSig_vwww and AfxSig_lwl, which tells the MFC framework what the interface to the handler function is supposed to be, (UINT, UINT, UINT) and (WPARAM, LPARAM) respectively.

#define ON_WM_CHAR() \
    { WM_CHAR, 0, 0, 0, AfxSig_vwww, \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, UINT, UINT) > ( &ThisClass :: OnChar)) },

#define ON_MESSAGE(message, memberFxn) \
    { message, 0, 0, 0, AfxSig_lwl, \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
        (memberFxn)) },
like image 26
Richard Chambers Avatar answered Feb 11 '23 18:02

Richard Chambers