65.9K
CodeProject 正在变化。 阅读更多。
Home

再见,BEGIN_MSG_MAP!

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.46/5 (17投票s)

2005年5月28日

2分钟阅读

viewsIcon

72388

downloadIcon

384

使用 Boost.MPL 库来替代 BEGIN_MSG_MAP 宏。

前言

四年前,我编写了一个程序。 除了 Win32 薄封装器之外,WTL 原本拥有的东西都没用。 由于 CUpdateUI 是其中之一,我通过宏进行了替换。 后来,我读了《C++ 模板元编程》这本书,并受到了示例代码(有限状态机)的启发。 这可以删除宏,结果被命名为 ketchup,旨在取代 ATL/WTL 的 BEGIN_MSG_MAP

随着时间的推移,制作 biscuit 的经验让我能够避免编译时崩溃。 现在,ketchup 是 BEGIN_MSG_MAP 的类型安全同义词,可以帮助 WTL 赶上现代编程的步伐。

引言

Ketchup 是一个使用类模板实现的message map生成器。 这些模板允许我们编写类型安全的 BEGIN_MSG_MAP

一个简单的 BEGIN_MSG_MAP 宏片段

BEGIN_MSG_MAP(CMainFrame)
  MESSAGE_HANDLER(WM_CREATE, OnCreate)
  COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
  COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
  COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
  CHAIN_MSG_MAP(WTL::CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()

使用 Ketchup 的功能可以近似实现,如本代码片段所示

begin_msg_map
<
  message_handler<WM_CREATE, &_::OnCreate>,
  command_id_handler<ID_APP_EXIT, &_::OnFileExit>,
  command_id_handler<ID_FILE_NEW, &_::OnFileNew>,
  command_id_handler<ID_APP_ABOUT, &_::OnAppAbout>,
  chain_msg_map< WTL::CFrameWindowImpl<CMainFrame> >
>
end_msg_map;

要求

Microsoft Visual C++ .NET 版本 7.1, WTL 7.5Boost C++ 库 (无需构建)。 我在带有 Visual C++ 工具包 2003 的 Visual C++ .NET 标准版中测试了演示。 Ketchup 本身是一个仅标头模板库。

快速入门

  1. 包含一个标头并从 ketchup::message_processor<CMainFrame> 派生
    #include "ketchup/ketchup.hpp"
    
    class CMainFrame : 
      public WTL::CFrameWindowImpl<CMainFrame>,
      public WTL::CUpdateUI<CMainFrame>,
      public WTL::CMessageFilter, public WTL::CIdleHandler,
      public ketchup::message_processor<CMainFrame>
    {
  2. 定义消息处理程序
    private:
      LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                     LPARAM /*lParam*/, BOOL& /*bHandled*/)
      {
        // create command bar window
        HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, 
                        NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
        // attach menu
        m_CmdBar.AttachMenu(GetMenu());
        // ...
      }
      // ...
  3. CMainFrame 成为一个 message map
    public:
      begin_msg_map
      <
        message_handler<WM_CREATE, &_::OnCreate>,
        command_id_handler<ID_APP_EXIT, &_::OnFileExit>,
        command_id_handler<ID_FILE_NEW, &_::OnFileNew>,
        command_id_handler<ID_VIEW_TOOLBAR, &_::OnViewToolBar>,
        command_id_handler<ID_VIEW_STATUS_BAR, &_::OnViewStatusBar>,
        command_id_handler<ID_APP_ABOUT, &_::OnAppAbout>,
        chain_msg_map< WTL::CUpdateUI<CMainFrame> >,
        chain_msg_map< WTL::CFrameWindowImpl<CMainFrame> >
      >
      end_msg_map;
  4. 最后,像 BEGIN_MSG_MAP 偷偷做的那样,重写 CMessageMap::ProcessWindowMessage,使用 ketchup::message_processor<CMainFrame> 提供的 process_window_message
      virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg,
        WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0)
      {
        return process_window_message(hWnd, uMsg, wParam, 
                            lParam, lResult, dwMsgMapID);
      }
    };

请记住,消息处理程序的声明应放置在条目之前,并且 C++ 标准不允许您缩写成员函数指针的语法。

关注点

最后一点是性能。程序大小似乎不是问题。 VC++ 7.1 生成的程序大小与原始程序相同,因为 Ketchup 的 message map 几乎与 BEGIN_MSG_MAP 相同。 但 VC++ 7.1 无法内联消息处理程序,这与 BEGIN_MSG_MAP 不同。 这会不会是速度问题呢?

嗯,我无意模仿 BEGIN_MSG_MAP 的外观。 这不仅仅是语法糖,而是一种巧合,是由于与 ATL/WTL 命名一致性导致的。 这对我来说是一个惊人的发现。

顺便说一句,Ketchup 必须是第一个使用 Boost.Xpressive 进行实现的应用程序。

参考文献

历史

  • 2005 年 5 月 23 日 - 版本 0.910 (初始发布)。
  • 2005 年 5 月 27 日 - 版本 0.940。
  • 2005 年 5 月 30 日 - 版本 0.950 (添加了 class_tracedebugging_entry)。
  • 2005 年 6 月 12 日 - 版本 0.951 (移除了 debugging_entry;添加了 empty_entrydebug_entry)。
  • 2005 年 7 月 20 日 - 版本 0.9992。
© . All rights reserved.