【问题标题】:What kind of C++ statement(s) is the wxWidgets event table?wxWidgets 事件表是哪种 C++ 语句?
【发布时间】:2019-05-29 12:22:03
【问题描述】:

刚开始学习wxWidgets,遇到了一组代码如下:

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

wxWidgets 称之为“事件表”。我想知道这是一种什么样的陈述,因为我已经阅读了几个 C++ 教程,但我还没有见过这样的东西。它们看起来像函数调用,但没有分号。我知道它与 MACROS 有关,但我真的不明白它是如何工作的。这种语句是MACROS的东西,还是C++中通用的东西我还没遇到过?

【问题讨论】:

  • 这些是成员函数指针
  • C(和 C++)预处理器宏只是参数化的文本替换。这些代码行是宏调用,但替换的结果必须是真正的 C++ 代码。
  • 它们不是语句,它们是扩展为表声明的宏。
  • 如果您询问wxBEGIN_EVENT_TABLEEVT_MENU,这些很可能是宏。宏在某处有其定义,预处理器(在编译开始之前)会将这些定义复制并粘贴到宏调用的位置。通常不是一个好习惯。

标签: c++


【解决方案1】:

事件表通常是某种类型的 struct,其中包含事件消息标识符以及指向要处理事件消息的函数的指针。

这种类型的数据结构在许多 GUI 框架中相当普遍。例如,Microsoft MFC 框架使用它。

事件表是实际的数据结构,不是 C++ 编程语言的一部分。您使用 C++ 来定义事件表。

使这种方法发挥作用所需的一些部分是:

  • 允许搜索消息标识符以找到其关联处理程序的表结构

  • 一种框架查找事件表的方法,以便它可以使用它进行消息标识符查找并使用正确的接口调用正确的函数

  • 一种了解事件表中第一个和最后一个条目的方法

手动编码后的框架表格元素的简单版本可能如下所示。这将是一个用于创建事件表元素数组的元素,每个事件消息标识符一个。

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

在没有宏的源代码中使用此功能的不完整示例如下所示。在此示例中,MSG_ID_ONE 和 MSG_ID_TWO 使用 int 值定义,而 handler1handler2 是处理这些消息的函数:

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}
};

然后框架使用事件表来确定代码是否正在处理特定的消息标识符,以及应用程序是否正在处理消息标识符,调用什么函数来处理它。该框架将提供一个默认处理程序,在许多情况下,该处理程序除了指示消息已被处理之外什么都不做。

大多数框架都希望更容易地完成相当于样板源代码的工作,它们将提供一组预处理器宏,使事件表更易于创建。

来自 MFC 的示例宏

我无法访问 wxWidgets,但是 Microsoft MFC 框架提供了与 wxWidgets 框架类似的东西,尽管 MFC 中的内容比窗口管理更多。

使用 MFC 的事件表如下所示,实际上与您使用的框架非常相似。此消息映射是 MFC 窗口类的实现文件的一部分。在这种情况下,应用程序类 CFrameworkWndDoc 派生自 MFC 类 CWindowDocument,它是 MFC 框架的一部分。

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()

除了在实现源文件.c文件中的上述消息映射之外,还有在类定义中使用DECLARE_MESSAGE_MAP()定义,通常位于头文件中,因此有一个类和消息映射之间的联系。

DECLARE_MESSAGE_MAP() 宏只是将必要的声明放入类定义中,将实现文件中的实际消息映射与类联系起来。

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

MFC 包含文件具有如下定义。首先是事件表的开始和结束的两个定义,或者 MFC 所说的消息映射。如您所见,这为 MFC 类创建了一个新成员,该成员由 MFC 框架调用,以便在 MFC 框架处理消息时访问消息映射。 class 定义中的DECLARE_MESSAGE_MAP() 宏声明了由以下宏生成的函数。

#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

然后有一些宏用于简化事件表的构建。 MFC 使用的这些比上面的简单示例要复杂得多,因为它们旨在与 MFC 窗口类一起使用并插入到源代码文件中并由 Visual Studio 开发环境管理。

还要注意,MFC 消息映射技术使用一组特殊标识符,AfxSig_vwwwAfxSig_lwl,它们告诉 MFC 框架处理函数的接口应该是什么,(UINT, UINT, UINT) 和 @987654339 @分别。

#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)) },

【讨论】:

    【解决方案2】:
    1. 你是对的。 wxBEGIN_EVENT_TABLE 是“宏”的一个例子:

    2. 那么什么是“宏”?这是一个合理的定义:

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

    宏是已命名的代码片段。每当 名称被使用,它被宏的内容替换。那里 是两种宏。它们的主要区别在于它们的外观 当它们被使用时。类对象宏在以下情况下类似于数据对象 使用的类似函数的宏类似于函数调用。

    1. 在这种情况下,wxBEGIN_EVENT_TABLEwxEND_EVENT_TABLE 一起“扩展”为 wx“事件处理程序”列表:

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

        #define wxBEGIN_EVENT_TABLE   (       theClass,
              baseClass 
      )       
    

    在源文件中使用此宏开始列出静态事件处理程序 对于特定的类。

    使用 wxEND_EVENT_TABLE() 终止事件声明块。

    1. “宏”是在最早的汇编语言中引入的。他们只是做“文本替换”——修改实际编译器看到的源代码。

      宏(和宏预处理器)是原始“C”语言不可分割的一部分,并被继承到 C++(和许多其他高级语言)中。

      您可以在此处阅读有关 C/C++ 宏的更多信息:

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

    '希望有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-22
      • 2014-08-30
      • 2012-12-29
      • 1970-01-01
      相关资源
      最近更新 更多