Solved

Using a virtual function from a variable.

Posted on 2009-07-06
55
321 Views
Last Modified: 2012-05-07
Hi,

Hope this isn't too simple, if it is please give me an answer and tell me to go to bed... :) Anyways, please see the code below in the first section, nothing too complex, although what I'm wanting is for me to be able to provide an implementation for the callback function for the someMethod function. I dunno... maybe something like the code shown in the second section... Clearly it doesn't work, but hopefully you can get the gist of what I'm trying to accomplish.

Looking forward to some solutions - unless I'm asleep...! :)

Thanks,
Uni
//////////////////////////////////////////////////////////////////////////////////////////

class CMyClass{

	public:	

		class CCallback { public: virtual void callback(); };

		void setVariable(unsigned int a){

			localVar=a;

			callbackClass.callback();

		}

		unsigned int getVariable(){

			return localVar;

		}

		void setCallback(CCallback aCallback){

			callbackClass=aCallback;

		}
 

	private:

		CCallback callbackClass;

		unsigned int localVar;

};
 

CMyClass localClassObject;

void CSomeOtherClass::someMethod(){

	localClassObject.setCallback(???);

	localClassObject.setVariable(5);

}

//////////////////////////////////////////////////////////////////////////////////////////
 

//////////////////////////////////////////////////////////////////////////////////////////

CMyClass localClassObject;

CMyClass::CCallback localClassCallback;
 

void CSomeOtherClass::someMethod(){

	localClassObject.setCallback(localClassCallback);

	localClassObject.setVariable(5);

}
 

void localClassCallback.callback(){

}

//////////////////////////////////////////////////////////////////////////////////////////

Open in new window

0
Comment
Question by:Unimatrix_001
  • 28
  • 18
  • 9
55 Comments
 
LVL 40

Expert Comment

by:evilrix
ID: 24783212
Hey Uri,

I have to be honest I'm not sure I can see exactly what you're trying to do here. Are you trying to implement a functor? If so have a look at my article on functions vs. functors to see if it helps.

http://www.experts-exchange.com/articles/Programming/Languages/CPP/Function-pointers-vs-Functors.html

If not I think you'll need to elaborate a little bit more.

-Rx.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24783306
Morning! long time no speak! :)

Well, I'm trying to implement a tidier and safer function pointer. The idea being that I create a class (CMyClass), which when appropriate can call a callback function. The problem I have is that the callback function from the CCallback class doesn't change by inheritence but by the objects that create it... Hm, somehow that made sense in my head, reading it back doesn't seem to help...

Ok, say I have two objects of CMyClass and 2 objects of the callback class. I can easily specify which CMyClass object should use which classback class object (using the setCallback method). What I have difficulty with is that the two objects of the callback class don't override the callback() method so are essentially useless. I'd rather not override the callback method by providing each possible callback method its own class as there could be quite a lot, inherited from the callback class, I'd much rather the callback method be overridden on a per object basis.

Hope that clears some things up. I remember reading your article a while ago, perhaps I should go over it again...

Thanks
Uni
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24783333
>> Morning! long time no speak! :)
Hi Uri, I hope you're well :)

>> I remember reading your article a while ago, perhaps I should go over it again...
Maybe it's just too early for me but I'm still struggling to get my head around the requirement. I don't want to seem like I'm self promoting but my article does provide some good examples on how to implement functors. Maybe it's worth having a re-read to see if it helps consolidate your thoughts and if it doesn't we can thrash this out together. If it does but you'd like a solution more tailored to your needs I'm sure we can do that too.

I'm really sorry if I am being a bit thick this morning... I am struggling with being awake today :)
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24783373
>>Maybe it's just too early for me but I'm still struggling to get my head around the requirement.
Nah, more than likely I'm wanting to do something that just doesn't exist yet or can't explain it very well! ;)

I'll have a good read over your article a few times - I'm off to the doctors soon and then have to call out to get somebodys pc up and running again, hopefully I won't be too long. I should be back for 12, and I'll let you know the state of things then.

Cheers mate,
Uni.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24783380
Ok Uri... meanwhile I'll have a re-read of the Q and if any inspiration comes to me I'll let you know :)
0
 
LVL 40

Accepted Solution

by:
evilrix earned 250 total points
ID: 24783412
Ok, is this anything like what you're trying to do?
#include <iostream>
 

struct Cb_base

{

   virtual void operator()(unsigned int u) const = 0;

};
 

struct Cb1 : Cb_base

{

   void operator()(unsigned int u) const

   {

      std::cout << "Cb1: " << u << std::endl;

   }

};
 

struct Cb2 : Cb_base

{

   void operator()(unsigned int u) const

   {

      std::cout << "Cb2: " << u  << std::endl;

   }

};
 

struct MyClass

{

   MyClass():pCb_(0), u_(0){}
 

   void setCb(Cb_base const * pCb)

   {

      pCb_ = pCb;

   }
 

   void setu(unsigned int u)

   {

      u_ = u;

   }
 

