[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