window message dispatcher
Time to look at the window message dispatcher. This is the third major implementation. so far.
Callers of dispatch
should call DefWindowProc if this returns result.first == false
.
One goal was to eliminate manual maintenance of a message-map from message id to function. Other goals included pay-for-use performance characteristics, code driven instead of data driven design, no virtual function calls and allow source debugging even in the macros (which are unfortunately required until C++ supports 'templated' names).
The current approach inspects the specified target class instance for a function matching each supported windows message. The parameters for the function are determined using the HANDLE_MSG_... macros from windowsx.h in the SDK. So if the target class has a function matching LRESULT OnPaint(const lib::wnd::Context<Tag>& context)
then code to forward the WM_PAINT message to the function is included, otherwise the code is omitted.
template<typename WindowClassTag, typename Target>
std::pair<bool, LRESULT>
dispatch(
Target target,
const Context<WindowClassTag>& context)
{
BOOL handled = FALSE;
LRESULT result = 0;
std::tie(handled, result) =
generator<WindowClassTag, Target>::type(
base<WindowClassTag, Target>(
target, &context)).dispatch();
if (!handled) {
std::tie(handled, result) =
optionalUnhandled(target, context);
}
return std::make_pair(
handled ? true : false, result);
}
dispatch looks simple, it constructs a type stitched together in the generator detail class and calls the dispatch method on the class instance then if that function specifies that the message was not handled it calls optionalUnhandled
. When the guts of the generator are exposed later that facade of simplicity will disintegrate. Until that time enjoy the appearance of simplicity.
struct UnhandledTag {};
The tag is used to increase the flexibility of the window_message_error_contract trait function.
template<typename WindowClassTag, typename Target>
std::pair<bool, LRESULT> optionalUnhandled(
Target target,
const Context<WindowClassTag>& context,
decltype(
cmn::instance_of<Target>::value->OnUnhandled(
cmn::instance_of<Context<WindowClassTag>>::value)))
{
LRESULT result = 0;
window_message_error_contract(
[&] {
result = target->OnUnhandled(context);
},
context,
UnhandledTag(),
WindowClassTag()
);
return std::make_pair(true, result);
}
This optionalUnhandled overload is only included in the overload set if the decltype parameter can resolve the OnUnhandled(context) function on the target.
The window_message_error_contract function that must be provided by the target implementor can have many overloads in multiple namespaces. The two tag parameters allow this function to be provided once for the whole class or individually for each message type. The purpose of the error contract is for targets that enable exceptions to provide an overload that handles the exceptions.
Finally, the OnUnhandled method on target is called.
inline std::pair<bool, LRESULT> optionalUnhandled(...)
{
return std::make_pair(false, 0);
}
If the first overload is not valid then this one is used to report that the message is still unhandled.
This introduced the optional method construct, this is the mechanism that is used to detect whether the target supports each potential message. In this case it detects wether the target is interested in the unhanded messages. Providing this function on the target allows the target to chain the message to another handler.
Next up is the generator..