?
Solved

Passing pointer to member function

Posted on 2003-03-03
28
Medium Priority
?
1,628 Views
Last Modified: 2012-06-27
I have a class as follows:

class A {
  public:
    A(void (p1*)());
};

The class A has a constructor that accepts a pointer to a function that accepts no parameters and returns void.

In another class:

class B {
  public:
    void X();
    void Y();
};

In method X of class B, I want to construct an instance of class A. I want to pass the pointer to methodY() to class A's constructor. How do I do so?

I can't do this:

A* a = new A(Y);

By the way, I don't want to modify class A's constructor to accept a pointer to a member function.
0
Comment
Question by:yongsing
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 10
  • 6
  • 5
  • +2
28 Comments
 
LVL 9

Expert Comment

by:jasonclarke
ID: 8056184
The simple answer is, how you have it, you do not.

Member function pointers and normal function pointers are just not the same the same thing at all.

Apart from anything else, you must have an object to call a member function.

The simplest solution is to pass a static method pointer - these are the same as normal function pointers - but may not be what you want.

The more complicated way is to use a member function pointer and an object.  The following code demonstrates both approaches:

class B {
public:
    static void X() {}
    void Y() {}
};

class A
{
public:
    A(void (*fp)()) { (*fp)(); }
    A(B* obj, void (B::*fp)()) { (obj->*fp)(); }
};

int main()
{
    A* a = new A(B::X);
    B b;
    A* a2 = new A(&b, B::Y);

    return 0;
}

0
 
LVL 9

Expert Comment

by:jasonclarke
ID: 8056191
> By the way, I don't want to modify class A's
> constructor to accept a pointer to a member function.

I didn't notice this at first.  The only sensible thing to do is to use static methods if this is a requirement.

It is possible to 'hack' a method pointer into a normal function pointer - but if you do the target method must obey certain rules which mean that a static method would do just fine anyway - i.e. it can't be virtual or touch any member data.
0
 
LVL 12

Expert Comment

by:Salte
ID: 8056203
The simple answer is this:

a non-static member function cannot get a regular function pointer variable to point to it. They are simply two different types. It's like stating that you want the integer x to store the string "hello world\n".

So the method B::Y cannot be reached through a regular

void (* foo)();

pointer such as foo above.

If you want to be able to call the method B::Y you must declare the pointer as:

void (B::* foo)();

Further, when you call it you must have an object of type B available:

B b;

b.*foo(); // will call B::Y if foo hold a pointer to B::Y.

The pointer you have declared in A is capable of holding pointers to static - emphasis on _static_ - member functions (of any type) and also on functions that aren't member functions at all. I.e. functions that doesn't require any object of any type. So the method B::Y can never be stored in that pointer, they are simply two different types.

Either change the pointer to be a pointer to member or change Y() to be a static function or move it out of the class.

Alf
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 49

Expert Comment

by:DanRollins
ID: 8056430
If the B that you construct will be the only such B object, then there is a trick that lets you make, for instance B::Y be a static member function but without the limitations.  The trick is to make a global (or static member) "this" pointer which is intitialized by the B constructor.  Then the Y fn used code like:

   pThis->TheNonStaticActualY();

That is useful in some situations, but no good in multithreaded or if you will be creating a bunch of copies of that object.
-- Dan
0
 
LVL 12

Expert Comment

by:Salte
ID: 8056650
It is actually even without limitations if he does something like this:

class B;

class A {
private:
   B * bobj;
   void (* func)(B * bp);
public:
   A(B * bob, int (*func)(B * bptr)) : bobj(bob), bp(bptr)
   {}
   void call_B() { func(bobj); }
};

class B {
private:
   ....
public:

   void NonStaticY() { ... }
   static void StaticY(B * bp) { bp -> NonStaticY(); }
};

B b;
A a(& b, B::staticY);

a.call_B(); // will call the NonStaticY() function.

The point to recognize is this:

if a function is non-static it actually has one more argument than the ones you declare: The 'this pointer' is an argument in itself:

class X {

   void func() { ... }
};

The function func above is actually a function that takes one argument. It appears to be a function that takes no arguments but in reality it does take one argument - the this pointer. It is therefore compatible with a static function that takes an explicite pointer as argument:

class X {
    ....
    void func() { ... }
    static void funcX(X * xp) { xp -> func(); }
    ...
};

