Link to home
Start Free TrialLog in
Avatar of Unimatrix_001
Unimatrix_001Flag for United Kingdom of Great Britain and Northern Ireland

asked on

Pointer to a member function that isn't common

Hello.

Strange title - apologies... perhaps the addition of a description field on questions would improve things slightly?

Anyways, yes, I have the code below (incomplete - obviously), which doesn't work simply for the reason that the second parameter to addEventListener is not declared as a member of CEventListener, now I could just add in an eventReceived method to the CEventListener class and override it, but that's not quite what I'm after as then there can only be one eventReceived method in any one class that derives from CEventListener. So, are there any tricks/tips/hacks of getting a pointer to a member function, of whose class will always be derived from CEventListener of which it is not a member and then calling it?

Hm, that's a bit of a mouthful that last sentence, hopefully those of you who are more skilled at English than us native speakers can decipher what I mean. ;)

Thank you,
Uni
/******************************************************************************************
This class contains no functionality of its own - in that respect it is useless. What it
is useful for though is to allow us to use member function pointers in the event sender. We
could use templates, but this would have the disadvantage that when we declare the
CEventSender, events could only be send to the class that was defined when we created the
object. So, any class derived from this class can then listen for events.
******************************************************************************************/
class CEventListener{
};//End class definition.
 
 
CEventSender{
	public:
		bool addEventListener(CEventListener *aEventListener, void (*aMemberFunction)()){
			return true;
		}
};
 
class CListener1:public CEventListener{
public:
	void eventReceived(){
		MessageBox(0, L"", L"", 0);
	}
};
 
int main(){
	CEventSender objEventSender1;
	CListener1 objListener1;
	objEventSender1.addEventListener(&objlistener1, &CListener1::listener1);	
	return 0;
}

Open in new window

Avatar of evilrix
evilrix
Flag of United Kingdom of Great Britain and Northern Ireland image

>> that's a bit of a mouthful that last sentence, hopefully those of you who are more skilled at English than us native speakers can decipher what I mean
Hmmm, that probably explains why I haven't a clue what you are trying to do :)
What if you make eventReceived a static method ? Does that solve your problem ?
Avatar of Unimatrix_001

ASKER

>>Hmmm, that probably explains why I haven't a clue what you are trying to do :)
Everybody from the south knows northerners cannot talk! Hehehe. ;)

Well - essentially, I'm trying to take a pointer to a member function (eventReceived()), and the class which contains that method (CListener1) is derived from another class (CEventListener). But the issue is that the method which accepts the member function (addEventListener(...)), must accept any object which derives from CEventListener, although CEventListener doesn't actually contain the member function (eventReceived()) but derived classes do.... Hm, hope that helps...

>>What if you make eventReceived a static method ? Does that solve your problem ?
Sort of... I could make a static method in the class CListener1, which then passes it to the correct member function within its class, but I'm trying to find a way around that...

Thanks,
Uni
No comprende, senor. :)

Actually a short in the dark, are you trying to allow derived classes to override a listener member function?
>>No comprende, senor. :)
Not to worry... Seems to be a recurring theme in my questions lately...

>>Actually a short in the dark, are you trying to allow derived classes to override a listener member function?
No, because then when we get a pointer to the CEventListener::eventRaised method, then I cannot have more than one eventRaised method within the derived class.

Uni.
You can call pointers to member functions polymorphically. So, if you want to define the listener in derived classes but use it in the context of the super class just make it virtual. When you can pass around the this pointer as a pointer to the super (base) class and still bind it to the method of the sub (derived) class and it will behave as a polymorphic function.
>>You can call pointers to member functions polymorphically....
Erm, I think I follow that... Although as above with my reply to MrJoltCola, that way would mean derived classes couldn't use any method they wished as a listener and would be stuck to using the virtual methods defined in CEventListener.
>> I could make a static method in the class CListener1, which then passes it to the correct member function within its class, but I'm trying to find a way around that...

You've lost me too now.

What I meant when suggesting a static member function, is that you can define as many of them as you want, in any class you want, and as long as they take no parameters, and return void (ie. they fit the function pointer), they can be used with the addEventListener method.
Anyway, I'm off to bed now, so I'll leave you in the capable hands of my colleagues ;)
Hehe, okay cheers Infinity. :)
Okay - clearly me explaination skills are shoddy... I'll start at the beginning of how I came to where I am... Reply coming up...
>>then I cannot have more than one eventRaised method within the derived class