   void callCb() const

   {

      if(pCb_ != 0)

      {

         (*pCb_)(u_);

      }

   }
 

   Cb_base const * pCb_;

   unsigned int u_;

};
 

int main()

{

   Cb1 cb1;

   Cb2 cb2;
 

   MyClass myClass;
 

   myClass.setu(12);

   myClass.setCb(&cb1);

   myClass.callCb();
 

   myClass.setu(89);

   myClass.setCb(&cb2);

   myClass.callCb();

}

Open in new window

0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 250 total points
ID: 24783554
>>>>            void setVariable(unsigned int a){
>>>>                  localVar=a;
>>>>                  callbackClass.callback();
>>>>            }

functors are a (great) means to passing more information to a function that takes a function pointer as argument than could be provided with the call itself.

Looking at the setVariable member function, we see that we have another issue here though maybe it was intended to pass the localVar argument to the callback function. But if you want to pass an int argument to the callback you simply could change the prototype of the callback member function but don't need a functor for that.

As the design doesn't have any function pointers and the callback actually is a virtual function, we don't have the case where functors apply.

But waht about deriving from CCallback and implementing an override of the callback function?

The CCallback member would need to turn to a CCallback baseclass pointer, but the rest could remain as it was:


//////////////////////////////////////////////////////////////////////////////////////////

class CMyClass{

    public: 

        class CCallback { public: virtual void callback(unsigned int a) = 0; };

        void setVariable(unsigned int a){

            localVar=a;

            if (callbackClass != NULL)

                callbackClass->callback(a);

        }

        unsigned int getVariable(){

            return localVar;

        }

        void setCallback(CCallback * aCallback){

            callbackClass=aCallback;

        }

        CMyClass() : callbackClass(NULL), localVar(0) {}

 

    private:

        CCallback * callbackClass;

        unsigned int localVar;

};
 

//////////////////////////////////////////////////////////////////////////////////////////

class MyDerivedCallback : public CMyClass::CCallback

{

    unsigned int mya;

    std::string  mys;

public: 

    MyDerivedCallback(const std::string s) : mya(0) {}

    virtual void callback(unsigned int a)

    {

       mya = a;

       dosomethingwitha();

    }

    void dosomethingwitha() { std::cout << "s: " << mys << " a: " << mya; }

};

 

//////////////////////////////////////////////////////////////////////////////////////////

void CSomeOtherClass::someMethod(){
 

    CMyClass localClassObject;

    MyDerivedCallback localClassCallback("My Callback");

 

    localClassObject.setCallback(localClassCallback);

    localClassObject.setVariable(5);

    // output:   "s: My Callback a: 5"

}

 

//////////////////////////////////////////////////////////////////////////////////////////

Open in new window

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24784155
>>Ok, is this anything like what you're trying to do?
I've done a bit of thinking during the morning about what I really need (simple class mechanics really) so yes what you've come up with is just what I'm after. :)

>>itsmeandnobodyelse
Thank you for the further information. :)

Thanks both.
Uni
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24784161
Ah hold up... I dunno if it is... I think I need to get things straightened out... bbs.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24784292
Ok, here goes... I'll keep this simple to help convey the meaning. The whole purpose of this code is that when the value is changed in CFancyClass we notify the listener that the value has changed, that's all. See with the code below, there is no way for either fancyClassObject to let anything know if the value changes.
class CFancyClass{

public:

	void setValue(unsigned int aValue){

		value=aValue;

		listener();

	}

	void setListener(???){

		???

	}

private:

	unsigned int value;

	??? listener;

};
 
 

void CSomeOtherClass::noSpecialMethod(){
 

	CFancyClass fancyClassObject1;

	CFancyClass fancyClassObject2;
 

	fancyClassObject1.setListener(listenerMethod1);

	fancyClassObject2.setListener(listenerMethod2);
 

	fancyClassObject1.setValue(5);

	fancyClassObject2.setValue(99);

}
 
 

void CSomeOtherClass::listenerMethod1(){

	//This indicates that fancyClassObject1 value has changed.

}
 
 

void CSomeOtherClass::listenerMethod2(){

	//This indicates that fancyClassObject2 value has changed.

}

Open in new window

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24784411
One solution for the above would be to create two derived classes from CFancyClass, and create a virtual listener function within CFancyClass that is overridden by the two inherited classed - one for fancyClassObject1 and another for fancyClassObject2.

The problem with this is that the two derived classes aren't actually part of CSomeOtherClass - i.e. where the actual objects two objects fancyClassObject1 and fancyClassObject2 exist. So, the derived classes couldn't be treated as a method of CSomeOtherClass and wouldn't be able to access any private members etc.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24784522
Hey Uri,

Just to let you know, I'll look at this for you later when I get home (assuming no one else has solved it for you in the meantime). Sorry, real tied up at work today :(

-Rx.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24784534
Not a problem mate - I think that's my ticket to take a nap then for a couple of hours... :)
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24784684
I found 5 mins :)