Here the funcX and func are compatible, i.e. by the arguments given to funcX you can call func().

This is also the reason why your original code didn't work and why the pointer to members are really very different from other function pointers and why static functions aren't using the pointer to member style but are regular function pointers.

Once you recognize that a non-static function is simply a function that takes an extra argument you can more or less mix static and non-static functions freely:

T * tp;

tp -> func(a, b)

is in some ways equivalent to saying:

Cfunc(tp, a, b)

where Cfunc is a regular C style function or a static member function (static member functions are just like regular C functions). Non-static member functions are just like static member functions (and C style functions) but with one extra argument. This is also reflected in the syntax:

void func() const { ... }

The 'const' here refer to the const ness of the this argument, so this is equivalent to something like:

void Cfunc(const T * this) { ... }

In fact the Cfront program which was an early implementation of C++ to C converter did exactly that.

Alf
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8056667
nice trick.
-- Dan
0
 
LVL 9

Expert Comment

by:jasonclarke
ID: 8057221
The whole problem with any approach like this is this statement:

> By the way, I don't want to modify class A's constructor to accept a pointer to a member function.

This makes anything like this fairly impossible.

There are some neat encapsualtion of member function methods that use functionoids that make this stuff fairly transparent - but anything would require you to change *something* in A.
0
 
LVL 12

Expert Comment

by:Salte
ID: 8057373
Jason,

Personally I really don't understand why people make what you might call "artificial limitations". If they say they don't want to change the constructor to accept a pointer to member function, why is that limitation there? It doesn't appear to make sense to me and when it doesn't make sense it either means that whoever made the statement hasn't told us why it is important or it really isn't important and he just made that limitation because he wanted to be difficult.

It's like "I want to create an object A but without instantiating it and without calling any constructor of A". Why would anyone ever want to do that? What good would it do? What is the PURPOSE?

Programming is mostly about finding solutions to problems, not creating artificial problems just because we want to be difficult. Nobody pays us to be difficult, they pay us to solve problems.

Alf
0
 
LVL 9

Expert Comment

by:jasonclarke
ID: 8057443
There obviously can be valid reasons:  in this case, class A could actually represent some sort of library code for which source is not available - in which case you there is no choice.

I don't think it is for us to say whether the limitation is reasonable or not: thats up to whoever asks the question.  Similarly, it is not reasonable for us to ignore it: we can merely point out alternatives if the limiation is removed and/or question the validity of the assumption.

0
 
LVL 12

Accepted Solution

by:
Salte earned 400 total points
ID: 8058088
Jason,

It is possible you are right but my point is he never stated why we cannot change it. Also, if he doesn't have source code to the class and can't change it, it is possible he cannot even provide that pointer which I required, if so he cannot do what he wants to do. He can still find ways around it but they generally involve storing the 'this' pointer in some global variable and other things. Unless this is the only way out, you really should look for other solutions. However, what solutions are available to him is only known once we know all the unknowns, such as why this limitation is there.

It's just that I frequently see such requirements but they are stated without ever giving a reason why it is so. It COULD be a valid reason but it could also just as well have been "Because I don't know how to handle it in any other case" and the correct solution might rather be to allow for a solution that violates the requirement but it requires the person asking to learn how to handle it in the new situation. Again, we cannot tell which is which unless we know the reasons for such limitations.

It is therefore usually a good idea to give reasons for such limitations when you express them. Some limitations has obvious reasons:

"I need this data to be read from a database and not from a file" is obviuosly a limitation due to the fact that his original data is in a database and not in a file. You could dump the table to a file but it might be more trouble than it is worth, so in such a situation that limitation most likely do not require further explanation.

My point is that his requirement isn't so obvious. It could be a library as you say but in that case it would be very nice to know that. In this case you might end up with something like this:

class A {
private:
   void (* p)();

public:
   A(void (*func)()) : p(func) {}
   void call_func() { p(); }
};

class B {
private:
   ....
public:
   void X() { ... }
};

as stated like this and with that requirement, you simply cannot solve the problem easily, the reason is that method X require an object of type B and the class A has no referene to any B anywhere and neither do call_func(). This does not mean it is impossible but it means any solution is likely to be hairy and undesirable.

If there is only one B object:

B b;

Then you can make a function like this:

void func() { b.X(); }

and func can be given to A's constructor. func could even be a static function in B if you can edit the B class.