Well, you could not have more than one anyway unless you are overloading it. Are you overloading?
Bed time for me too (like I told MJC about 1/2 hrs ago!)... night Uri.... good luck MJC :)
Later mate. :)
A member function can be done as follows:

**********************************************************************
class CListener{
      void eventListener();
};

class CSender{
      void addListener(CListener *aEventListener, void (CListener ::*aMemberFunction)());
};

int main(){
      CListener objListener;
      CSender objSender;
      objListener.addListener(&objListener, &CListener::eventListener);
}
**********************************************************************

The issue with the above code is that CSender::addListener can only accept CListener objects and methods contained within. I could also make the eventListener method virtual (don't think this code is correct...):

**********************************************************************
class CListenerDefinition{
      virtual void eventListener()=0;
};

class CListener:public CListenerDefinition{
      void eventListener(){};
};

class CSender{
      void addListener(CListenerDefinition *aEventListener);
      //Further methods here call the overrided method eventListener.
};

int main(){
      CListener objListener;
      CSender objSender;
      objListener.addListener(&objListener);
}
**********************************************************************

The issue with this is that there can only be one eventListener method in a derived class of CListenerDefinition.

So, what I'm trying to do is 'expand' the top way of doing things by removing the dependancy of CSender only being able to accept pointers of member functions to CListener.

Hope that clears things up...
...and continuing on with the hope of making things clearer:

The issue with this is that there can only be one eventListener method in a derived class of CListenerDefinition.
If I have something like eventListener(uint) and eventListener(uint, uint), then I no longer have just one eventListener method, there are 3 with 2 being overloads. Something that could be done is to have CListenerDefinition have a number of eventListener methods such as eventListener1(), eventListener2(), eventListener3() etc. But again, this falls down to the issue of functions within CListener not being name friendly - not forgetting that when coding from int main(), I would have to know that CListener::eventListener() does job X, rather than have a method CListener::doJobX().

A further issue with this overriding 'solution' is that I can only have the number of listeners in CListener as there are eventListener methods within the base class CListenerDefinition which is not ideal.
IMPORTANT:
Just realised, in the above code snippets I say:      
      objListener.addListener(&objListener);
This should read:
      objSender.addListener(&objListener);
SOLUTION
Avatar of mrjoltcola
mrjoltcola
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
...continuation once more...

So, what I'm trying to do is 'expand' the top way of doing things by removing the dependancy of CSender only being able to accept pointers of member functions to CListener.
See, the only way I know of removing CSenders dependancy on CListener is by using templates, but the issue with this is that CSender could no longer accept multiple CListener-type objects in the same object. For example in the code below the last line falls flat on its face as CSender can only contain CListener1 type objects.

**********************************************************************
class CListener1{
      void eventListener1();
};

class CListener2{
      void eventListener2();
};

template<typename TListenerClassName>
class CSender{
      void addListener(TListenerClassName *aEventListener, void (TListenerClassName::*aMemberFunction)());
};

int main(){
      CListener1 objListener1;
      CListener2 objListener2;
      CSender<CListener1> objSender;
      objSender.addListener(&objListener1, &CListener1::eventListener);
      objSender.addListener(&objListener2, &CListener2 ::eventListener);
}
**********************************************************************

Hope everybody has followed so far... I think this is where I will lose you... So, rather than using templates I am wanting to be able to say of each class:

**********************************************************************
CListener - this is a general class doing nothing more than providing a common base class for any class that wishes to listen for events.

CSender - this is a class which accepts class objects that are derived from CListener. When it accepts class objects, it also accepts a pointer to a member function that will exist in a class derived from CListener.
**********************************************************************

The main issue is that CListener contains no methods itself, but the derived classes do, and it is those that I'm wanting a pointer to so that I am able to call them. I think as it stands CListener could be removed, and the eventListener methods could be static to give:

**********************************************************************
class CListener1{
      static void eventListener1();
};

class CListener2{
      static void eventListener2();
};

class CSender{
      void addListener(void (*aMemberFunction)());
};

int main(){
      CListener1 objListener1;
      CListener2 objListener2;
      CSender objSender;
      objSender.addListener(&CListener1::eventListener);
      objSender.addListener(&CListener2::eventListener);
}
**********************************************************************

Which works (which is what Infinity suggested), although this then means that I can't call any member functions on either CListener1 or CListener2. To change this I could have something as follows:

**********************************************************************
class CListener1{
      static void eventListener1(void *aMember);   //Calls memberEventListener1 after typecasting the arg.
      void memberEventListener1();
};

class CListener2{
      static void eventListener2(void *aMember);   //Calls memberEventListener2 after typecasting the arg.
      void memberEventListener2();
};

class CSender{
      void addListener(void (*aMemberFunction)(void *), void *aMember);
};

int main(){
      CListener1 objListener1;
      CListener2 objListener2;
      CSender objSender;
      objSender.addListener(&CListener1::eventListener, objListener1);
      objSender.addListener(&CListener2::eventListener, objListener2);
}
**********************************************************************

Thing is, this is slightly messy in that each memberEventListener method I have to have an equivalent static method (or one if I use some method identification within the static method to state which method I wish to call).

So, my overall goal is to move this static method functionality within the CSender class...

Hope that hels! :)
<quote>
class CListener1{
     void eventListener1();
};