Does this model the behaviour you are after?
#include <iostream>
 

struct Listener_base

{

   virtual void operator()(unsigned int u) const = 0;

};
 

struct Listener1 : Listener_base

{

   void operator()(unsigned int u) const

   {

      std::cout << "Listener1: " << u << std::endl;

   }

};
 

struct Listener2 : Listener_base

{

   void operator()(unsigned int u) const

   {

      std::cout << "Listener2: " << u  << std::endl;

   }

};
 

struct FancyClass

{

   FancyClass():pListener_(0), u_(0){}
 

   void setListener(Listener_base const * pListener)

   {

      pListener_ = pListener;

   }
 

   void setu(unsigned int u)

   {

      u_ = u;
 

      // Notify listerner

      if(pListener_ != 0)

      {

         (*pListener_)(u_);

      }

   }
 

   Listener_base const * pListener_;

   unsigned int u_;

};
 

int main()

{

   Listener1 listener1;

   Listener2 listener2;
 

   FancyClass fancyClass;
 

   fancyClass.setListener(&listener1);

   fancyClass.setu(1);

   fancyClass.setu(2);

   fancyClass.setu(3);
 

   fancyClass.setListener(&listener2);

   fancyClass.setu(4);

   fancyClass.setu(5);

   fancyClass.setu(6);
 

   fancyClass.setListener(&listener1);

   fancyClass.setu(7);

   fancyClass.setu(8);

   fancyClass.setu(9);

}

Open in new window

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24784724
>>>> So, the derived classes couldn't be treated as a method of CSomeOtherClass and wouldn't be able to access any private members etc.

You were mixing up class objects and methods.

If you derive classes from a baseclass the (data) members of the baseclass normally are protected (not private) to allow the derived classes to access them directly. But even if they were private to the baseclass you can add protected getters and setters so that you can have the access to the members as needed.

>>>>> fancyClassObject1.setListener(listenerMethod1);
If the listenerMethod1 is a function pointer it hardly can be a member function of CSomeOtherClass which hardl ycan be assumed to be known by the (much more basic) CFancyClass::setListener.

So to get your design compiled you would need to declare the functions as static member functions what would allow to pass the function pointers without an object of CSomeOtherClass. That is cause function pointers to non-static member functions were bound to their class type and virtual class hierarchie don't apply there. Moreover, the function pointers only could be called if you have some instance of the derived class where you could apply the function with. That's why function pointers to non-static member functions are rarely used (e. g. in MFC message maps where they need to make ugly casts hidden in macros to make it work. Here they have a pointer to the baseclass CWnd stored in the window resource, and virtually were calling the overloaded message map function, which then makes the casts to the member function pointers defined in macros between BEGIN_MESSAGE_MAP and END_MESSAGE_MAP).  

No, that hardly is a design which should be copied.

Alternatively, you have

(1) functors which can be used instead of static class function pointers (sample of evilrix)
(2) baseclass pointers which can be used for virtual calls of known member functions (my sample)
(3) static member function pointers where you pass a baseclass pointer as additional argument

For (3) you would have a function setListener like

void CFancyClass::setListener(ListenerBase* ptrListener, ListenerNotify notifyFunc)
{
      notifyFunc(ptrListener);
}

with

typedef void (*ListenerNotify)(ListenerBase* ptrListener);

and a static notifier function like

class Listener1 : public ListenerBase
{
     static void notifyListener(ListenerBase* ptrListener)
     {
           // turn baseclass pointer to derived class
          Listener1* ptrThis = (Listener1*)ptrListener;
          if (ptrThis == NULL) throw("wrong listener class. Listener1 expected");
          ptrThis->notify();
     }

};

That is not so much different from using a functor but less elegant.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24787386
Ah, my apologies... I slept a little longer than planned then had to get tea ready! Sorry about that... Anyway:

Does this model the behaviour you are after?
That is close, but the differences between that and what I'm after are:

1) Your listener_base class should be part of the FancyClass.

2) The Listener1 and Listener2 overridden () methods are part of class X (see next point).

3) Instead of the main() method, imagine we had another class called X, in it were 3 methods, classMain() (equivalent to the int main()), listener1Response() and listener2Response(). Both the listener1Response and listener2Response are equivalent to the overridden () operator in both your Listener1 and Listener2 class.

itsmeandnobodyelse
Thanks for the details response, I think the below text should help clear up any confusion of what I'm after.

**********************************************************************

Clarification:

If I want to implement a callback system using virtual methods, I would have to do something such as evilrix suggested in a couple of posts above. Although, if I were to create a new class, I could not have a listener without inheriting from the base listener (obviously):

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class SomeClass : public CListener_Base{

       FancyClass myFancyClass;
 
       void someMethod(){
              myFancyClass.setListener(&this);
              myFancyClass.setu(1);
       }

             void operator()(unsigned int u) const {
                    std::cout << "Listener1: " << u << std::endl;
       }

};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

