Boost logo

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