If there are more than one B then you have to store the B pointer to use somewhere and if you can't edit A you have to store it outside of A:

B * bobj;

void func() { bobj -> X(); }

Provided bobj have the correct value this function can now be used. However, code like this will break if you have more than one A object and you want each A object to reference a B object. This really associates an A with a B and so the B reference should be stored in A. If you can't edit A you must do something like this:

step 1, create a hash table, associating A's with B.

A a1(func1);
A a2(func2);
B b1;
B b2;
// mymap uses const A & as key and B * as data.
mymap[a1] = & b1;
mymap[a2] = & b2;

If func1 is written something like:
void func1()
{
   B * p = mymap[a1];
   p -> X();
}

Then calling func1 will call b1.X() and so on. However, this is also hairy and troublesome. For example the function func() has no arguments, so when it is called you don't even know which a object activates it. This means that the two a objects must have their own such function, so func1 and func2 must both exist, you can't write a common function func to handle both. This also reveal that the object A hasn't really been written in a smart way. Having a function there that is called without any form of info of which A object calls it means you create troubles for those who wants to use it. A smarter way would be that the function could take an A as argument like this:

class A {
private:
   void (* p)(A * ap);
 ....
};

just this little change would make life easier in that the two a1 and a2 above could share the function:

void func(A * ap)
{
   B * p = mymap[*ap];
   p -> X();
}

so if the code is available, maybe he should reconsider his design of A. If the code for A is not available, then he should rather state that he is unable to modify A in any way, rather than saying he cannot change that pointer to be a pointer to member. That last statement causes us to wonder why we can't do such a change. saying that A is beyond reach would explain more what is going on.

If A cannot change, then if he has only one B object it shouldn't be much problem, if he has several B objects then it is possible that one common B * can do the trick. If he has both several B and several A objects then he might end up having a map as described above. However, these solutions all have increasing difficulty complexity of code and you definitely want to choose the first if you can and only go to the second if you have to and again only choose the map if you have no choice.

If A can change I rather think he should change A than doing all that stuff above and rather allow for pointer to member. If he cannot use pointer to member because he want to allow for many different object types: C, D, E etc and call functions in all of them then he probably need to do something else.

If he uses Borland C++ builder he can use the __closure type which is a pointer to object combined with a pointer to member function or regular pointer to function or static member. A closure would solve his problem very nicely in this case:

class A {
private:
   void (* __closure p)();
public:
   A(void (* __closure func)());
   void call_B() { p(); }
};

class B {
......
public:
   void X();
};

class C {
....
public:
   void Y();
};

B b;
C c;
A ab(b.X);
A ac(c.Y);
ab.call_B(); // call b.X()
ac.call_B(); // call c.Y()

If he doesn't use Borland C++ builder this closure can be faked by a struct or two data values:

template <class T>
class Closure {
private:
   T * pObj;
   void (T::* pFunc)();
};

Note that the Closure is determined by type while the __closure is 'type unrelated'. This means that you have to declare the class A as a templated type also.

template <class T>
class A {
private:
   T * pObj;
   void (T::* pFunc)()
public:
    A(T * po, void (T::* pf)()) : pObj(po), pFunc(pf) {}
    void call_B() { pObj ->* pFunc(); }
};

B b;
C c;
A<B> ab(& b, B::X);
A<C> ac(& c, C::Y);
ab.call_B(); // call b.X()
ac.call_B(): // call c.Y()

Now if you don't like the template and would prefer the same class to be used in both cases that can also be arranged, however it is then typically very compiler specific. Generally you figure out how the this pointer is transferred for your compiler and then call the function with some inline assembly that sets up the this pointer correctly and also sets up the parameters to the function properly and then call the function via the pointer directly by a CALL instruction. Since you're using inline asembly the compiler won't complain about function prototype not matching etc etc. However, this is very ugly coding and should again be avoided if possible.

So yes, there are solutions to the problem but which solution to select depends very much upon what is available and what is possible to arrange.

Since we don't know it leaves us asking those questions and the requirement seem artificial.

Alf
0
 
LVL 5

Expert Comment

by:Kocil
ID: 8062577
Excuse me, I just want to say that ...

using a POINTER TO FUNCTION in C++ is a BAD PRACTICE.
also POINTER TO FUNCTION is NOT a POINTER TO METHOD.

