[wx-dev] Template based wxEvtHandler::Connect feature request/suggestion

Brian Vanderburg II BrianVanderburg2 at aim.com
Wed Jul 23 19:18:02 PDT 2008


Vadim Zeitlin wrote:
> On Wed, 23 Jul 2008 18:52:28 -0400 Brian Vanderburg II <BrianVanderburg2@=
aim.com> wrote:
>
> BVI> I haven't tested this at all yet, as it would require changes to wha=
t =

> BVI> the dynamic event table stores and that may be a pretty big change, =
I =

> BVI> don't know. =

>
>  It's certainly not trivial but this code is well isolated and it shouldn=
't
> be very difficult to change it.
>
> BVI> I just responded to another post about the same, and had the Idea th=
at =

> BVI> if wxEVT_PAINT was a class then
> BVI> wxEVT_PAINT::EventObjectType could be a typedef to the correct event=
, =

>
>  Yes, this is even simpler than specializing a template for them, I like
> this.
>
>  A problem which immediately comes to mind with this approach is that
> defining new event types is going to completely change which would mean
> that all the existing code doing it wouldn't compile any more. And this is
> probably not acceptable, even considering all the gains.
>
>  So we probably would have to keep the existing int event types and add n=
ew
> and different ones to be used with template Connect().
>
> BVI> template <typename C, typename E>
> BVI> void ConnectMember(int id, E type, void (*C::fn)(typename =

> BVI> E::EventObjectType&), C* obj)
> BVI> {
> BVI> }
> BVI> =

> BVI> This would require receiving an object when calling the template fun=
ction:
>
>  This rather negates what we're trying to do...
>
> BVI> A better idea is to omit the type from an argument to =

> BVI> ConnectFunction/ConnectMember and call it as this:
> BVI> =

> BVI> template <typename C, typename E>
> BVI> void ConnectMember(int id, void (*C::fn)(typename E::EventObjectType=
&), =

> BVI> C* obj)
> BVI> {
> BVI> }
> BVI> =

> BVI> Then it would be connected in user code as:
> BVI> =

> BVI> ConnectMember<wxEVT_PAINT>(id, &MyClass::OnPaint);
>
>  This still requires an object, doesn't it? I don't see the difference wi=
th
> the above version to be honest.
>   =

Changing wxEVT_PAINT to a class, the version above required an instance =

of wxEVT_PAINT since it was receiving it as an argument:

Connect(int id, E type, ...) -> Connect(myobj->GetId(), wxEVT_PAINT(), ...)

You have to pass wxEVT_PAINT() instead of just wxEVT_PAINT

The second version does not receive E as an argument so it doesn't need =

an instance of wxEVT_PAINT, it only needs to be told the type:

Connect<wxEVT_PAINT>(myobj->GetId(), ...)

> BVI> The Connect function would then check that the function is getting t=
he =

> BVI> correct argument for the type of event.
>
>  How?
>   =


When using ConnectMember<wxEVT_PAINT>(...) E becomes wxEVT_PAINT, so =

since the connected function requires E::EventObjectType for the =

template to match, it will only match if the function signature is =

correct:  MyWindow::OnPaint(wxPaintEvent&) would match, but =

MyWindow::OnPaint(wxCommandEvent&) would not.

I've attached a small file of some more work I've done thinking about this.


Brian Vanderburg II


-------------- next part --------------
#include <iostream>
#include <vector>

// Get unique event type value
int RegisterEventTypeValue()
{
    static int evt =3D 1;
    return evt++;
}

// base event
class Event
{
public:
    Event() : m_type(0) { }
    virtual ~Event() { }
    =

    int GetType() const { return m_type; }
    void SetType(int type) { m_type =3D type; }
    =

protected:
    int m_type;
};

// create an event type
// T is not used but is still important
// Otherwise two different event types that use the same event object type
// would have the same event value (EVT_MOTION and EVT_LEFT_DOWN both would
// have the same value if only used class Blah : public TypeEvent<MouseEven=
t>)
template <typename T, typename E>
class TypeEvent
{
public:
    static int GetEventTypeValue()
    {
        static int value;
        if(value =3D=3D 0)
        {
            // use critical section and double-checked locking
            // if using threads
            value =3D RegisterEventTypeValue();
        }
        return value;
    }
    =

    typedef E EventObjectType;
};


#define DEFINE_EVENT(type, obj) class type : public TypeEvent<type, obj> { }

// Mouse events
class MouseEvent : public Event
{
public:
    MouseEvent(int x, int y) : m_x(x), m_y(y) { }
    =

    int m_x, m_y;
};

DEFINE_EVENT(EVT_MOTION, MouseEvent);
DEFINE_EVENT(EVT_LEFT_DOWN, MouseEvent);
DEFINE_EVENT(EVT_LEFT_UP, MouseEvent);

// Activate events
class ActivateEvent : public Event
{
public:
    ActivateEvent(bool active) : m_active(active) { }
    =

    bool m_active;
};

DEFINE_EVENT(EVT_ACTIVATE, ActivateEvent);

// Functors
class Functor
{
public:
    Functor() {}
    virtual ~Functor() { }
    =

    virtual void Execute(Event& evt) =3D 0;
};

template <typename E>
class FunctionFunctor : public Functor
{
public:
    FunctionFunctor(void (*fn)(E&)) : m_fn(fn) { }
    ~FunctionFunctor() { }
    =