class CListener2{
     void eventListener2();
};
</quote>

It appears what you are trying to do is akin to an interface or mixin, without inheritance, but that is not C++, in C++ we do this by inheritance. Interfaces are defined by a base class and implemented by the derived classes, its that simple. C++ does not provide, at the language level, a feature that says "if it quacks like a duck it must be a duck", it provides "if it has duck genes then it is a duck". This is type safety.
>>So, what I'm trying to do is 'expand' the top way of doing things by removing the dependancy of CSender only being able to accept pointers of member functions to CListener

Why is this dependency a bad thing? If you want that, why use classes at all? You can just use plain old C style functions, which have no class type associated.
>>Why is this dependency a bad thing?
Because I may have two listeners that want to listen for the same event, but they both cannot be added to CSender as CSender can only accept the type CListener1 or CListener2.
>>It appears what you are trying to do is akin to an interface or mixin, without inheritance, but that is not C++, in C++ we do this by inheritance.
Just to possibly state the obvious, I'm not necessarily wanting to use inheritance and virtual functions, they are merely a way that I thought I could achieve what I'm after. I'm open to any suggestions...
>>>>Why is this dependency a bad thing?
>>Because I may have two listeners that want to listen for the same event, but they both cannot be added to CSender as CSender can only accept the type CListener1 or CListener2.

That doesn't sound right to me. I think it would be clearer with a concrete implementation of CSender. As of now, you don't have one. Consider how you would write CSender. Is there to be a list<> of listeners that you will iterate?

Lets implement CSender::addListener()
>>Is there to be a list<> of listeners that you will iterate?
Yes.
Then that list can contain any derived class of CListener. Again, show me an implementation of CListener as is in your mind. I am thinking pointers to functions may not be needed. Perhaps plain virtual functions are adequate.

foreach listener in list
   listener->fun()

If the fun() is a virtual function of CListener, all classes deriving will implement it. The only reason to use pointers to members would be if a specific listener class needs multiple functions. In that case you can take my suggestion above regarding "thunks" and add a base class function for each, which will call the corresponding derived virtual method, implemented in the derived class. If the base class provides a default implementation for each, then the derived classes would only need to implement/override the ones they wanted to change.

>>Perhaps plain virtual functions are adequate.
I don't think they will do what I'm after because I may not want CListener1 to have the method fun(), I may want the method to be called something more relevant to what CListener1 should do. Take this example for instance:


CEmergencyDispatcher:public CEventListener{
      void sendEmergencyResponse(unsigned int aLocation);
};

CNewsCrew:public CEventListener{
      void sendNewsTeam(unsigned int aLocation);
};

CVehicle:public CSender{
      void crashed();
};


CVehicle has been set up so that the crashed method from CVehicle calls the sendEvent method that is part of CSender. This has been set up so that this event sender calls the methods in CEmergencyDispatcher and CNewsCrew with the location. Now I may have a different CSender class such as:

CInterview:CSender{
      void setupInterview();
};

