views:

94

answers:

3

Hi,

I'm trying to code the following situation: I have a base class providing a framework for handling events. I'm trying to use an array of pointer-to-member-functions for that. It goes as following:

class EH { // EventHandler
   virtual void something(); // just to make sure we get RTTI
public:
  typedef void (EH::*func_t)();
protected:
  func_t funcs_d[10];
protected:
  void register_handler(int event_num, func_t f) {
    funcs_d[event_num] = f;
  }
public:
  void handle_event(int event_num) {
    (this->*(funcs_d[event_num]))();
  }
};

Then the users are supposed to derive other classes from this one and provide handlers:

class DEH : public EH {
public:
  typedef void (DEH::*func_t)();
  void handle_event_5();
  DEH() {
     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile
     ........
  }
};

This code wouldn't compile, since DEH::func_t cannot be converted to EH::func_t. It makes perfect sense to me. In my case the conversion is safe since the object under this is really DEH. So I'd like to have something like that:

void EH::DEH_handle_event_5_wrapper() {
  DEH *p = dynamic_cast<DEH *>(this);
  assert(p != NULL);
  p->handle_event_5();
}

and then instead of

     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile

in DEH::DEH() put

     register_handler(5, &EH::DEH_handle_event_5_wrapper); 

So, finally the question (took me long enough...): Is there a way to create those wrappers (like EH::DEH_handle_event_5_wrapper) automatically? Or to do something similar? What other solutions to this situation are out there?

Thanks.

A: 

You really shouldn't be doing it this way. Check out boost::bind

http://www.boost.org/doc/libs/1_43_0/libs/bind/bind.html

Elaboration:

First, I urge you to reconsider your design. Most event handler systems I've seen involve an external registrar object that maintains mappings of events to handler objects. You have the registration embedded in the EventHandler class and are doing the mapping based on function pointers, which is much less desirable. You're running into problems because you're making an end run around the built-in virtual function behavior.

The point of boost::bindand the like is to create objects out of function pointers, allowing you to leverage object oriented language features. So an implementation based on boost::bind with your design as a starting point would look something like this:

struct EventCallback
{
    virtual ~EventCallback() { }
    virtual void handleEvent() = 0;
};

template <class FuncObj>
struct EventCallbackFuncObj : public IEventCallback
{
    EventCallbackT(FuncObj funcObj) :
        m_funcObj(funcObj) { }
    virtual ~EventCallbackT() { }

    virtual void handleEvent()
    {
        m_funcObj();
    }

    private:
        FuncObj m_funcObj;
};

Then your register_handler function looks something like this:

  void register_handler(int event_num, EventCallback* pCallback) 
  {
      m_callbacks[event_num] = pCallback;
  }

And your register call would like like:

register_handler(event, 
    new EventCallbackFuncObj(boost::bind(&DEH::DEH_handle_event_5_wrapper, this)));

Now you can create a callback object from an (object, member function) of any type and save that as the event handler for a given event without writing customized function wrapper objects.

bshields
Could you please elaborate a little bit?I kinda had a feeling that there should be a way to do it betterwith all the binding stuff, but couldn't figure it out.
anatoli
Ok, thanks, I understand your suggestion.
anatoli
A: 

Why not just use virtual functions? Something like

class EH {
public:
  void handle_event(int event_num) {

    // Do any pre-processing...

    // Invoke subclass hook
    subclass_handle_event( event_num );

    // Do any post-processing...
  }
private:
  virtual void subclass_handle_event( int event_num ) {}
};

class DEH : public EH {
public:
  DEH() { }
private:
  virtual void subclass_handle_event( int event_num ) {
     if ( event_num == 5 ) {
        // ...
     }
  }
};
Troubadour
It's possible. However, I wanted to make the implementation of DEH as easy as possible for the user - this 'if' over all the events is going to be pretty ugly.
anatoli
@anatoli: So you think that users creating derived classes will find pointers-to-members simpler than an if or switch construct?
Troubadour
Sorry I dropped off the net - had some trouble with the account.I agree with you that pointers-to-members are very nasty. However, I think that in this case most of this stuff is under the hood. The way for the user to register the handlers is very simple and follows a very simple template. As well, as opposite to the if/switch solution, in this case the base class can do more checks - for example that there are handlers for all events etc.
anatoli
+1  A: 

Instead of creating a wrapper for each handler in all derived classes (not even remotely a viable approach, of course), you can simply use static_cast to convert DEH::func_t to EH::func_t. Member pointers are contravariant: they convert naturally down the hierarchy and they can be manually converted up the hierarchy using static_cast (opposite of ordinary object pointers, which are covariant).

The situation you are dealing with is exactly the reason the static_cast functionality was extended to allow member pointer upcasts. Moreover, the non-trivial internal structure of a member function pointer is also implemented that way specifically to handle such situations properly.

So, you can simply do

DEH() {
   func_t f5 = &DEH::handle_event_5;
   register_handler(5, static_cast<EH::func_t>(f5));
   ........
}

I would say that in this case there's no point in defining a typedef name DEH::func_t - it is pretty useless. If you remove the definition of DEH::func_t the typical registration code will look as follows

DEH() {
   func_t f5 = static_cast<func_t>(&DEH::handle_event_5); 
   // ... where `func_t` is the inherited `EH::func_t`
   register_handler(5, f5);
   ........
}

To make it look more elegant you can provide a wrapper for register_handler in DEH or use some other means (a macro? a template?) to hide the cast.

This method does not provide you with any means to verify the validity of the handler pointer at the moment of the call (as you could do with dynamic_cast in the wrapper-based version). I don't know though how much you care to have this check in place. I would say that in this context it is actually unnecessary and excessive.

AndreyT
Thanks. It solves the problem. I knew that the cast was possible, but thought that it had an undefined behavior according to the standard.
anatoli
@anatoli: No, the behavior is defined, assuming the call is valid. And again, the validity of the call is determined *at the moment of the call*, i.e. if the object has the right type and the pointer points to the right method, then everything is OK. This is how it is defined in the standard.
AndreyT