    void Execute(Event& evt)
    {
        (*m_fn)(static_cast<E&>(evt));
    }
    =

private:
    void (*m_fn)(E&);
};

template <typename E, typename C>
class MemberFunctor : public Functor
{
public:
    MemberFunctor(void (C::*fn)(E&), C* obj) : m_fn(fn), m_obj(obj) { }
    ~MemberFunctor() { }

    void Execute(Event& evt)
    {
        (m_obj->*m_fn)(static_cast<E&>(evt));
    }

private:
    void (C::*m_fn)(E&);
    C* m_obj;
};


// Event handlers
struct EventEntry
{
    int type;
    Functor* func;
};

class EventHandler
{
public:
    EventHandler() { }
    ~EventHandler() { }
    =

    template <typename E>
    void ConnectFunction(void (*fn)(typename E::EventObjectType&))
    {
        Functor* f =3D new FunctionFunctor<typename E::EventObjectType>(fn);
        EventEntry e;
        e.type =3D E::GetEventTypeValue();
        e.func =3D f;
        m_connections.push_back(e);
    }
    =

    template <typename E, typename C>
    void ConnectMember(void (C::*fn)(typename E::EventObjectType&), C* obj)
    {
        Functor* f =3D new MemberFunctor<typename E::EventObjectType, C>(fn=
, obj);
        EventEntry e;
        e.type =3D E::GetEventTypeValue();
        e.func =3D f;
        m_connections.push_back(e);
    }
    =

    void ProcessEvent(Event& evt)
    {
        for(size_t pos =3D 0, len =3D m_connections.size(); pos < len; pos+=
+)
        {
            if(m_connections[pos].type =3D=3D evt.GetType())
            {
                m_connections[pos].func->Execute(evt);
            }
        }
    }
    =

private:
    // Connections
    std::vector<EventEntry> m_connections;
};

// Fire events
void FireMotion(EventHandler& handler)
{
    MouseEvent evt(10, 20);
    std::cout << "Motion is going\n";
    std::cout << "Event type value: " << EVT_MOTION::GetEventTypeValue() <<=
 "\n";
    evt.SetType(EVT_MOTION::GetEventTypeValue());
    =

    handler.ProcessEvent(evt);
}

void FireLeftDown(EventHandler& handler)
{
    MouseEvent evt(30, 40);
    std::cout << "Left is going down\n";
    std::cout << "Event type value: " << EVT_LEFT_DOWN::GetEventTypeValue()=
 << "\n";
    evt.SetType(EVT_LEFT_DOWN::GetEventTypeValue());
    =

    handler.ProcessEvent(evt);
}

void FireActivate(EventHandler& handler)
{
    ActivateEvent evt(true);
    std::cout << "Window is getting activated\n";
    std::cout << "Event type value: " << EVT_ACTIVATE::GetEventTypeValue() =
<< "\n";
    evt.SetType(EVT_ACTIVATE::GetEventTypeValue());
    =

    // Here ActivateEvent could have directly set the type in its construct=
or
    // since there is only one event type for it, unlice MouseEvent which h=
as
    // several event types
    =

    handler.ProcessEvent(evt);
}

// Some tests
void fn1(MouseEvent& evt)
{
    std::cout << "fn1(MouseEvent& evt)\n";
    std::cout << evt.m_x << " " << evt.m_y << "\n";
}

void fn2(MouseEvent& evt)
{
    std::cout << "fn2(MouseEvent& evt)\n";
    std::cout << evt.m_x << " " << evt.m_y << "\n";
}

class Object
{
public:
    void fn1(MouseEvent& evt)
    {
        std::cout << "Object::fn1(MouseEvent& evt) of " << m_name << "\n";
        std::cout << evt.m_x << " " << evt.m_y << "\n";
    }
    =

    void fn2(ActivateEvent& evt)
    {
        std::cout << "Object::fn2(ActivateEvent& evt) of " << m_name << "\n=
";
        std::cout << (int)evt.m_active << "\n";
    }
    =

    Object(const char* name)
    {
        m_name =3D name;
    }
    =

    const char* m_name;
};

class Derived : public Object
{
public:
    Derived(const char* name) : Object(name) { }
};

int main()
{
    EventHandler handler;
    =

    handler.ConnectFunction<EVT_MOTION>(fn1);
    handler.ConnectFunction<EVT_LEFT_DOWN>(fn2);
    FireLeftDown(handler);
    FireMotion(handler);
    =

    std::cout << "\n\n";
    =

    Object o1("o1");
    Object o2("o2");
    =

    handler.ConnectMember<EVT_MOTION>(&Object::fn1, &o1);
    handler.ConnectMember<EVT_MOTION>(&Object::fn1, &o2);
    handler.ConnectMember<EVT_ACTIVATE>(&Object::fn2, &o1);
    =

    FireMotion(handler);
    FireActivate(handler);
    =

    // Note when connecting to a base member using a deived object
    // you must cast to the object pointer to a base as well:
    Derived d("d");
    =

    // Derived::fn1 does not exist
    // handler.ConnectMember<EVT_MOTION>(&Derived::fn1, &d);
    =

    // First parameter (&Object::fn1) decieds C of the template to be Objec=
t so
    // obj needs to match it
    // handler.ConnectMember<EVT_MOTION>(&Object::fn1, &d);
    =

    handler.ConnectMember<EVT_MOTION>(&Object::fn1, static_cast<Object*>(&d=
));
    =

    =

    return 0;
}







More information about the wx-dev mailing list