where the setupInterview would call only the method of the CNewsCrew class.
Also, just to expand on why I don't think virtual functions would help. Consider if CEmergencyDispatcher looked like:

CEmergencyDispatcher:public CEventListener{
      void sendEmergencyResponse(unsigned int aLocation);
      void sendInvestigationUnit(unsigned int aLocation);
};

there are two seperate event listeners for this class.
MJC, I hope you don't mind but I'm afraid I'm going to have to go to bed - starting to fall asleep here... I shall be back around 9:30 GMT... :)

Thanks for your help,
Uni
No problem. Perhaps evilrix can help you in the morning, GMT. I am EST. I can see what you are attempting, but I have issues with the approach from a design perspective. You can simply write individual classes per event, if you want, and register those. I think perhaps you are caught up on the actual naming signature of a method. Thats another reason I say use a base class dispatch thunk method (which is always named func()) and implement the thunk in each derived class. For a dispatcher, you could write multiple queues/lists so each event has its own list of listeners and you call fun() on each. I still see no problem with a single listen() function per listener. The only issue that arises is if you want one listener to simultaneously listen for multiple, different events. In this scenario, I don't see a need for that.

Keep in mind, forcing the issue with C++ pointers to members may not win you as much performance as you think, over if/then/else or switch. They are not like C pointers to functions, which are super fast. C++ actually uses some overhead due to the additional typing and virtual dispatching features.

ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Then I set forth to try to get the static_casts out of the listener implementations and into a more central location, and came up with the following.

Advantages :
(a) the main code is "clean"
(b) the listener classes (Listener1 and Listener2 in the example) are clean too. They use member functions for the listener implementations, and have one dispatcher that maps the enum value to the proper event handler (kind of like a poor man's virtual functions lol). All the more difficult code is hidden in the other classes.

Disadvantages :
(a) the enum definition is common for all listeners, which makes it harder to implement listeners separately (ie. in isolation of the rest of the code). This can be solved however by casting enum values to ints when passing them through the addListener function, and back to the appropriate enum values in the triggerListener function.
(b) it's not very pretty, and looks a bit over-engineered lol ... it's a lot of effort just to keep the listener classes clean.
#include <iostream>
#include <utility>
#include <vector>
 
typedef enum {
  LT_eventListener1,
  LT_eventListener2
} ListenerType;
 
 
class ListenerBase;
typedef void (*Listener)(ListenerType, ListenerBase&);
 
class ListenerBase {
  public :
};
 
template <typename ListenerDerived>
class ListenerInter : public ListenerBase {
  public :
    static void triggerListener(ListenerType type, ListenerBase &listener) {
      ListenerDerived::eventDispatcher(type, static_cast<ListenerDerived&>(listener));
    }
};
 
class Listener1 : public ListenerInter<Listener1> {
  private :
    int value;
 
  public :
    Listener1(int v) : value(v) { }
 
    static void eventDispatcher(ListenerType type, Listener1 &obj) {
      switch (type) {
        case LT_eventListener1 : obj.eventListener1(); break;
        case LT_eventListener2 : obj.eventListener2(); break;
      }
    }
 
    void eventListener1() {
      std::cout << "Listener1::eventListener1 -> value = " << this->value << std::endl;
    }
 
    void eventListener2() {
      std::cout << "Listener1::eventListener2 -> value = " << this->value << std::endl;
    }
};
 
class Listener2 : public ListenerInter<Listener2> {
  private :
    double value;
 
  public :
    Listener2(double v) : value(v) { }
 
    static void eventDispatcher(ListenerType type, Listener2 &obj) {
      switch (type) {
        case LT_eventListener1 : obj.eventListener1(); break;
        case LT_eventListener2 : obj.eventListener2(); break;
      }
    }
 
    void eventListener1() {
      std::cout << "Listener2::eventListener1 -> value = " << this->value << std::endl;
    }
 
    void eventListener2() {
      std::cout << "Listener2::eventListener2 -> value = " << this->value << std::endl;
    }
};
 
class Sender {
  private :
    typedef struct ListenerData {
      ListenerBase *obj;
      Listener trigger;
      ListenerType type;
      ListenerData(ListenerBase *o, Listener tr, ListenerType t) : obj(o), trigger(tr), type(t) { }
    } ListenerData;
    std::vector<ListenerData> listeners;
  
  public :
    template <typename ListenerDerived>
    void addListener(ListenerDerived &obj, ListenerType type) {
      listeners.push_back(ListenerData(&obj, ListenerDerived::triggerListener, type));
    }
 
    void triggerListeners() {
      std::vector<ListenerData>::iterator it;
      for (it = listeners.begin(); it != listeners.end(); ++it) {
        ListenerData &data = *it;
        data.trigger(data.type, *(data.obj));
      }
    }
};
 
int main(void) {
  Listener1 objListener1a(5);
  Listener1 objListener1b(10);
  Listener2 objListener2(15.5);
  Sender objSender;
  objSender.addListener(objListener1a, LT_eventListener1);
  objSender.addListener(objListener1b, LT_eventListener2);
  objSender.addListener(objListener2, LT_eventListener2);
  objSender.triggerListeners();
  return 0;
}

Open in new window

I wouldn't really recommend either of these methods I posted. They were just a bit of fun getting them to work, and maybe might give you some ideas ...
Hi both,

Thanks for your input - sorry I'm running late... neighbourhood appeared to have lost power for the morning... I shall split the points between the three of you providing nobody objects to such?

Thank you,
Uni
Not at all. Does anything that was posted do what you wanted in a way that you can live with ? Or do you need some further brainstorming ?
>>Not at all. Does anything that was posted do what you wanted in a way that you can live with ?
No, I think what has been posted by everybody has been sufficient... :) I'm guessing evilrix is having a busy day at the office, still just out of courtesy I'd like to make sure he doesn't object to sharing the points... :)
>> I'm guessing evilrix is having a busy day at the office
Yeah ---> http:/Q_24571684.html ---{ even experts need a little help sometimes }

