I have a class, Mouse, than handles mouse events. It consists of a number of static functions for simple "where is it, etc" calls, but it also has a few non-static members, namely some event-handling stuff when it's used as an object. I'm having trouble, however with how I can allow any object to subscribe to the events. In my Mouse.h file I have the follwing declarations: (excuse the syntax errors, this is from memory)
typedef void (*MouseEvent)(Point pos,MouseButton button)
class Mouse {
MouseEvent m_downEvent;
//...
void HookMouseDown(MouseEvent handler);
void OnMouseDown();
}
...and in the implemenation...
void Mouse::HookMouseDown(MouseEvent handler) {
if (handler != NULL) m_downEvent = handler;
}
void Mouse::OnMouseDown() {
if (m_downEvent != NULL) m_downEvent(m_pos,m_button);
}
Now in my subscriber's code, It seemed logical to hook up the event this way:
m_mouse.Hoo开发者_高级运维kMouseDown(&MyClass::MouseDown);
But my compiler (MVC2008) doesn't like the fact that I'm passing it a pointer-to-member-function rather a pointer-to-free-function. After some research on here, I discovered that changing my typedef to
typedef void (MyClass::*MouseEvent)(Point pos,MouseButton button)
it won't complain and will work fine, but the problem is that this restricts subscribers to the event to only MyClass objects. Will I have to get templates involved to allow any object to subscribe to these events? Or would it be bad design to allow anything to consume mouse events in the first place?
it won't complain and will work fine, but the problem is that this restricts subscribers to the event to only MyClass objects.
Nope, you will be able to 'invoke' that member through derived class instances as well.
Regardless, the problem has been solved many times over using std::mem_fun_ptr (c++03) std::function<> (c++0x), std::bind (c++0x) and boost::bind.
Here is a full sample, see it live on https://ideone.com/mut9V:
#include <iostream>
struct MyBase
{
virtual void DoStuff(int, float) { std::cout << "Base" << std::endl; }
};
struct MyDerived : MyBase
{
virtual void DoStuff(int, float) { std::cout << "Derived" << std::endl; }
};
int main()
{
typedef void (MyBase::*memfun)(int, float);
memfun event(&MyBase::DoStuff);
MyBase base;
MyDerived derived;
(base.*event)(42, 3.14);
(derived.*event)(42, 3.14);
}
To make the class Mouse
handle pointers to member-functions of arbitrary classes, you could make it a template:
template<class T>
class Mouse {
typedef void (T::*MouseEvent)(Point pos,MouseButton button);
MouseEvent m_downEvent;
//...
void HookMouseDown(MouseEvent handler);
void OnMouseDown();
}
Or would it be bad design to allow anything to consume mouse events in the first place?
I think it is quite nice to use an event based design. On the other hand it is a pain in the ass to support both free functions and member functions. Especially if you have also to supply the instance which calls this function.
Coincidentally I stumbled upon a Delegate
implementation which exactly deals with this matter. Have a look at this interesting article.
The HookMouseDown
could now return a reference to an internal delegate, which then can be bound to either a free function or a member function:
mouse.HookMouseDown().Bind<MyClass, &MyClass::MyMember>(myinstance);
You really want std::function and std::bind/lambdas (function/bind also available in Boost). This more than adequately solves the problem.
精彩评论