Title: GOOD BYE BEGIN_MSG_MAP! Author: MB Email: mb2act@yahoo.co.jp Environment: Microsoft Visual C++ .NET Version 7.1, Windows XP Keywords: ATL, WTL, MPL, boost, C++, metaprogramming Level: Intermediate Description: A replacement for BEGIN_MSG_MAP macros, using the Boost.MPL library Section WTL, C++ SubSection Doc/View
Four yeas ago, I made a program. Everything WTL originally made was useless, except win32 thin wrappers. CUpdateUI was the one of them, so I made the replacement for it by macros. Later, it was extended to support general window messages with no macro. It was named ketchup, aimed to replace BEGIN_MSG_MAP of ATL/WTL.
I read the book, C++ Template Metaprogamming, and I was inspired by the sample code, the finite state machine. WTL is the "Template" library..., so it's time to tie.
Microsoft Visual C++ .NET Version 7.1, WTL 7.5 and Boost C++ libraries(No build required).
I did test the demo by Visual C++ .NET Standard Edition with Visual C++ Toolkit 2003.
A type which has a member function,
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID);The return value is TRUE if the message is fully handled; otherwise, it is FALSE. This is the concept of ATL.
Any type which is derived from ketchup::message_processor<Derived>
any MessageProcessor from which a Derived is derived
An MPL Sequence of a Entry
A type which has a static member function,
static bool process(Derived& derived, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID, BOOL& bHandled);The return value is true if the message is fully handled; otherwise, it is false.
A static constant of a window message id or command message id
A member function of a Derived
A model of a Entry and the type of func is compatible with ATL.
A model of a Entry, created from MessageMap
A model of a Entry, created from ChainClass
Your job is to create a type which conforms an MessageProcessor, using ketchup::message_processor. The minimum codes require four steps.
Include headers and derive from ketchup::message_processor.
#include <boost/mpl/vector.hpp> #include "ketchup/ketchup.hpp" class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CMessageFilter, public CIdleHandler, public ketchup::message_processor<CMainFrame> {CMainFrame conforms to a Derived.
Define message handlers as you did.
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { CreateSimpleToolBar(); CreateSimpleStatusBar(); // ... }
Define a MessageMap
struct message_map : boost::mpl::vector< 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< CFrameWindowImpl<CMainFrame> > > { };message_map conforms to a MessageMap. m_hWndToolBar of CMainFrame can be a commandbar which is not a toolbar, take care.
Finally, override CMessageMap::ProcessWindowMessage as BEGIN_MSG_MAP did, by process_window_message provided by ketchup::message_processor.
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) { return process_window_message<message_map>(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); }CMainFrame now conforms to a MessageProcessor. Bear in mind that declarations of message handlers should be placed before message_map, and the message_map should be placed before ProcessWindowMessage. message_map is not a macro but a type.
ketchup also supports Updating Command UI mechanism of MFC, and the limited automatic-disable. You will find the following code from the demo.
virtual BOOL OnIdle() { ketchup::update_toolbar_cmd_ui(m_hWnd, m_wndToolBar); return FALSE; } // ... struct message_map : boost::mpl::vector< menu_cmd_ui_generator, cmd_ui_handler_auto_enable< boost::mpl::vector_c<UINT, ID_BLACK, ID_RED, ID_GREEN, ID_BLUE, ID_WHITE, ID_CUSTOM, ID_SPEED_SLOW, ID_SPEED_FAST >, chain_mdi_child_cmd_ui >, cmd_ui_handler<ID_VIEW_TOOLBAR, &CMDIFrame::OnUpdateViewToolBar>, cmd_ui_handler<ID_VIEW_STATUS_BAR, &CMDIFrame::OnUpdateViewStatusBar>, // ... > { };
If cracked handlers not supported, nobody would call ketchup type-safe. You can find the followings from the demo.
struct message_map : boost::mpl::vector< chain_msg<cmd_ui_map>, msg_wm_paint<&CHelloView::OnPaint>, // cracked! command_range_handler<ID_BLACK, ID_WHITE, &CHelloView::OnColor>, command_id_handler<ID_CUSTOM, &CHelloView::OnCustomColor> > { };
The compiler generates a big if-statement from the MessageMap. ketchup has no runtime-maps, but fatal compile-time errors may occur about the heap memory. I guess it is not ketchup's issue and if you encounter the problem...,
Never generate boost::mpl::vector using
BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS macro.
The default boost::mpl::vector can have up-to 20 elements.
If you want a big boost::mpl::vector, you can chain MessageMaps with chain_msg.
Remove the debug-time compiler options /Z7,/Zi and /ZI.
The demo is the one that I modified the MDIDocVw(a WTL sample) with the ketchup-style...which wins?
Well, modified codes are marked with <mdf>, compare. (MDIDocVw has a bug, the confusion between m_hWndToolbar and commandbar, but I didn't fix.)
The last interest is the performance. The message handlers seem not to be inlined, unlike ATL.
23 May, 2005 - version 0.910
26 May, 2005 - version 0.922