This is quite a nice solution, the overridden operator () is actually part of SomeClass. The problem with this is that using this solution I cannot have more than one listener for this class. I can't create myFancyClass2 and tell it to use (&this) and hope to have a different implementation for the listener for myFancyClass2.

I'm after something similar to the above that will allow for me to have different listeners within a single class.

Thanks both,
Uni
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24788812
Ok, maybe you mean something like this that make use of member function pointers?

Note, you can have different listeners but they MUST all have the same signature.
#include <iostream>
 
 

struct Listener

{

   void cb1(unsigned int u) const

   {

      std::cout << "Listener1: " << u << std::endl;

   }

   

   void cb2(unsigned int u) const

   {

      std::cout << "Listener2: " << u  << std::endl;

   }

};
 

typedef void (Listener::*listener_func_t)(unsigned int) const;
 

struct FancyClass

{

   FancyClass(Listener & listener):listener_(listener), func_(0), u_(0){}

 

   void setListener(listener_func_t const func)

   {

      func_ = func;

   }

 

   void setu(unsigned int u)

   {

      u_ = u;

 

      // Notify listerner

      if(func_ != 0)

      {

         (listener_.*func_)(u_);

      }

   }

 

   Listener  listener_;

   listener_func_t func_;

   unsigned int u_;

};

 

int main()

{

   Listener listener;

 

   FancyClass fancyClass(listener);

 

   fancyClass.setListener(&Listener::cb1);

   fancyClass.setu(1);

   fancyClass.setu(2);

   fancyClass.setu(3);

 

   fancyClass.setListener(&Listener::cb2);

   fancyClass.setu(4);

   fancyClass.setu(5);

   fancyClass.setu(6);

 

   fancyClass.setListener(&Listener::cb1);

   fancyClass.setu(7);

   fancyClass.setu(8);

   fancyClass.setu(9);
 

}

Open in new window

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24792235
Hi evilrix,

Thanks for the reply, had an early night so I couldn't reply, sorry about that. Anyways, yes I could use function pointers, although I take it there is no way to use virtual functions for more than one listener in SomeClass as in my code above?

Thanks,
Uni
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24792744
>>>> I cannot have more than one listener for this class.
Why not storing an array of listeners in the FancyClass?




class FancyClass

{

     std::vector<ListenerBase*> listeners;

     unsigned int value;

public:

     void addListener(ListenerBase* ptrListener)

     {

           listeners.push_back(ptrListener); 

     }

     void removeListener(ListenerBase* ptrListener)

     {

           std::vector<ListenerBase*>::iterator f;

           f = std::find(listeners.begin(), listeners.end(), ptrListener); 

           if (f != listeners.end())

              listeners.erase(f); 

     }

     void setValue(unsigned int u)

     {

          std::vector<ListenerBase*>::iterator i;

          for (i = listeners.begin(); i != listeners.end(); ++i)

                (*i)->notify(value, u);  // virtual notifier

          value = u;

    }
 

};

Open in new window

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24792771
My apologies... I think I'm confusing you both here... I'll try to be a little clearer. :) Instead of working with what I have I'll explain what the end result is I'm after, perhaps that'll help. Post coming up...
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24792810
Here we go...

Take a look at the code below and let me know if you have any questions. Clearly the code below doesn't compile, but it should help convey what I'm trying to achieve.

Thanks again,
Uni
CExample1 example1Object;
 
 

class CExample1{

private:

	CVariableWatch example1Watcher;

public:

	void mainMethod(){

		example1Watcher.initialise(&CExample2::example1Listener);

	}

	void example2Listener(unsigned int example2Variable){

		cout<<"The variable for example 2 has changed!"<<endl;

	}

};
 
 

class CExample2{

private:

	static CVariableWatch example2Watcher;

public:

	static void mainMethod(){

		example2Watcher.initialise(&example1Object.example2Listener);

	}

	static void example1Listener(unsigned int example1Variable){

		cout<<"The variable for example 1 has changed!"<<endl;

	}

};
 
 

class CVariableWatch{

private:

	unsigned int watchedVariable;

	/*Pointer to listenerMethod here.*/

public:

	void initialise(/*method that is the listener here*/){

		/*Set the pointer of listenerMethod to the argument passed into the method*/

	}

	void setVariable(unsigned int newValue){

		watchedVariable=newValue;

		/*Call listenerMethod here.*/

	}

};

Open in new window

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24793034
>>>> Clearly the code below doesn't compile, but it should help convey what I'm trying to achieve.
It would help better if you could tell the actions to happen rather than posting a class design which may not be suitable for the given task.

In your above classes I can't see two listeners to get notified when the value watched for was changed.

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24793150
>>It would help better if you could tell the actions to happen rather than posting a class design which may not be suitable for the given task.
Fair enough. :)

The CVariableWatch class contains a variable that whenever it is set a listener method (that was specified during the initialise method) should be called specifying the new value of the variable.

So, in example1, we have a CVariableWatch object that we initialise in the mainMethod. We initialise it so that should the value ever change we notify the listener method in CExample2 (example1Listener).

