Channels ▼
RSS

Parallel

C++ and format_iterator


Version 1 Implementation: Test-Driven Development

I implemented the first version of format_iterator using test-driven development principles, in test-first mode. I confess that I anticipated that I would be able to write an article about its development, and implementing in this way would allow me to explain output iterator implementation by example, relying largely on the code to explain. So, that's what I'll do.

Assume any one of the uses of the iterator shown above. Compiling the code gives lots of errors about format_iterator not existing. Listing 1 shows the first iteration, the class skeleton that defines the appropriate member types for an output iterator. As explained in detail in Extended STL, Volume 1, an output iterator should have an iterator_category of std::output_iterator_tag, and all other members (difference_type, pointer, reference, and value_type) should be void; the type generator std::iterator is used to define them.


#include <stlsoft/util/std/iterator_helper.hpp>
namespace fastformat
{
  class format_iterator
    : public std::iterator<std::output_iterator_tag, void, void, void, void>
  {
  };

} /* namespace fastformat */

Listing 1

Listing 2 shows the next iteration, which changes the class name to format_output_iterator; format_iterator is now a creator function (a la std::make_pair()). This is because the iterator class will need to be a template, and using a creator function means that the compiler will automatically deduce the specialising types, saving us from having to explicitly specify them.


template <typename S, typename F>
class format_output_iterator
  : public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
  typedef format_output_iterator<S, F>  class_type;
public:
  format_output_iterator(S& sink, F const& format);
};
template <typename S, typename F>
inline format_output_iterator<S, F> format_iterator(S& sink, F const& format)
{
  return format_output_iterator<S, F>(sink, format);
} 

Listing 2

Next, the compiler will complain that format_output_iterator<> does not support the dereference and increment operators. Listing 3 shows the addition of these in the next iteration. Note the use of the Dereference Proxy pattern, which proscribes fatuous (and non-portable) syntactic misuse of output iterator instances.


template <typename S, typename F>
class format_output_iterator
  : public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
  typedef format_output_iterator<S, F>  class_type;
private:
  class deref_proxy;
  friend class deref_proxy;
public:
  format_output_iterator(S& sink, F const& format);
private:
  class deref_proxy
  {
  public:
    deref_proxy(format_output_iterator* it)
      : m_it(it)
    {}
  private:
    format_output_iterator* const m_it;
  };
public:
  deref_proxy operator *()
  {
    return deref_proxy(this);
  }
  class_type& operator ++()
  {
    return *this;
  }
  class_type operator ++(int)
  {
    return *this;
  }
};

Listing 3

Now the compiler will complain that the format_output_iterator<>::deref_proxy type does not support the assignment operator, leading us to Listing 4.


template <typename S, typename F>
class format_output_iterator
  : . . .
{
  . . .
private:
  class deref_proxy
  {
  public:
    deref_proxy(format_output_iterator* it);
  public:
    template <typename A>
    void operator =(A const& value);
  private:
    void operator =(deref_proxy const&);
    . . .
  };
public:
  deref_proxy operator *()
  {
    return deref_proxy(this);
  }
  . . .
};

Listing 4

Now we get a linker problem, because the dereference proxy type's assignment operator template is not defined. We implement it in terms of the new invoke_() method of the iterator class template (see Listing 5). Hopefully, in this step you can see how the format iterator works: it's implemented in terms of FastFormat's Format API function fmt() [4].


template <typename S, typename F>
class format_output_iterator
  : . . .
{
  . . .
private:
  class deref_proxy
  {
  public:
    deref_proxy(format_output_iterator* it);
  public:
    template <typename A>
    void operator =(A const& value)
    {
      m_it->invoke_(value);
    }
  private:
    void operator =(deref_proxy const&);
  private:
    format_output_iterator* const m_it;
  };
  template <typename A>
  void invoke_(A const& value)
  {
    fmt(m_sink, m_format, value);
  }
public:
  deref_proxy operator *()
  {
    return deref_proxy(this);
  }
  . . .
};

Listing 5

Naturally, the compiler now complains that it does not know what m_sink and m_format are. These members are defined in Listing 6.


template <typename S, typename F>
class format_output_iterator
  : . . .
{
public:
  typedef format_output_iterator<S, F>    class_type;
  typedef S                               sink_type;
  typedef F                               format_type;
private:
  typedef std::basic_string<ff_char_t>    string_type_;
private:
  class deref_proxy;
  friend class deref_proxy;
public:
  format_output_iterator(sink_type& sink, format_type const& format)
    : m_sink(sink)
    , m_format(::stlsoft::c_str_data(format), ::stlsoft::c_str_len(format))
  {}
private:
  class deref_proxy
  {
    . . .
  };
  template <typename A>
  void invoke_(A const& value);
public:
  deref_proxy operator *();
  class_type& operator ++();
  class_type& operator ++(int);
private: // Fields
  sink_type&         m_sink;
  string_type_ const m_format;
};

Listing 6

Believe it or not, this series of steps was carried out in just a couple of hours. I've found this to be one of the effects of a test-driven approach. In suitable cases it can result in very fast development times. (This may be because, being guided by the objective tests, I have much more confidence, as well as making fewer missteps.) Of course, I had the guidance of the relevant chapters of Extended STL to help. (Not to mention the obvious advantage of having written it, of course!)


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video