>> I'd like to make sure he doesn't object to sharing the points
Nope, as long as you are happy and the answers you select have PAQ value I am a happy bunny :)

Thanks Uri.
>>even experts need a little help sometimes
*Gasp* *Huuuu* *Jaw drop to floor*... You're human?!? Hm, I'm familiar with quite a number of compression algorithms, but can't say I've ever heard of that one before... certainly isn't mainstream, mind me asking what type of data are you compressing?

>>Nope, as long as you are happy and the answers you select have PAQ value I am a happy bunny :)
Very well. :)
>> You're human?
Yeah, but notice Infinity08 isn't :)

>> mind me asking what type of data are you compressing?
Proprietary :)
>>Yeah, but notice Infinity08 isn't :)
True... true... ;)

>>Proprietary :)
Hehe, fair enough. ;) Here's a site which compares a fair number of compression programs on different types of data. The best ones are open-source, so if you find that the 3R doesn't do what you're after then you may find something on that site...

Cheers all,
Uni
Thank you! :)
>> >> You're human?
>> Yeah, but notice Infinity08 isn't :)

But ... "I want to be a real boy !"
>> so if you find that the 3R doesn't do what you're after
Unfortunately, it's already in use and I need to understand it :(

Nice link though (bookmarked)... have 500 pts on me :)

>> I want to be a real boy
Oh dear!
>> >> I want to be a real boy
>> Oh dear!

Let's see if this one rings a bell : "I'm sorry I'm not real. If you let me, I'll be so real for you! "
>>But ... "I want to be a real boy !"
Hahahahaha! :D That's brilliant...

>>Unfortunately, it's already in use and I need to understand it :(
Ah... not to worry... Infinity is on it, you'll understand it soon after he has completed the telepathic mind-meld... ;)

>>Nice link though (bookmarked)... have 500 pts on me :)
Wow! Thank you very much! :)

>>Let's see if this one rings a bell : "I'm sorry I'm not real. If you let me, I'll be so real for you! "
Can't say that one is familiar to me... sounds like something that could be from an virtual adult site... Anything you want to share with the community Infinity? ;)
>> sounds like something that could be from an virtual adult site... Anything you want to share with the community Infinity? ;)

Not really, unless you're also into egg whiskers, feather dusters and flying helmets (one more reference ... I'm on a roll here lol).

The quote is from the modern day Pinocchio movie : A.I. ;)
>>The quote is from the modern day Pinocchio movie : A.I. ;)
Oh!... That film was creepy! Worse than the Final Fantasy film with the uncanny valley...