Likewise in example2, we have a CVariableWatch object, but this time should the variable contained within it ever change we notify the listener which is in CExample1.

>>In your above classes I can't see two listeners to get notified when the value watched for was changed.
Well, in the mainMethod of example1, we setup the variable watch so that the listener for that variable is in CExample2, likewise in the main method of example2 we setup the variable watch so that the listener is in CExample1.



upload.JPG
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24793169
cont...

The picture below should help give a more visual example. The listener for each example class is set to be in the opposite example.

I'm not too sure how else to explain it, so if the above is insufficient I'd be grateful if you could explain what you're not grasping and I'll do my best.

Thanks very much! :)
Uni
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24793444
Hey Uri,

Again... real busy -- unless Alex has resolved your issue I'll get back to you later tonight :)

-Rx.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24793456
Hehe, not a problem - I appreciate you letting me know. :)

Thanks,
Uni
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24793720
>>>> The CVariableWatch class contains a variable that whenever it is set a listener method (that was specified during the initialise method) should be called specifying the new value of the variable.

Ok. Let's take this sentence as the basic requirement, right?

Should we generalize the requirements to

'There is a member variable of a class that whenever it is set will invoke one or more listener objects (that were specified during some initialise method of the class) to be notified specifying the new value of the variable'.

?


0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24793740
Yes, that could be correct, although I worry about this part:

>>will invoke one or more listener objects

Unless I'm mistaken (9/10 I am...) using a listener object would mean that whatever class had the CVariableWatch object wouldn't have the listener method.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24795045
>>>> using a listener object would mean that whatever class had the CVariableWatch object wouldn't have the listener method.

Not knowing exactly what you were speaking of. The problem I have with the expression 'method' is that it describes the 'how' and not the target. But in my understandness of OOP we always needs objects to interact with. So, IMO we have

   SomeClass someclass;  // an instance of SomeClass
   someclass.variable        // the variable to be watched
   Listener1  listener1;      // an instance of Listener1 (derived from Listener)
   Listener2  listener2;      // an instance of Listener2 (derived from Listener)

If variable was changed we have a few possibilities how listener1 and perhaps listener2 could be notified.

(A) if someclass knows of listener1 and or listener2, e. g. by some initialization, it directly could call some 'method' of listener1 respectively listener2, which does the notification, when the value was set or modified. The call could be done virtually and if listener1 and listener2 are from different classes the notify could be different as well. But even if the notify is a baseclass function it could virtually expand by calling anyother virtual function.

(B) if listener1 and listener2 know of someclass but not vice versa they only could poll on the someclass.value but can't be notified immediately.

(C) if someclass doesn't know of listener1 and listener2 and the listeners don't know of someclass but only want to get notified when someclass.value was changed, they could *install* some functions (function pointers) at someclass which would do the notification. The problem I see is that these functions need to know the listener as the target of the notification. Here functors could come to play which allow to add parameters (here the target listener) to a function pointer argument. The problem with this is, that a functor is a temporary. Hence, it couldn't be stored at someclass. Thus, the listeners added to the functor must be provided by someclass at the time when value was changed, what means we are back at A.  

Note, function pointers to non-static member functions do not help as they would need the (listener) object as well. Moreover, they cannot be used virtually, i. e. a function pointer to a non-static member functions even if stored as function pointer of a (virtual) baseclass function must be casted explicitly to the derived class or it wouldn't work.

Also templates couldn't help as the template type must be defined at compile time, i. e. there is no way to decide at runtime what template type must be used.

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24795134
Well using the code I pasted in comment #24792810:

I have only got one CVariableWatch class, nothing is derived from it, BUT I have two objects of this class (one each in CExample1 and CExample2). Now for each CVariableWatch object I am wanting to specify what method should be called by that object when its variable is changed.

...I've got a gnawing feeling that what I'm wanting just isn't going to be possible... If it were I'm sure one of you two would have stumbled upon it... :(


0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24795286
>>>> Now for each CVariableWatch object I am wanting to specify what method should be called by that object when its variable is changed.

If that would be all you simply can store a function pointer in CVariableWatch as a member and call it when the set function was called.

Your problem is that the function should know what to do after and that only can be made by storing some object data (or pointer to an object) with the function.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24795358
Your problem is that the function should know what to do after and that only can be made by storing some object data (or pointer to an object) with the function.
I'm not understanding this part quite well? Which function knowing what?

Thanks.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24795372
If that would be all you simply can store a function pointer in CVariableWatch as a member and call it when the set function was called.
I'm unsure if there is anything else needed? That's all I'm after, but the issue I have is that any number of different classes can use CVariableWatch so I can't exactly construct a function pointer in any tidy way...
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24795741
>>>> Which function knowing what?
If the function should do more than to writing 'Hello here is function 1' it would need some parameters.

>>>> That's all I'm after, but the issue I have is that any number of different classes can use CVariableWatch so I can't exactly construct a function pointer in any tidy way...

That is not the big point. Take as prototype

   void mynotifier(Parameter * par, unsigned int oldval, unsigned int newval);

where

   struct Parameter
   {
         std::map<std::string, std::string> myparams;
   };

Now a Parameter instance can hold any count of key-value pairs and the problem left is how to pass a Parameter instance with the function pointer.

typedef void (*Notifier)(Parameter * par);

class CVariableWatch
{
     unsigned int valuewatched;
     Parameter * par;
     Notifier notify;
public:
      CVariableWatch() : valuewatched(0), par(NULL), notify(NULL) {}
     void init(Parameter* p, Notifier n) { par = p; notify = n; }
     void updatevalue(unsigned int u)
     { if (notify) notify(par, valuewatched, u); valuewatched = u;  }  
};
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24796050
Hi Uri,

Home now, just catching up with what you and Alex have been discussing :)

