|
Boost : |
From: John Torjo (john.lists_at_[hidden])
Date: 2003-08-10 07:13:42
Eugene,
one more thing when you implement the GUI library.
I ALWAYS hated the message maps from MFC/WTL.
So now I came up with a quite cute method of automating registering of events for a given window.
(this should work for registering messages, etc.)
It's very flexible. In other words, when you add an event for wnd_widget,
it does not couple the header file wnd_widget.h to the resource file (think MFC
for a moment ;) ) So, in case you modify the ID of a resource, all
you have to do is recompile the source file (wnd_widget.cpp), not the whole files
depending on wnd_widget.h.
So, lets get down to it:
Here's how to register an event for window test:
// test.h
class test : public wnd< test> {
public:
event_base on_ok();
};
// test.cpp
#include "resource.h"
test::event_base test::on_ok() {
// [do whatever you like to handle the event]
// this is how we say that on_ok should handle the ID_OK event
//
return event< ID_OK, &test::on_ok>();
}
P.S. you have no idea how many workarounds/ twists I had to do to make this compile on VC6 !!!
-------------------
#include <map>
#include <algorithm>
template< class initializer_class>
struct wnd_initializer {
struct init {
init() {
initializer_class::do_initialize();
}
};
static init s_init;
};
template< class initializer_class>
wnd_initializer< initializer_class>::init wnd_initializer< initializer_class>::s_init;
template< class wnd_class>
struct wnd {
struct event_base;
typedef event_base (wnd_class::*func)();
// ... prevent wnd_event_base from being constructed directly
struct event_base {
protected:
event_base( int id, func f, void* )
: m_id( id) {
}
public:
event_base( const event_base& other)
: m_id( other.m_id){}
private:
int m_id;
// VC6 chokes on this
//func m_f;
};
private:
template< int id, func f>
struct event_initializer {
static void do_initialize() {
wnd< wnd_class>::s_events.add_event( id, new func_wrapper<f>());
}
};
protected:
template< int id, func f>
struct event
: public event_base,
private wnd_initializer< event_initializer<id, f> >
{
typedef wnd_initializer< event_initializer<id, f> > initializer;
event() : event_base( id, f, (void*)&initializer::s_init) {
}
};
private:
// keeping the member function pointers as data members
// (example : struct keeper { func m_f; }) makes VC6 choke
struct func_wrapper_base {
virtual void call_event( wnd_class *) const = 0;
};
template< func f>
struct func_wrapper : public func_wrapper_base {
virtual void call_event( wnd_class *p) const {
(p->*f)();
}
};
public:
// contains the events for this window
struct events_for_wnd {
typedef std::map< int, func_wrapper_base*> events_coll;
void add_event( int id, func_wrapper_base * f) {
m_events[ id] = f;
}
const events_coll & get_events() const {
return m_events;
}
void call_event( int id, wnd_class * p) const {
typedef typename events_coll::const_iterator const_iterator;
const_iterator found = m_events.find( id);
if ( found != m_events.end() )
if ( p != 0)
found->second->call_event( p);
}
~events_for_wnd() {
std::for_each( m_events.begin(), m_events.end(), do_delete);
}
private:
static void do_delete( const std::pair< const int, func_wrapper_base *> &val) {
delete val.second;
}
private:
events_coll m_events;
};
static events_for_wnd s_events;
};
template< class wnd_class>
wnd< wnd_class>::events_for_wnd wnd< wnd_class>::s_events;
// test.h
class test : public wnd< test> {
public:
event_base on_ok();
event_base on_cancel();
event_base on_apply();
};
// test.cpp
#include <iostream>
#define ID_OK 1
#define ID_CANCEL 2
#define ID_APPLY 3
test::event_base test::on_ok() {
std::cout << "ON OK event called" << std::endl;
// this is how we say that on_ok should handle the ID_OK event
//
return event< ID_OK, &test::on_ok>();
}
test::event_base test::on_cancel() {
std::cout << "ON CANCEL event called " << std::endl;
// this is how we say that on_ok should handle the ID_CANCEL event
//
return event< ID_CANCEL, &test::on_cancel>();
}
test::event_base test::on_apply() {
std::cout << "ON APPLY event called " << std::endl;
// this is how we say that on_ok should handle the ID_APPLY event
//
return event< ID_APPLY, &test::on_apply>();
}
int main(int argc, char* argv[])
{
test t;
typedef test::events_for_wnd::events_coll coll;
const coll & events = test::s_events.get_events();
coll::const_iterator first = events.begin(),
last = events.end();
while ( first != last) {
int id = first->first;
std::cout << "event handler for id " << id << " is in place" << std::endl;
// calling this event for 't':
test::s_events.call_event( id, &t);
++first;
}
std::cin.get();
return 0;
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk