Thursday, December 15, 2011

WINDOW_MESSAGE_DEFINITION

WINDOW_MESSAGE_DEFINITION is used with #include to stamp out code for each window message. One example is when generator is defined.

template<typename WindowClassTag, typename Target>
struct generator {
  typedef
    typename generator_root < WindowClassTag, Target
#   define WINDOW_MESSAGE_DEFINE_BEGIN_GENERATOR
#   include "win32_messages.h"
#   undef WINDOW_MESSAGE_DEFINE_BEGIN_GENERATOR

#   define WINDOW_MESSAGE_DEFINE_END_GENERATOR
#   include "win32_messages.h"
#   undef WINDOW_MESSAGE_DEFINE_END_GENERATOR
    >::type
  type;
};

win32_messages.h contains a #define and #include for each supported window message.

#define WINDOW_MESSAGE_DEFINITION (PAINT,   Paint,   0, ())
#include "win32_message_define.h"

#define WINDOW_MESSAGE_DEFINITION (NCPAINT, NCPaint, 1, (HRGN))
#include "win32_message_define.h"

win32_message_define.h then uses that #define to stamp out code for that message. Here are the salient parts of win32_message_define for the generator definition.

#define WINDOW_MESSAGE_CAPITAL_NAME           TPLT_CALL(TPLT_EXTRACT, TPLT_ARGUMENTS_APPEND_LIST(4, WINDOW_MESSAGE_DEFINITION, 0))
#define WINDOW_MESSAGE_CASED_NAME             TPLT_CALL(TPLT_EXTRACT, TPLT_ARGUMENTS_APPEND_LIST(4, WINDOW_MESSAGE_DEFINITION, 1))
#define WINDOW_MESSAGE_PARAMETER_COUNT        TPLT_CALL(TPLT_EXTRACT, TPLT_ARGUMENTS_APPEND_LIST(4, WINDOW_MESSAGE_DEFINITION, 2))
#define WINDOW_MESSAGE_PARAMETER_LIST         TPLT_CALL(TPLT_EXTRACT, TPLT_ARGUMENTS_APPEND_LIST(4, WINDOW_MESSAGE_DEFINITION, 3))

#if defined(WINDOW_MESSAGE_DEFINE_BEGIN_GENERATOR)

#define WINDOW_MESSAGE_OPTIONAL TPLT_CALL(MAKE_IDENTIFIER_EXPLICIT, (optional, WINDOW_MESSAGE_CASED_NAME))

  , generator<WindowClassTag, Target, decltype(WINDOW_MESSAGE_OPTIONAL(instance_of<Target>::value, instance_of<Context<WindowClassTag>*>::value, 0)) 

#undef WINDOW_MESSAGE_OPTIONAL 

#elif defined(WINDOW_MESSAGE_DEFINE_END_GENERATOR)
  >
#endif

#undef WINDOW_MESSAGE_PARAMETER_LIST
#undef WINDOW_MESSAGE_PARAMETER_COUNT
#undef WINDOW_MESSAGE_CASED_NAME
#undef WINDOW_MESSAGE_CAPITAL_NAME
#undef WINDOW_MESSAGE_DEFINITION

First the arguments are pulled out of the WINDOW_MESSAGE_DEFINITION. Then one of the #if blocks is selected and the parameters are used to fill in the name of a message specific identifier. Now that the macro machinery is exposed, here is the generator definition that is stamped out.

template<typename WindowClassTag, typename Target>
struct generator {
  typedef
    typename generator_root < 
      WindowClassTag, 
      Target
      , generator<
          WindowClassTag, 
          Target, 
          decltype(
            optionalPaint(
              instance_of<Target>::value,
              instance_of<Context<WindowClassTag>*>::value, 
              0)) 

          , generator<
              WindowClassTag, 
              Target, 
              decltype(
                optionalNCPaint(
                  instance_of<Target>::value,
                  instance_of<Context<WindowClassTag>*>::value, 
                  0)) 
         >
       >
    >::type
  type;
};

The 'optional' methods are also generated via the same macros. The only piece missing is another #if block in win32_message_define.h That will be next up..

Thursday, December 8, 2011

window message dispatch generator

One goal for this dispatcher was to achieve pay-for-play performance characteristics. The generator builds a recursive type with a recursive dispatch function that only includes the message handling supported by the target. This produces a pretty concise if-else construct.

namespace detail {
template<typename WindowClassTag, typename Target>
struct generator_end
{
  typedef
    base<WindowClassTag, Target>
  type;
};

generator_end is used to terminate the recursive type being generated. base is defined previously but will be covered later.

template <
  typename WindowClassTag,
  typename Target,
  typename MessageChoice,
  typename Base = generator_end<WindowClassTag, Target >>
struct generator;

This is the prototype for generator, 2 specializations of generator are used to either add or omit a type from the recursive type.

template <
  typename WindowClassTag,
  typename Target,
  template <
    typename A,
    typename B,
    typename C > class MessageChoice,
  typename Base >
struct generator <
  WindowClassTag,
  Target,
  MessageChoice <
    WindowClassTag,
    Target,
    base<WindowClassTag, Target >> ,
  Base >
  : public Base
{
  typedef
    MessageChoice <
      WindowClassTag,
      Target,
      typename Base::type >
  type;
};

this specialization of generator matches when the target does support a message. It creates a type result using the supplied MessageChoice which is chained into the recursive type result (by changing the last template parameter to the result of the next recursed generator type result).

template <
  typename WindowClassTag,
  typename Target,
  typename Base >
struct generator <
  WindowClassTag,
  Target,
  nohandler,
  Base >
  : public Base
{
};

This specialization of generator matches when the target does not support the message (the nohandler class is already defined. nohandler will be described later.) There is no type defined here which ensures that no code will be generated for this message.

template <
  typename WindowClassTag,
  typename Target,
  typename Base >
struct generator_root
  : public Base
{
};
}

generator_root provides a container for the generator that allows the type to be stamped out by some macros.

template<typename WindowClassTag, typename Target>
struct generator {
  typedef
    typename detail::generator_root < WindowClassTag, Target
#   define WINDOW_MESSAGE_DEFINE_BEGIN_GENERATOR
#   include "win32_messages.h"
#   undef WINDOW_MESSAGE_DEFINE_BEGIN_GENERATOR

#   define WINDOW_MESSAGE_DEFINE_END_GENERATOR
#   include "win32_messages.h"
#   undef WINDOW_MESSAGE_DEFINE_END_GENERATOR
    >::type
  type;
};

This is how the type for a set of messages is created. This marco technique is complicated in order to allow the debugger to step through the dispatch code with source lines. WINDOW_MESSAGE_DEFINE_BEGIN_GENERATOR causes win32_messages.h to resolve to a recursive type for each message, while WINDOW_MESSAGE_DEFINE_END_GENERATOR causes win32_messages.h to resolve to a < for each type, this is required to finish the recursive generator definition.

Next is a look at the macro machinery..

Thursday, December 1, 2011

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..