-Rx.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24796426
Hi Rix, don't suppose that's short for Ricky as in Gervais is it? ;) Lovely, I think Alex is getting a little tired of hearing me try to explain things! :)

That is not the big point. Take as prototype...
If it is easier just assume there are no parameters, as there probably won't be - passing the integer of the altered value is nothing more than an example.

and the problem left is how to pass a Parameter instance with the function pointer.
Just a couple of things, what would the function look like that would be called from CVariableWatch?
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24796470
Ok, what about this?
#include <iostream>

 

template <typename listenclassT, typename varT>

class VarWatch

{

public:

	VarWatch(varT const & v = varT())

		: pc_(0), lf_(0), v_(v) {}

			

	typedef void (listenclassT::*listener_fp_t)(varT const &) const;

	void set_listener(listenclassT const * pc, listener_fp_t lf)

	{

		pc_ = pc;

		lf_ = lf;

	}

 

	void set(varT const & v)

	{

		v_ = v;

		

		if(pc_ && lf_)

		{

			(pc_->*lf_)(v);

		}

	}

	

	varT const & get() const { return v_; }

	

private:

	listenclassT const * pc_;

	listener_fp_t lf_;

	varT v_;

};

 

class Example1;

class Example2;

 

template <typename watchclassT, typename varT>

class ExampleBase

{

public:

	typedef VarWatch<watchclassT, varT> vw_t;

	

	void set_listener(watchclassT const * pc, typename vw_t::listener_fp_t lf)

	{

		vw_.set_listener(pc, lf);

	}

	

	void set(unsigned int u)

	{

		vw_.set(u);

	}	

	

protected:

	vw_t vw_;

};

 

class Example1 : public ExampleBase<Example2, unsigned int>

{

public:

	void listener(unsigned int const & u) const

	{

		std::cout << "Example1 listener fired: " << u << std::endl;

	}

};

 

class Example2 : public ExampleBase<Example1, unsigned int>

{

public:	

	void listener(unsigned int const & u) const

	{

		std::cout << "Example2 listener fired: " << u << std::endl;

	}

	

private:

	vw_t vw_;

};

 

int main()

{

	Example1 e1;

	Example2 e2;

	

        // Can be any member of ExampleN as long as it matches the prototype

        // void (listenclassT::*listener_fp_t)(varT const &) const

	e1.set_listener(&e2, &Example2::listener);

	e2.set_listener(&e1, &Example1::listener);

	

	e1.set(10);

	e1.set(45);

	e1.set(37);

 

	e2.set(67);

	e2.set(86);

	e2.set(93);

}

Open in new window

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24796584
Hi evilrix,

Cheers, I've had a look at your code. I'm not too strong with templates, but from what I can gather it looks pretty good, although is there a way to do away with the class ExampleBase as there are some things in there which are repeated i.e:

void set_listener(watchclassT const * pc, typename vw_t::listener_fp_t lf)

already exists in the class VarWatch?

Thanks very much,
Uni
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24796642
They are not repeated par se. Look carefully and you'll see they forward to the watcher. The watcher is an aggregate type not a super type (ie ExampleN doesn't subclass the watcher it owns it as a member variable so there is a HAS_A and not an IS_A relationship) so the only way to call the methods on it are to do it via forwarding methods. The ExampleBase is necessary to prevent duplicating "common" methods and variables in Example1 and Example2. The other way would be to make vw_ public in ExampleN but that breaks encapsulation.

The templates allow the VarWatch class to have ANY class with ANY member function (that matches the prototype void (listenclassT::*listener_fp_t)(varT const &) const) as a listener. It's about as flexible as you're gonna get whilst still maintaining compile time type safety. To go any more flexible would mean using runtime type coercion and you really don't want to go there (for your sanity as well as mine!) :)
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24796680
Hiya,

Thanks, it's gonna take me a bit to fully understand this code but I'll be back shortly. :)

Thanks again,
Uni
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24796751
>> Thanks, it's gonna take me a bit to fully understand this code but I'll be back shortly. :)
No worries Uri, take your time.

BTW: This is a variation of what is known as the Observer Pattern.

http://en.wikipedia.org/wiki/Observer_pattern

"Implementing the Observer Pattern in C++ - Part 1"
http://accu.org/index.php/journals/372
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 24796927
>>>> Lovely, I think Alex is getting a little tired of hearing me try to explain things! :)
Only a little bit. It's only that I still don't know what's your final purpose?

>>>> If it is easier just assume there are no parameters
Is it? The solution not passing any parameters beside of the value changed to a function called by function pointer was posted already a few times.

>>>> what would the function look like that would be called from CVariableWatch?
Do you mean the one with prototype 'void mynotifier(Parameter * par, unsigned int oldval, unsigned int newval);' , or generally?

If you mean generally, we got the root of the issue. As long as you don't know what the methods to install at CVariableWatch actually should do, it is hard to give advice ;-)

But, let's assume you mean the prototype of my last comment.

We could have like below if we want to fulfil a scenario with watched variables and listeners which need to get notified.




class AnyClass

{

     std::map<std::string, pair<void*, Parameter*> > watchedObjects; 

public:

     static void notifier(Parameter* par, unsigned int oldval, unsigned int newval)

     {

          std::map<std::string, std::string>& m = par->myparams;

          std::string watchId = m["WatchId"];

          std::string listenerId = m["ListenerId"];

          CVariableWatch* ptrWatched = (CVariableWatch*)watchedObjects[watchId].first;

          Listener* ptrListener = Listener::getListener(listenerId);

          if (ptrListener != NULL)

              ptrListener->handleUpdateEvent(ptrWatched, oldval, newval);

     }

     static void othernotifier(Parameter* par, unsigned int oldval, unsigned int newval)

     {

          std::map<std::string, std::string>& m = par->myparams;

          std::string watchId = m["WatchId"];

          std::string listenerId = m["ListenerId"];

          AnyOtherClass* ptrWatched = (AnyOtherClass*)watchedObjects[watchId].first;

          Listener* ptrListener = Listener::getListener(listenerId);

          if (ptrListener != NULL)

              ptrListener->handleUpdateEvent(ptrWatched, oldval, newval);

     }

     ~AnyClass() 

     {

         std::map<std::string, pair<void*, Parameter*>::iterator i = watchedObjects.begin();

         for (; i != watchedObjects.end(); ++i) 

         {

              delete (CVariableWatch*)i->second.first;

              delete i->second.second;

         } 

     } 

     

     void watchVariableWatch()

     {

          CVariableWatch* ptrWatched = new CVariableWatch("12345");

          watchedObjects["WatchId"] = "12345";

          Listener::addListener("98765");

          Parameter* par = new Parameter;

          par->myparams["WatchId"] = "12345";

          par->myparams["ListenerId"] = "98765";

          ptrWatched->init(p, notifier);
 

     }

     void watchAnyOtherClass()

     {

          AnyOtherClass* ptrWatched = new AnyOtherClass();

          watchedObjects["WatchId"] = "AOC123";

          Listener::addListener("999999");

          Parameter* par = new Parameter;

          par->myparams["WatchId"] = "AOC123";

          par->myparams["ListenerId"] = "999999";

          ptrWatched->init(p, othernotifier);
 

     }

 

};

Open in new window

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24797100
BTW: This is a variation of what is known as the Observer Pattern.
Hm, this may be what I'm after... At the moment I'm just rewriting the code you gave me going over it fully to make sure I'm understanding this. :)

Only a little bit. It's only that I still don't know what's your final purpose?
My apologies - I'm not terribly good explaining my ideas. :) I'm attempting to make some sort of event sender/handler system, where the event sender doesn't have to know anything other than which function to call when an event is raised.

Do you mean the one with prototype 'void mynotifier(Parameter * par, unsigned int oldval, unsigned int newval);' , or generally?
I'm not too sure at the moment. :( I think if I could explain better what I'm after that would clear things up. :(

Bbs, just going through the code from Rix.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24797347
yes I am Ricky :)

yes the observer pattern is for writing event handling mechanisms so I think we are on the right track.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24797499
Oh man... I'm just not getting this together at all! :( I've had a good look over your code and there are a number of parts that I'm unsure of, so I'm rewriting it slightly in hopes of understanding it better. I have the following code which is without the equivalent of your ExampleBase class as that's the part that I'm struggling with. However the errors I'm getting are:

Error 1:
CEventRaiser<TEventHandlerClass>::eventHandlerClass' uses undefined class 'CExampleClass

Error 2:
CExampleClass' : illegal use of this type as an expression

Error 3:
illegal operation on bound member function expression

I think these errors are to do with no 'in-between' class but I'm unsure how it fits in...

Thanks,
Uni
template<typename TEventHandlerClass>

class CEventRaiser{

public:

	typedef void (TEventHandlerClass::*TEventHandlerMethod)();