Some tricks (like Salte's) work, but the clean methods
would be:
1. Using functor (Only in Borland C++ Builder)
2. Using listener pattern.

Method 1 is straight forward, just read the Borland C++ manual.

Method 2 is something like this

class Listener {
  virtual void CallMe() = 0;
};

classs A {
  Listener& L;
  A (Listener &L) {this.L = L;};
  UseListener() {L.CallMe();}
};

class B: Listener {
  B();
  void CallMe() {cout << "I'm called\n";}
};

main()
{
   B b;
   A a(B);

   A.UseListener();
}

0
 
LVL 12

Expert Comment

by:Salte
ID: 8062999
Problem with the __closure is that it is not portable. Not quite sure what you refer to as 'functor'. functors are normally classes that act like functions - as used by the STL. They are standard and are not specific to Borland C++.

Functors are classes that perform functions and that is what they do. The idea is that a class is a type and can be a template argument and so you can perform the function via a template. You can overload the () operator and the class appear syntactically as a function.

Also, pointer to function maybe "bad practice" in your eyes but virtual functions are really nothing other than pointers to functions. So obviously they must be good for something.

Personally I think pointers to functions and pointers to methods (yes, they are different) are good whenever you use them where appropriate. Some places a virtual function call s appropriate but not always. For example if he cannot change the class A then he cannot make the argument become a class with virtual function instead of a function pointer.

Once in a while there are these religious wars about which language is better than which language and what is "good" or "bad" practice.

In my experience it doesn't matter what is "good" or "bad" practice in the sense that someone has decided that it's their opinion that X is "good" practice while Y is "bad". In some situations Y might be better than X or Y gets the job done while X cannot get the job done unless you really stretch it to its limit and what was supposed to be readable and clear becomes very hard to understand.

What is "good" in my opinion is usually what works and get the job done with as little fuzz as possible and is aimed directly at solving the problem at hand. Making up a lot of extra stuff might work and might even be the better solution at other times but it doesn't mean it is the best solution at all times.

Alf
0
 
LVL 9

Author Comment

by:yongsing
ID: 8063031
Thanks everyone for your response. I do have a problem understanding some of your codes though. I'm not as familiar with C++ as I am with Java.

I like the listener pattern described by Kocil. In fact, this is how it's done in Java.

I'm using C++ Builder V5.0 to write my program.

Kocil:

Suppose that B already inherits from a class (TForm), can I use multiple inheritance to achieve what you suggested?

class B: public TForm, Listener {
  public:
    __fastcall B();
    void CallMe() {cout << "I'm called\n";}
};

I'm not clear about the exact sytax, so I'll have to check it out.

One more thing, what is this functor you suggested? I use the C++ Builder's online help but can't seem to find it.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8063039
yongsing,
Are you listening?

In my experience there are two cases where persons ask this question.  
1) They want to provide a TimerProc callback for a windowless SetTimer() API call. ...or....
2) They are trying to figure out how to pass a ThreadProc itn to CreateThread() API.

Are either of these you final goal?

-- Dan
0
 
LVL 5

Expert Comment

by:Kocil
ID: 8063121
Yeah, you can use multiple inheritance.
Functor = closure. My apologize :)

0
 
LVL 9

Author Comment

by:yongsing
ID: 8063262
Hi Kocil, for the class A's constructor, I define it as:

A::A(TComponent* Component, Listener& L) {
  this->Component = Component;
  this.L = L;

  // some other stuff here.
}

The compiler complains that the reference L is not initialized. What's wrong?

To DanRollins:

Those were not my goals.

I'm writing a class, and classes that use this class need to provide event handlers methods which the class will call when the events occur.
0
 
LVL 12

Expert Comment

by:Salte
ID: 8063267
Dan,

if either of those are the final goals then the funciton pointer shouldn't be:

void (*)();

At least for the create thread - and I believe also for the timer it typically takes a void * argument which is usually used to pass an object or other instance information - read "this pointer".

The problem with a function pointer like:

void (*)();

is that the function called receives absolutely no information about who called it and why. This means the function must itself carry that information. This means that if you want another timer or thread you have to have a different function for that and so you need separate functions for each thread, even though they are all very similar:

void funcA()
{
   A a;
   a.foo();
}


void funcB()
{
   B b;
   b.bar();
}

etc..

It would be much easier if you could have something like:

class Base {
public:
   virtual void mainfunc() = 0;
};

