Stephen Collins
Stephen Collins

Reputation: 3653

Pointer to member function the strange way

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.HookMouseDown(&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?

Upvotes: 3

Views: 162

Answers (4)

sehe
sehe

Reputation: 393134

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);
}

Upvotes: 3

Puppy
Puppy

Reputation: 146940

You really want std::function and std::bind/lambdas (function/bind also available in Boost). This more than adequately solves the problem.

Upvotes: 0

Constantinius
Constantinius

Reputation: 35069

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);

Upvotes: 0

Bj&#246;rn Pollex
Bj&#246;rn Pollex

Reputation: 76828

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();
}

Upvotes: 1

Related Questions