Sign in to follow this  
yckx

typedef, template, and member function pointers

Recommended Posts

I'm slowly developing an OO Win32 wrapper for my own use and amusement. I have a BaseWindow class that has a static BaseProcess member, which is registered as the WndProc of every window, and which routes the messages to the Process member of windows that are instances of derived classes. Right now, Process is written like a traditinal WndProc function, using a large switch statement, and there's a different one for each derived window class (It's a virtual function). I have an idea of giving windows a std::map storing message/function pointer pairs. Process can then be a fairly generic function that searches for the message in the std::map and calls the function pointer if it finds it, and calls DefWindowProc if the search fails. The problem I'm having is how to set up the function pointer. Ideally, the message handler functions should be members of the window class, so if I have a MainWindow class and a ChildWindow class, both derived from BaseWindow, I'd have something like:
class MainWindow : public BaseWindow
{
     //other stuff
     LRESULT CloseHandler(WPARAM, LPARAM);
     LRESULT PaintHandler(WPARAM, LPARAM);

     typedef LRESULT(MainWindow::*MainMsgHandler) (WPARAM, LPARAM);
     std::map<UINT, MainMsgHandler> msghandler_map;
};

class ChildWindow : public BaseWindow
{
     //other stuff
     LRESULT CloseHandler(WPARAM, LPARAM);
     LRESULT SizeHandler(WPARAM, LPARAM);

     typedef LRESULT(ChildWindow::*ChildMsgHandler) (WPARAM, LPARAM);
     std::map<UINT, ChildMsgHandler> msghandler_map;
};
Now, it seems to me that msghandler_map and the typedef would be best moved to BaseWindow, but I can't do that because the member function pointer needs the class scope. So my thought is to use a template. My problem is that I can't figure out how to templateize the typedef statement. I'd appreciate guidance. Also, if I'm going about this in completely the wrong way, I'd appreciate some guidance ;) I could get around this by taking the message handler functions outside the class and make them , but that doesn't feel like an acceptable solution; there must be a better way.

Share this post


Link to post
Share on other sites
You can't template a typedef directly, but it's not the end of the world. You can, if you really like, do this:

template <class DerivedT> struct MessageHandlerType{
typedef typename (DerivedT::*MsgHandler)(WPARAM, LPARAM) type;
};

//and in the code using it you'd use...

MessageHandlerType<MainWindow>::type



Or maybe you want to pass the derived class as a template parameter to the base? I forgot what this idiom is called, but I've seen it used before.

template<class DerivedT> class BaseWindow{<tt>
//...
typedef typename LRESULT(DerivedT::*MsgHandler)(WPARAM, LPARAM);
std::map<UINT, MainMsgHandler> message_handlers;
//...
}

//and then when declaring the derived class:

class MainWindow : public BaseWindow<MainWindow>{
LRESULT CloseHandler(WPARAM, LPARAM);
LRESULT PaintHandler(WPARAM, LPARAM);

//...
}



And there's always higher-level solutions like Boost.Function with boost::mem_fn and Boost.Signals. [smile]

Share this post


Link to post
Share on other sites
I went another route when I defined my last event management system (although it was not GUI-based, It can easily be adapted for a GUI system).

The idea is to use placeholders that will do the argument extraction + method call for you. Those placeholders are templated objects that inherit a basic class:

class Marshaller
{
public:
virtual LRESULT on_event(WPARAM, LPARAM) { return 0; }
};

template <class WINDOW>
class MarshallerA0R0 // args:none return:none (0 assumed, as nothing is returned)
{
public:
typedef void (WINDOW::*memberfn)();
private:
onpaintfn memberfn;
WINDOW *wnd;
public:
MarshallerA0R0(WINDOW *wnd, memberfn callback) : memberfn(callback) { }
virtual LRESULT on_event(WPARAM, LPARAM)
{
(wnd->*memberfn)();
return 0;
}
};

The BaseWindow class only defines a std::map<UINT,Marshaller*> and a utility function:

class BaseWindow
{
// TAKE CARE:
// the destructior HAVE TO destroy all the marshallers in the map
std::map<UINT,Marshaller*> marshallers;
protected:
void handle_event(UINT msg, Marshaller* marshaller)
{
// should test if the even is already registred; in this case,
// delete the previous marshaller.
marshallers[msg] = marshaller;
}
};
The creation of marshallers is easily done using template functions:
template <class WINDOW> create_marshaller_0_0(WINDOW *w, MarshallerA0R0<WINDOW>::memberfn callback)
{
return new MarshallerA0R0<WINDOW>(w, callback);
}

It takes advantage of the automatic template type deduction using the functions arguments.
Macros can help to reduce the code complexity:
#define HANDLE_WM_PAINT(memberfn) { install_handler(WM_PAINT, create_marshaller_0_0(this, (memberfn)); }

The code to call the handle the event in the message proc is then

Marshaller *marshaller;
marshaller = wnd->find_marshaller_for_message(msg);
if (marshaller)
{
return marshaller->on_event(wparam, lparam);
}

The whole idea ensure type safety and is flexible enough to adapt to any kind of event-based systems (network communication was my primary target). It requires some code on the library side (define all the marshallers and the macros to help code reading) but is very easy to use on the client application side (and quite fun, as you can easily redefine the window behavior while the application is running).

HTH,

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
I went another route when I defined my last event management system (although it was not GUI-based, It can easily be adapted for a GUI system).

Your approach actually seems similar to what I had in my head--I was just fumbling my approach to it, since I've never tackled this before. I'll have to take a closer look at it over the weekend--I'm a little drunk now :) and doubt I'll have time to work on it tomorrow.

EDIT: I haven't been able to sleep tonight, so I decided to code, and got it working. Now my window message handling code is much sleeker. Thanks, MrEvil and Emmanuel Deloget!

[Edited by - yckx on October 6, 2006 1:43:22 AM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this