	void setListener(TEventHandlerClass *aEventHandlerClass, TEventHandlerMethod aEventHandlerMethod){

		eventHandlerClass=aEventHandlerClass;

		eventHandlerMethod=aEventHandlerMethod;

	}

	void callListener(){

		eventHandlerClass->*eventHandlerMethod;

	}

private:

	TEventHandlerClass eventHandlerClass;    //Error 1.

	TEventHandlerMethod eventHandlerMethod;

};
 
 

class CExampleClass{

public:

	void initialise(){

		eventRaiser.setListener(CExampleClass, &listener); //Error 2 + 3.

	}

	void listener(){

	}

private:

	CEventRaiser<CExampleClass> eventRaiser;

};

Open in new window

0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24797949
I think I've got so trapped up in what I'm after I've been getting a little confused between member methods and objects. I think I've been looking for some way so that each object can have its own member declaration - which couldn't be more mistaken. Objects only have different data of the same set, not differing methods... Things are looking a little clearer after I have looked back on the code and comments you've both provided. :)

Would either you object if I split the points 50-50, as you've both put in a tremendous amount of time and effort into this question to get me where I am?

Thanks,
Uni
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24797954
Fixed. Comments inline...
template<typename TEventHandlerClass>

class CEventRaiser{

public:

	typedef void (TEventHandlerClass::*TEventHandlerMethod)();

	void setListener(TEventHandlerClass *aEventHandlerClass, TEventHandlerMethod aEventHandlerMethod){

		eventHandlerClass=aEventHandlerClass;

		eventHandlerMethod=aEventHandlerMethod;

	}

	void callListener(){

		eventHandlerClass->*eventHandlerMethod;

	}

private:

//	TEventHandlerClass eventHandlerClass;    //Error 1.

	TEventHandlerClass * eventHandlerClass;  // < ------- should be a pointer

	TEventHandlerMethod eventHandlerMethod;

};

 

 

class CExampleClass{

public:

	void initialise(){

//		eventRaiser.setListener(CExampleClass, &listener); //Error 2 + 3.

		eventRaiser.setListener(this, &CExampleClass::listener); // < ------- this pointer / syntax error taking address of member function

	}

	void listener(){

	}

private:

	CEventRaiser<CExampleClass> eventRaiser;

};

Open in new window

0
 
LVL 40

Expert Comment

by:evilrix
ID: 24797998
>> Would either you object if I split the points 50-50
Uri, you split as you see fit. As long as the selected answers are related to the Q and were helpful to you neither Alex nor I will object.

Don't feel pressured to close the Q until you are happy you have a working solution though.. there's no rush, I'm not going anyway (well, maybe to bed but you know what I mean) :)

>> don't suppose that's short for Ricky as in Gervais is it?
Oi, cheeky Muppet! I am much better looking and funnier than he is :)
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24798058
>>Uri, you split as you see fit. As long as the selected answers are related to the Q and were helpful to you neither Alex nor I will object.
:)

I've got quite a number of solutions, many of which I don't fully understand so with the knowledge I've got from this question I'll do a bit of reading and should come out on tops. Besides, I think it would be quite unfair of me to keep this going after all the solutions that have been put forward.

>>Oi, cheeky Muppet! I am much better looking and funnier than he is :)
Hahahaha! If it is any consolation I only enjoyed The Office really, can't say I found his stand-up stuff that funny to be honest. :D

Later pal,
Uni
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24798098
>> I only enjoyed The Office
Me too. Have you seen "Extras", his other show? I love it too :)
http://en.wikipedia.org/wiki/Extras

>> can't say I found his stand-up stuff that funny to be honest.
Me neither. I am a HUGE Eddie Izzard fan!!! :)
0
 
LVL 3

Author Closing Comment

by:Unimatrix_001
ID: 31600049
Alex, Ricky - Thanks to you both. I really wish I could give more than 500 points as it's answers like this and dedication to helping has been well above anything I've received in a long time and deserve much more if it were allowed. Thanks, again for your perseverance. Uni.
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24798165
>>Me too. Have you seen "Extras", his other show? I love it too :)
Ah yes! Extras yeah that was brilliant! :)

>>Me neither. I am a HUGE Eddie Izzard fan!!! :)
Hm, ain't really seen anything really of Eddie Izzard - I'll have to have a little look later! (-:
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24798322
>> Hm, ain't really seen anything really of Eddie Izzard - I'll have to have a little look later! (-:
One of the funniest live shows ever: -

http://www.imdb.com/title/tt0184424/
http://en.wikipedia.org/wiki/Dress_to_Kill

Transcript: http://www.auntiemomo.com/cakeordeath/d2ktranscription.html

Clips from Eddie Izzard's show, "Dressed to Kill".: http://tinyurl.com/mnspeh

Enjoy ;)
0
 
LVL 40

Expert Comment

by:evilrix
ID: 24798330
My favorite sketch: http://tinyurl.com/l2jyk2 :)
0
 
LVL 3

Author Comment

by:Unimatrix_001
ID: 24798336
...and there's the night-time viewing sorted. ;)
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now