class ThreadA : public Base {
public:
   virtual void mainfunc() { ...do foo... }
};

class ThreadB : public Base {
public:
   virtual void mainfunc() { ...do bar... }
};

However, this requires the thread real main function gets a pointer to either a ThreadA or ThreadB object:

void func(void * ptr)
{
   Base * bp = reinterpret_cast<Base *>(ptr);
   bp -> mainfunc();
}

Now, this func can be given as argument to the class A but it requires the function pointer to be:

void (*)(void *)

and not

void (*)()

However, as yongsing says he liked that listener method which is basically the same as I showed above - although I have never heard the term "listener" for this before - I interpret that as he really has the ability to change the type of that pointer at will and given what I said previously that a method pointer really is a function pointer with an argument you might as well change the above:

void (*)(void *)

to use

void (*)(Base *)

instead (avoid the cast) and from there you get:

void (Base::*)()

Which is essentialy the same type except that it is a pointer to a member of class Base. Now the mainfunc can be given directly as the member of the class A. However, if it is a thread function or something you probably need that first variant with void (*)(void *) anyway as that is the function pointer that a CreateThread call would accept.

Actually, the start function is supposed to also have a return value which act as the exit code to the thread, so the real pointer to function should be something like:

int (*)(void *)

so class A should be something like:

class A {
private:
   int (* p)(void * d);

   static int start(void * d);

public:
   A(int (* func)(void * d)) : p(func) {}
   int start();
};

// This function is called outside the timer or thread
// and typically execute CreateThread or something like
// that.
int A::start()
{
   // create thread or timer or whatever.
   // use 'this' as the argument for parameter
   // use the static A::start(void * p)
   // as the start function or timer function.
}

// This function is called by the timer or in the context
// of the thread.

// static
int A::start(void * d)
{
   A * q = reinterpret_cast<A *>(d);
   // q is now like the this pointer
   // if you want to make it a real this pointer by
   // calling a (possibly virtual) member function
   // at this point to do the job of the timer/thread.
   q -> do_the_job();
   // or you can call p directly:
   return q -> p(d);
}

As seen from the above the start function stored in p could just as well have been a pointer to member function:

class A {
public:
   int (B::* pf)();
   B * p;

   static int start(void * d);
public:
   A(B * ob, int (B::* pfun)()) : pf(pfun), p(ob) {}
   int start();
};

the non-static A::start would be almost identical to the previous example since it uses the static start function as the function to call.

The static A::start would be slightly different:

int A::start(void * d)
{
   A * bp = reinterpret_cast<A *>(d);
   bp -> p ->* (bp -> pf)();
}
Again the pointer to member could actually point to a virtual function and as long as the constructor was called something like:

A a(bptr, & bptr -> func);

then it would work fine even if func was a virtual function declared as:

class B {
public:
   virtual int func();
};

as long as the bptr pointed to a real object which was a subclass of B and then the correct function would be chosen and given as argument to A's constructor.

This also includes the case that func is pure virtual.

I personally think that the Borland C++ __closure is a good  idea and should have made it to the standard C++b C# delegates are similar to the __closure.

Alf
0
 
LVL 9

Author Comment

by:yongsing
ID: 8063269
Actually, I wrote it as follows, but the compiler complains that L is not initialized.

A::A(TComponent* Component, Listener& L) {
 this->Component = Component;
 this->L = L;

 // some other stuff here.
}
0
 
LVL 12

Expert Comment

by:Salte
ID: 8063329
I suspect that the Listener in the class is of reference type.

Reference types are special, you cannot ever assign to them, you can only initialize them.

int y;
int & x = y;  // initialization.

x = 5;
looks like an assignment to x but it isn't, x still refers to y. The assignment is an assignment to what x refers to, i.e. y. It is y that changes value to 5 and not x.

The result is that the assigment:

this -> L = L;

doesn't work, you must initialize the member and not assign to it. Use initialization list:

A::A(TComponent* aComponent, Listener& anL)
  : Component(aComponent), L(anL)
{}

Now it should work.

Alf
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8063358
Why not simply provide a virtual member function and let the people who use your class override it?

-- Dan
0
 
LVL 12

Expert Comment

by:Salte
ID: 8063363
Actually yongsing,

from your description of event handlers and stuff, it really begs for pointer to member functions. Events are typically objects and so pointer to member functions stands out as the 'obvious' alternative. Yes a listener (actually a bad name for this situation) can do it but unless the listener class is a baseclass for all the events and it has the event handling as the virtual function you call it really becomes an added artificial construction that you would be better without.

