// Purpose.  Visitor (double dispatch)
//
// Discussion.  On the left, the State derived classes must query the type of
// the Cmd objects they receive, in order to identify what the next course of
// action is.  "case" stmts are always a maintenance headache.  On the right,
// we have recognized that what we really want to do is "dispatch" based on
// the type of TWO objects, (a State object and a Cmd object).  The call to
// accept() discriminates the type of the State object that is being messaged,
// and then the call to visit() discriminates the type of the Cmd object
// (while passing the type of the State object).  If new Cmd classes are
// added, no change whatsoever is necessary in the code of the State classes.
// If new State classes are added, then every Cmd class must be changed, and
// Visitor is NOT the right approach to take.

 #include <iostream.h>                   #include <iostream.h>
 int  current = 0;                       int  current = 0;
 enum CmdTyp { OnT, OffT };              class One;  class Two;

 class Cmd { public:                     class Cmd { public:
    virtual CmdTyp typ() = 0;               virtual void visit( One* ) {
 };                                            cout << "ERROR\n"; }
 class On : public Cmd { public:            virtual void visit( Two* ) {
    CmdTyp typ() { return OnT; }               cout << "ERROR\n"; }
 };                                      };
 class Off : public Cmd { public:        class On : public Cmd { public:
    CmdTyp typ() { return OffT; }           void visit( One* ) {
 };                                            current = 1;
                                               cout << "One,On => Two\n"; }
 class State { public:                      void visit( Two* t ) {
    virtual void process( Cmd* c ) {           Cmd::visit( t ); }
       cout << "ERROR\n"; }              };
 };                                      class Off : public Cmd { public:
 class One : public State { public:         void visit( One* o ) {
    void process( Cmd* c ) {                   Cmd::visit( o ); }
       if (c->typ() == OnT) {               void visit( Two* ) {
          current = 1;                         current = 0;
          cout << "One,On => Two\n"; }         cout << "Two,Off => One\n"; }
       else if (c->typ() == OffT)        };
          State::process( c );
    }                                    class State { public:
 };                                         virtual void accept( Cmd* c ) = 0;
 class Two : public State { public:      };
    void process( Cmd* c ) {             class One : public State { public:
       if (c->typ() == OnT)                 void accept( Cmd* c ) {
          State::process( c );                 c->visit( this ); }
       else if (c->typ() == OffT) {      };
          current = 0;                   class Two : public State { public:
          cout << "Two,Off => One\n"; }     void accept( Cmd* c ) {
    }                                          c->visit( this ); }
 };                                      };

 State* states[] = { new One, new Two }; State* states[] = { new One, new Two };

 void main( void )                       void main( void )
 {                                       {
    Cmd* c[] = { new Off,                   Cmd* c[] = { new Off,
         new On, new Off, new Off, 0 };          new On, new Off, new Off, 0 };
    for (int i=0; c[i]; i++)                for (int i=0; c[i]; i++)
       states[current]->process( c[i] );       states[current]->accept( c[i] );
 }                                       }

 // ERROR                                // ERROR
 // One,On => Two                        // One,On => Two
 // Two,Off => One                       // Two,Off => One
 // ERROR                                // ERROR

