views:

51

answers:

2

I am new to pointer to member functions, and I would like to know their pros and cons.

Specifically, consider this:

#include <iostream>
#include <list>

using namespace std;

class VariableContainer;

class Variable
{
public:
  Variable (int v) : value (v), cb_pointer (0) {}
  ~Variable () {}

  void SetCallback (void (VariableContainer::*cb)(int)) {
    cb_pointer = cb;
  }
  void FireCallback (void) {
    /* I need a reference to a VariableContainer object! */
  }

private:
  int value;
  void (VariableContainer::*cb_pointer) (int);
};

class VariableContainer
{
public:
  VariableContainer () {}
  ~VariableContainer () {}

  void AddVar (Variable &v) {
    v.SetCallback (&VariableContainer::Callback);
  }
  void Callback (int v) { cout << v << endl; }
};

int main ()
{
  Variable v (1);
  VariableContainer vc;

  vc.AddVar (v);
  v.FireCallback();

  return 0;
}

As stated in the comment, to trigger the callback (FireCallback) I need some reference to an existing VariableContainer object, which should be provided as an additional argument of VariableContainer::AddVar (...). Now:

  1. Should I use the pointer to member function? Or should I call directly Callback (...) (since I have a pointer to the VariableContainer object)?
  2. What are the advantages and drawbacks of each solution?

TIA, Jir

+1  A: 

It depends on what your needs are here.

If you only have one callback function, and you always know its name, you might as well call it directly. It's simpler, easier to understand, and doesn't get extra variables involved.

Member function pointers would come in useful if you need the ability to call one of several callbacks depending on configuration: You would set the callback member function pointer to the actual callback you wish to call, and then that is used to select which callback to execute.

Mark B
Useful explanation as well. Thank you!
Jir
+2  A: 

Depending on what will vary in the future, you can decide:

  • Other functions will be called by the variable's FireCallback => a pointer-to-mf may be useful, but other mechanisms are more c++ish
  • Only the 'Callback' function is going to be called => stick with calling arg->CallBack() directly.

A possible solution to the problem is using an interface layer: this is no more than an implementation of the Observer pattern. This is a little more OO, hence verbose, but the syntax is way easier.

class Observer {
public:
    virtual ~Observer(){};
    virtual void callback( int v ) = 0;
};

// actual implementation
class MyCallbackObserver : public Observer {
    virtual void callback( int v ) { std::cout << v << std::endl; }
    void some_other_method( int v ) { std::cout << "other " << v ; }
};

And your Variable class would have a container full of observers:

class Variable {
public:
   std::vector<Observer*> observers; // warning: encapsulation omitted
   void FireCallback(){
      // assuming C++ 0x
      for( auto it : observers ) {
        (*it)->Callback( value );
      }
   }

If other functions need to be called on the same object, you may introduce a wrapper:

class OtherCaller: public Observer {
 public:
     MyObserver* obs;
     virtual void callback( int v ) { obs->some_other_method( v ); }
}

And add it to the collection:

Variable var;
MyObserver m;
OtherCaller mo;
mo.obs = &m;

var.observers.push_back(&m);
var.observers.push_back(&mo);

var.FireCallback();
xtofl
Concise, though thorough, explanation. Thanks!Now I see that a direct call is simpler but poorly scalable, contrarily to PMFs.Also thanks for pointing out the Observer pattern (which I wasn't aware of before).
Jir