Dereference Proxy Pattern
[Patterns]

    Proscribing Fatuous Output Iterator Syntax.

Many common implementations of output iterator classes provide an assignable dereference (i.e. the result of *o is an lvalue) by having operator *() return a reference to the iterator type, and defining an operator =() that takes the value type. The following code illustrates this:

  template <typename T, . . . >
  class acme_output_iterator
  {
  public: // Member Types
    typedef T                                 value_type;
    typedef acme_output_iterator<T, . . . >   class_type;

  public: // Construction, etc.
    . . .

  public: // Output Iterator methods
    class_type &operator ++();
    class_type &operator ++(int);
    class_type &operator *()
    {
      return *this;
    }
    class_type &operator =(value_type const &value)
    {
      . . . // Do the 'output' operation here
    }

  private: // Members, Implementation, etc.
    . . .
  };

Although this technique is simple, succinct and efficient, it facilitates semantically bogus expressions. Consider the following sample code, using a specialisation of the putative iterator type shown above:

  typedef acme_output_iterator<int, . . .>  iterator_t;
  typedef std::vector<int>                  ints_t;

  ints_t         ints(. . . );

  iterator_t     o(std::cout, "\n");

  std::copy(ints.begin(), ints.end(), o); // This is fine. It outputs all the elements in 'ints' to std::cout

  *o++ = 10; // This is fine. It outputs the integer '10' to std::cout

  o = 11; // This is bogus, but works. It outputs the integer '11' to std::cout!

Think it queer if you will, but I don't like that kind of thing. Thus was born the Dereference Proxy pattern. An output iterator using this pattern defines a member class template deref_proxy, whose job is to supply the assignment operator. Rewriting the above iterator to use the Dereference Proxy pattern would be as follows:

  template <typename T, . . . >
  class acme_output_iterator
  {
  public: // Member Types
    typedef T                                 value_type;
    typedef acme_output_iterator<T, . . . >   class_type;
  private:
    class deref_proxy;
    friend class deref_proxy;

  public: // Construction, etc.
    . . .

  public: // Output Iterator methods
    class_type &operator ++();
    class_type &operator ++(int);
    deref_proxy operator *()
    {
      return deref_proxy(this);
    }
    // NOTE: No more assignment operator

  private: // Dereference Proxy pattern implementation
    class deref_proxy
    {
    public:
      deref_proxy(concatenator_iterator_type *it)
        : m_it(it)
      {}

    public:
      void operator =(value_type const &value_type)
      {
        m_it->invoke_(value_type);
      }

    private:
      concatenator_iterator_type  *const m_it;

    // Not to be implemented
    private:
      void operator =(deref_proxy const &);
    };

    void invoke_(value_type const &value_type)
    {
      . . . // Do the 'output' operation here
    }

  private: // Members, Implementation, etc.
    . . .
  };

To be sure, there's quite a bit more code, but expressions of the pattern all take the same, well tested form, the correctness (or lack) of the implementation can be verified easily and definitively, and once written it is not changed.

Now we receive the (hopefully!) looked-for compile error in the previous example code:

  typedef acme_output_iterator<int, . . .>  iterator_t;
  . . .

  iterator_t     o(std::cout, "\n");

  o = 11; // Compiler error. Good!

Components in the STLSoft libraries that use the Dereference Proxy pattern include:

Note:
There's nothing about the pattern that restricts it to output iterators. It may be applicable to any type that supports the dereference operator (operator *()).
Remarks:
The Dereference Proxy pattern is discussed in part 3 of the forthcoming book Extended STL, along with a great many other techniques utilisied throughout the STLSoft libraries.


Generated on Thu Jun 10 08:58:21 2010 for STLSoft by  doxygen 1.5.6