The Call Gate Idiom

Quip Card
This post is about what I call the Call Gate idiom.
If you did any programming, you must be familiar with the so-called boolean flags. I’ve been writing C++ for quite some time, and I encounter them very often. Of course, I use flags myself. They are really hard to get away without. However I do find them annoying, and aesthetically unpleasant. Doesn’t this pattern of using the playing_ flag look familiar.


struct player{
  player()
    :playing_(false)
  {}

  ~player()
  {
    stop();
  }

  void play()
  {
    if( playing_ )
      return;

    do_play(); // start playing

    playing_ = true;
  }

  void stop()
  {
    if( !playing_ )
      return;

    do_stop(); // stop

    playing_ = false;
  }

private:
  bool playing_;

  void do_play();
  void do_stop();
};


The basic idea of the call gate idiom is to integrate the function call with a binary flag that indicates whether the gate is open or closed. If the gate is open, the function call ( operator() ) goes through, and gate is closed automatically. If it is closed, operator() does nothing. It's somewhat similar to the AND gate in electronics. In its simplest form, the implementation may look something like the following.

struct gate
{
       typedef boost::function< void () > func;

       gate( func f )
              :open_(false)
              ,f_(f)
       {}

       void operator()()
       {
              if( !open_ )
                     return;
              f_();
              open_ = false;
       }

       void open()
       {
              open_ = true;
       }

       void close()
       {
              open_ = false;
       }

private:
       bool open_;
       func f_;
};


Using the gate type, we can re-write the player sample.


struct player
{
  player()
    :play_( boost::bind(&player::do_play, get_this()) )
    ,stop_( boost::bind(&player::do_stop, get_this()) )
  {
    play_.open(); //open the play gate
  }

  ~player()
  {
    stop();
  }

  void play()
  {
    play_();
    stop_.open(); //open the stop gate
  }

  void stop()
  {
    stop_();
    play_.open(); //open the play gate
  }

private:
  // the player gates
  gate play_;
  gate stop_;

  player* get_this() //to make some compilers happy
  {
    return this;
  }

  void do_play();
  void do_stop();
};


I think it looks much nicer. Even if the user calls play() more than one time in a row, it calls do_play() once, before stop() is called that opens the play gate again. That happens because calling the gate ( calling operator() ) closes the gate automatically. That's consistent with using the playing_ flag.

The sample has only two gates. Let’s add two more, pause and resume.


struct player
{
  player()
    :play_( boost::bind(&player::do_play, get_this()) )
    ,stop_( boost::bind(&player::do_stop, get_this()) )
    ,pause_( boost::bind(&player::do_pause, get_this()) )
    ,resume_( boost::bind(&player::do_resume, get_this()) )
  {
    play_.on(); //open the play gate
  }

  ~player()
  {
    stop();
  }

  void play();
  void pause();
  void resume();
  void stop();

private:
  gate play_;
  gate stop_;
  gate pause_;
  gate resume_;

  player* get_this() //to make some compilers happy
  {
    return this;
  }

  void do_play();
  void do_stop();
  void do_pause();
  void do_resume();
};



To enforce the constraints, the player would have to keep the current state so that the play(), pause(), resume(), stop() methods could enable/disable the corresponding gates based on the state. Keeping and checking the current state isn’t much different from the playing_ flag used in the first sample.
What if we could hard wire the gates? When an open gate is called (operator()), other gates get opened/closed automatically. The modified gate type:

struct gate
{
       typedef boost::function< void () > func;

       gate( func f )
              :open_(false)
              ,f_(f)
       {}

       void operator()()
       {
              if( !open_ )
                     return;
              f_();

              std::for_each(  // open wired gates
on_wires_.begin()
,on_wires_.end()
,boost::mem_fn(&gate::open)
);

              std::for_each( //close wired gates
off_wires_.begin()
,off_wires_.end()
,boost::mem_fn(&gate::close)
);

              open_ = false;
       }

       void open()
       {
              open_ = true;
       }

       void close()
       {
              open_ = false;
       }

       void wire_on( gate& b )
       {
              on_wires_.push_back( &b );
       }
       void wire_off( gate& b )
       {
              off_wires_.push_back( &b );
       }

private:
       typedef std::vector wires;

       wires on_wires_;
       wires off_wires_;

       bool on_;
       func f_;
};


The player type can be written as following, that enforces the constraints of calling do_play(), do_stop(), etc. in the correct states only.


struct player
{
  player()
    :play_( boost::bind(&player::do_play, get_this()) )
    ,stop_( boost::bind(&player::do_stop, get_this()) )
    ,pause_( boost::bind(&player::do_pause, get_this()) )
    ,resume_( boost::bind(&player::do_resume, get_this()) )
  {
    //when play is called, open stop and pause, close resume
    play_.wire_on( stop_ );
    play_.wire_on( pause_ );
    play_.wire_off( resume_ );

    //when stop is called, open play, close pause, resume
    stop_.wire_on( play_ );
    stop_.wire_off( pause_ );
    stop_.wire_off( resume_ );

    //when pause is called, open resume, don't touch other gates
    pause_.wire_on( resume_ );

    //when resume is called, open pause, don't touch other gates
    resume_.wire_on( pause_ );

    play_.open(); //open the paly gate to start
  }

  ~player()
  {
    stop();
  }

  void play()
  {
    play_();
  }
  void pause()
  {
    pause_();
  }
  void resume()
  {
    resume_();
  }
  void stop()
  {
    stop_();
  }

private:
  gate play_;
  gate stop_;
  gate pause_;
  gate resume_;

  player* get_this() //to make some compilers happy
  {
    return this;
  }

  void do_play();
  void do_stop();
  void do_pause();
  void do_resume();
};


No comments:

Post a Comment