#include <iostream>
using namespace std;

//  Brief:  Demonstrating C++ Callbacks with member functions points.

//  Note:  Templates implement callbacks without the pointer indirection.

class f1
{
public:

  void cat()
    { cout << "cat" << endl; }
  void cat2()
    { cout << "cat2" << endl; }

};




template< typename T >
void callback(T & x, void ( T::*p )()  )
{
  (x.*p)();
}

void test01(int argc, char** argv)
{
  cout << "Demo the use of member points" << endl << endl;

  f1 f;

  callback(f, &f1::cat);
  callback(f, &f1::cat2);
}


// Fixed function name.
template< typename T >
void callback2( T & x)
{
  x.cat();
}

void test02(int argc, char** argv)
{
  cout << "Demo fixed names," << endl;
  cout << "  overloading operators is a fixed name strategy" << endl << endl;
  f1 f;
  callback2(f);
}


class cat3
{
public:

  void cat()
    { cout << "cat3" << endl; }

};


void test03(int argc, char** argv)
{
  cout << "Demo fixed names with Composition - Magic" << endl;
  cout << "STL is built on this strategy" << endl << endl;

  f1 f;
  cat3 c;

  callback2(f);
  callback2(c);
}

typedef long int const lintc;
typedef long int lint;

class multiplier;

//typedef long int const (multiplier::*pf)(long int const );

class multiplier
{
public:

  //typedef lintc (*pf)(lintc);
  
  long int const (multiplier::*pf )(long int const);

  void set2()
    { pf = & multiplier::multiplier2; }
  void set4()
    { pf = & multiplier::multiplier4; }
  void set8()
    { pf = & multiplier::multiplier8; }

  lintc multiplier2(lintc x)
    { return x * 2; }

  lintc multiplier4(lintc x)
    { return x * 4; }

  lintc multiplier8(lintc x)
    { return x * 8; }

  multiplier()
    { pf = & multiplier::multiplier2; }

  lintc eval(lintc x)
    { return (this->*pf)(x); }

};

void test04(int argc, char** argv)
{
  cout << "Flat Polymorphism" << endl;

  multiplier m;

  cout << m.eval(3) << endl;

  m.set4();
  cout << m.eval(3) << endl;

  m.set8();
  cout << m.eval(3) << endl;

}


int main(int argc, char** argv)
{
  test04(argc,argv);

  return 0;
}