If it is, then it probably shouldn't be called listener but rather be called something like 'Event' and then have a virtual function handle_event() or some such:

class Event {
public:
   virtual void handle_event(...);
};

and then your class would call the function to fire the event.

This has the drawback (and a listener which this really is has the same) that you have to have a different class for each type of handler. You can't have one event with two different ways of handling.

It really begs for a __closure data type (which would solve the drawback mentioned above) but if you don't run Borland C++ builder you can do something similar by having:

template <class T>
struct Closure {
   T * obj;
   int (T::* func)(....args...);

   Closure(T * obj_, int (T::* func_)(...args..))
   : obj(obj_), func(func_)
   {}

   int operator ()(...args...)
   { return obj ->* func(....args...); }
};

This isn't as nice as borland closures, the template would be specific for a specific argument list and return type. The return type could be a second template argument but the argument list would be harder. However, if the argument list would remain constant this would suit your purposes.

Closure<A> x(aptr, aptr -> func);
int y = x(...args...); // call via the closure.

a.func could easily be a virtual function and the correct virtual function based on the object pointed to by aptr will be used.

Alf
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8063394
Or handle it similar to the way WinInet, and other Windows do it... provide a method:
    A::SetCallBack( pointer-to-function, cookie_of_somekind )

and let the caller work out how to handle passing in a pointer-to-member (the caller is best able to know what technique will work for him).

-- Dan
0
 
LVL 9

Author Comment

by:yongsing
ID: 8063408
Salte, the __closure type solved my problem. Thanks a lot.

I would like to thank the others for trying to help me too.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8065736
Or instead of having the caller pass in a pointer-to-function, have the caller pass in a pointer-to-object.  Define a simple base object that has just the one member function.

Or have the caller pass in an HWND, then you can use SendMessage or PostMessage in your callbacks.

-- Dan
0
 
LVL 12

Expert Comment

by:Salte
ID: 8065995
Dan,

the possibilities are endless, the list you start there never ends....

Alf
0
 
LVL 5

Expert Comment

by:Kocil
ID: 8070028
Use closure, thats the easiest and most natural way in Borland C++ Builder.

Listener pattern is perfect for Java, but looks clumsy for C++.


0
 
LVL 12

Expert Comment

by:Salte
ID: 8070378
I wouldn't say clumsy, it has its uses but it also has its limitations:

The listener function is a virtual function of a derived class of the base class. If you for whatever reason cannot let the class be derived from that base you have to handle it by an additional class which sole purpose is to be the listener for your application:

class Base {
public:
   virtual void listener() = 0;
};

class Derived : public Base {
private:
   RealClass * rcptr;
public:
   Derived(RealClass * rc) : rcptr(rc) {}
   virtual void listener() { rcptr -> listener(...); }
};

This technique must also be used if the real class listener has different arguments than the Base::listener() function.

This might look clumsy and probably using an interface would be more elegant but C++ doesn't have interfaces.

In yongsing's situation where he is using Borland the __closure is the obvious solution to his problems I think.

I do believe that __closure should have been a standard part of C++, you can semi-fake it with templates and pointer to member functions but it isn't a real substitute.

Similarly I believe C++ should have interfaces.

I do hope for a update on the C++ standard sne of these days where these things get a place. I also then hope they revise the iostream library, there are many places where there are room for improvement in that library.

Alf
0
 
LVL 9

Expert Comment

by:jasonclarke
ID: 8070456
> This might look clumsy and probably using an interface
> would be more elegant but C++ doesn't have interfaces.

I don't think it is clumsy at all - if you extend the class a little using templates, then you can encapsulate a callback type mechanism fairly efficiently:


class AbstractCallback  {
public:
    virtual void operator()() = 0;
};

template <typename T>
class Callback : public AbstractCallback {
public:
    typedef void (T::*CBMethod)();
    Callback(T * rc, CBMethod cb) : rcptr(rc), cbptr(cb) {}
    virtual void operator()() { (rcptr->*cbptr)(); }
private:
    T* rcptr;
    CBMethod cbptr;
};

This type of scheme is far more flexible than a simple 'listener' type approach - a class can simply have multiple 'listening' methods and subscribe easily to multiple event sources.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
Suggested Courses

762 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