    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Code Critique Competition 103</title>
        <link>https://members.accu.org/index.php/articles/2334</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>




<div class="xar-mod-head"><span class="xar-mod-title">Programming Topics + CVu Journal Vol 28, #6 - January 2017</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c65/">Programming</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c77/">CVu</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c369/">286</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/c65-369/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/articles/c65+369/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Code Critique Competition 103</h1>
<p><strong>Author:</strong>&nbsp;Martin Moene</p>
<p>
<strong>Date:</strong> 03 January 2017 21:20:42 +00:00 or Tue, 03 January 2017 21:20:42 +00:00</p>
<p><strong>Summary:</strong>&nbsp;Set and collated by Roger Orr. A book prize is awarded for the best entry.</p>
<p><strong>Body:</strong>&nbsp;<p class="EditorIntro">Please note that participation in this competition is open to all members, whether novice or expert. Readers are also encouraged to comment on published entries, and to supply their own possible code samples for the competition (in any common programming language) to scc@accu.org.</p>

<p class="EditorIntro">Note: If you would rather not have your critique visible online, please inform me. (Email addresses are not publicly visible.)</p>

<h2>Last issueâ€™s code</h2>

<p class="blockquote">I am trying to parse a simple text document by treating it as a list of sentences, each of which is a list of words. I'm getting a stray period when I try to write the document out:</p>

<p style="margin-left:1em">-- sample.txt --</p>
<p style="margin-left:1em">This is an example.</p>
<p style="margin-left:1em">It contains two sentences.</p>
	 
<pre class="programlisting">
     $ parse &lt; sample.txt
     This is an example.
     It contains two sentences.
     .</pre>
	 
<p class="blockquote">Can you help fix this?</p>

<p>Listing 1 contains <span class="filename">parse.cpp</span>.</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
#include &lt;iostream&gt;
#include &lt;sstream&gt;
#include &lt;memory&gt;
#include &lt;string&gt;

// pointer type
template&lt;typename t&gt; using ptr =
  std::shared_ptr&lt;t&gt;;

// forward declarations
struct document;
struct sentence;
struct word;
document read_document(std::istream &amp; is);
sentence read_sentence(std::istream &amp; is);
void write_document(std::ostream &amp; os,
  document const &amp; d);
void write_sentence(std::ostream &amp; os,
  sentence const &amp; s);

// A document is a list of sentences
struct document
{
  ptr&lt;sentence&gt; first_sentence;
};
// A sentence is a list of words
struct sentence
{
  ptr&lt;sentence&gt; next_sentence;
  ptr&lt;word&gt; first_word;
};
struct word
{
  ptr&lt;word&gt; next_word;
  std::string contents;
};

// read a document a sentence at a time
document read_document(std::istream &amp; is)
{
  sentence head;
  auto next = &amp;head;
  std::string str;
  while (std::getline(is, str, '.'))
  {
    std::istringstream is(str);
    ptr&lt;sentence&gt; s(new sentence(
      read_sentence(is)));
    next-&gt;next_sentence = s;
    next = s.get();
  }
  document d;
  d.first_sentence = head.next_sentence;
  return d;
}

// read a sentence a word at a time
sentence read_sentence(std::istream &amp; is)
{
  word head;
  auto next = &amp;head;
  std::string str;
  while (is &gt;&gt; str)
  {
    ptr&lt;word&gt; w(new word{nullptr,str});
    next-&gt;next_word = w;
    next = w.get();
  } 
  sentence s;
  s.first_word = head.next_word;
  return s;
}

// write document a sentence at a time
void write_document(std::ostream &amp; os,
  document const &amp; d)
{
  for (auto s = d.first_sentence; s;
       s = s-&gt;next_sentence)
  {
    write_sentence(os, *s);
  }
}
// write sentence a word at a time
void write_sentence(std::ostream &amp; os, sentence const &amp; s)
{
  std::string delim;
  for (auto w = s.first_word; w;
       w = w-&gt;next_word)
  {
    std::cout &lt;&lt; delim &lt;&lt; w-&gt;contents;
    delim = ' ';
  }
  std::cout &lt;&lt; '.' &lt;&lt; std::endl;
}

int main()
{
  document d(read_document(std::cin));
  write_document(std::cout, d);
}
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 1</td>
	</tr>
</table>

<h2>Critique</h2>

<h3>Paul Floyd</h3>

<p>First, letâ€™s see what happens when the code is compiled. Firstly clang++:</p>

<pre class="programlisting">
clang++ -g -Wall -Wextra -std=c++11 -stdlib=libc++ -pedantic -o cc102 cc102.cpp 
cc102.cpp:89:36: warning: unused parameter 'os' [-Wunused-parameter]
void write_sentence(std::ostream &amp; os, sentence const &amp; s)
                                   ^</pre>

<p>Next, Oracle CC 12.5:</p>

<pre class="programlisting">
CC +w2 -g -std=c++14 -o cc102 cc102.cpp
&quot;cc102.cpp&quot;, line 48: Warning: is hides the same name in an outer scope.
&quot;cc102.cpp&quot;, line 88: Warning: os is defined but not used.
2 Warning(s) detected.</pre>

<p>Both of these are relatively harmless. The â€˜osâ€™ error can be fixed by streaming to os rather than cout, i.e., change</p>

<pre class="programlisting">
  std::cout &lt;&lt; delim &lt;&lt; w-&gt;contents;</pre>
  
<p>to</p>

<pre class="programlisting">
  os &lt;&lt; delim &lt;&lt; w-&gt;contents;</pre>
  
<p>In this case, it makes no difference since <code>write_sentence</code> is called from <code>write_document</code> which is passed <code>cout</code>. However, if ever <code>write_document</code> were called with another ostream the program would encounter a â€˜discovered bugâ€™ (similar to â€˜discovered checkâ€™ in chess).</p>

<p>The second warning can be removed by renaming the second <code>is</code> variable, e.g. to <code>iss</code>.</p>

<p>Neither of these affect the behaviour of the program as it stands. The issue is due to how <code>std::getline </code>works in the following loop:</p>

<pre class="programlisting">
 while (std::getline(is, str, '.'))
  {
    std::istringstream is(str);
    ptr&lt;sentence&gt; s(new sentence(
      read_sentence(is)));
    next-&gt;next_sentence = s;
    next = s.get();
  }</pre>
  
<p>Basically, <code>std::getline</code> has 2 overloads (if I ignore the rvalue reference overloads). The first takes just an <code>istream</code> and a string and reads to the next newline (or <code>eof</code>). The second adds a character delimiter, and <code>getline</code> reads to this delimiter (or <code>eof</code>). Here we have the second overload. When it is called the first time, before the call we have the input like this:</p>

<pre class="programlisting">
  This is an example.\n</pre>
  
<p><code>  ^  </code>Stream pointer here</p>

<p>Then the read takes place and we read to the <code>.</code> delimiter:</p>

<pre class="programlisting">
  This is an example.\n</pre>
<p><code>                      ^                      </code>Stream pointer here (under newline).</p>

<p>So far so good. Or is it? The stream pointer is not pointing to the null termination at the end of the line but rather the newline character.</p>

<p>On the second call to <code>getline</code>, we read <code>&quot;\nIt contains two sentences&quot;</code> into <code>str</code>. The leading newline gets stripped when it gets streamed into the <code>str</code> string variable in <code>read_sentence</code> which is breaking the line into words.</p>

<p>After the second call to <code>getline</code>, the stream is as follows:</p>

<pre class="programlisting">
  It contains two sentences.\n</pre>
<p><code>                             ^                 </code>Stream pointer again under newline</p>

<p>There is then a third call to <code>newline</code>. This just reads the second newline up to <code>eof</code>, and this is the source of the spurious extra line in the output.</p>

<p>There are many ways that this could be fixed. One would be to use the <code>getline</code> overload without the character delimiter and to then extract the sentence up to the full stop.  As a quick and simple solution, I just added an extra call to <code>getline</code> to eat the newline characters:</p>

<pre class="programlisting">
  while (std::getline(is, str, '.'))
  {
    std::istringstream iss(str);
    ptr&lt;sentence&gt; s(new sentence(
      read_sentence(iss)));
    next-&gt;next_sentence = s;
    next = s.get();
    std::getline(is, str);
  }</pre>
  
<p>In terms of design, this code needs a lot more work to be able to handle other punctuation like question and exclamation marks, more than one sentence on a line, and sentences that span several lines.</p>

<h3>Jim Segrave</h3>

<p>The error in this program is trivial â€“ the <code>while</code> condition <code>std::getline(is, str, '.')</code> will return <code>true</code> when you reach end of file. Adding a simple <code>if(is.eof()) { break; }</code> at the top of the loop will cause the <code>while</code> loop to terminate without trying to make a sentence from an empty read.</p>

<p>But there are other issues:</p>

<p>A minor one:</p>

<p>The code uses a large number of forward declarations. Rearranging the code so that <code>struct word</code> is defined before <code>struct sentence</code> and <code>struct sentence</code> before <code>struct document</code>, <code>read_sentence()</code> before <code>read_document()</code>, <code>write_sentence()</code> before <code>write_document()</code> allows all the forward declarations to be removed. If this were a multi-module program, the forward declarations would need to be in a header file, and would have some value. As itâ€™s a single file, removing them removes duplication, so changing any of the struct definitions or function signatures has a smaller impact (DRYâ€“ donâ€™t repeat yourself).</p>

<p>Another minor one:</p>

<p><code>write_document() </code>and <code>write_sentence()</code> are passed an <code>ostream</code> parameter. <code>write_document()</code> simply passes it on to <code>write_sentence()</code>, but itâ€™s not actually used there. It should be removed from both functions or the function should be altered to actually use the <code>ostream</code>s rather than <code>cout</code>.</p>

<p>Most importantly, the author has chosen to build his own singly-linked list. One has to ask â€˜Why?â€™ While linked lists are good when you need to do inserts and deletes at random locations within the list, but otherwise they are a performance and memory waste (pointers which arenâ€™t needed, lack of contiguous allocation).</p>

<p>This program simply needs to maintain the order of words encountered in sentences and sentences encountered in documents. For that, the <code>std::vector </code>class is ideal â€“ it performs well, maintains the insert order and keeps the data contiguous.</p>

<p>If you are going to use a linked list in spite of this, thereâ€™s already a container type for this purpose which is likely to be implemented better and tested more thoroughly than a one-off version. But as I said, thereâ€™s no reason to use a linked list here.</p>

<p>Finally, why not use the <code>make_shared</code> library function when you want to create an object and get a shared pointer to it? To me at least, the resulting code is clearer.</p>

<p>I attach a version using <code>vector</code>s and <code>make_shared</code> (with a heavy use of <code>auto</code> to save typing and shared pointers for all the robustness they bring with them.</p>

<pre class="programlisting">
  #include &lt;iostream&gt;
  #include &lt;sstream&gt;
  #include &lt;memory&gt;
  #include &lt;string&gt;
  #include &lt;vector&gt;

  using word = std::string;
  using word_shp = std::shared_ptr&lt;word&gt;;
  using sentence_vec = std::vector&lt;word_shp&gt;;
  using sentence_vec_shp =
    std::shared_ptr&lt;sentence_vec&gt;;
  using doc_vec = std::vector&lt;sentence_vec_shp&gt;;
  using doc_shp = std::shared_ptr&lt;doc_vec&gt;;

  // read a sentence a word at a time, return a
  // vector of the words in it
  sentence_vec_shp read_sentence(
      std::istream &amp; is) {
    std::string str;
    auto  s = std::make_shared&lt;sentence_vec&gt;();
    while (is &gt;&gt; str) {
      s-&gt;push_back(std::make_shared&lt;word&gt;(str));
    }
    if(s-&gt;size()) {
      return s;
    }
    // if no words found, don't return an empty 
    // vector, let it die here
    return nullptr;
  }

  // read a document a sentence at a time,
  // return a vector of the sentences in it 
  doc_shp read_document(std::istream &amp; is) {
    std::string str;
    auto d = std::make_shared&lt;doc_vec&gt;();
    while (std::getline(is, str, '.')) {
      if(is.eof()) {
        break;
      }
      std::istringstream is(str);
      auto s = read_sentence(is);
      if(s) {
        d-&gt;push_back(s);
      }
    }
    if(d-&gt;size()) {
      return d;
    }
    // if no sentences found, don't return an 
    // empty vector, let it die here
    return nullptr;
  }

  // write a sentence one word at a time
  void write_sentence(
    const sentence_vec_shp &amp; s) {
    std::string delim;
    for (auto wp: *s) {
      std::cout &lt;&lt; delim &lt;&lt; *wp;
      delim = ' ';
    }
    std::cout &lt;&lt; '.' &lt;&lt; std::endl;
  }

  // write document one sentence at a time
  void write_document(const doc_shp &amp; d) {
    for (auto s : *d) {
      write_sentence(s);
    }
  }

  int main() {
    auto dp = read_document(std::cin);
    write_document(dp);
  }</pre>
  
<h3>James Holland</h3>

<p>On first glance, the problem could be anywhere within the studentâ€™s code. In such a situation it is best to divide the code into roughly two equal parts and to attempt to discover which half contains the bug. This process is repeated until the defect is found. Fortunately, the studentâ€™s code is already divided into two parts; one that puts information into the linked list and another that reads it out.</p>

<p>An investigation revealed that the unwanted full stop is displayed because there are three sentences in the linked list; the last sentence being empty.  It would appear that the linked list is being displayed properly and so it must be the part of the code that assembles the list that is at fault. Therefore, <code>read_document()</code> requires further investigation.</p>

<p>Closer inspection shows that the body of the <code>while</code> loop within <code>read_document()</code> is executing three times. This is despite there being only two sentences in the sample document. I think we are getting close to the root of the problem. It all hinges on what keeps the <code>while</code> loop executing.</p>

<p>The controlling clause of the <code>while</code> loop consists solely of the function <code>std::getline()</code> and so the loop will keep executing for as long as the value returned by <code>std::getline()</code> can be evaluated as <code>true</code>. <code>std::getline()</code> returns its first parameter which, in our case, is of type <code>std::iostream</code>. A Boolean function is defined for <code>std::iostream</code> that returns <code>true</code> if the stream has not failed. The function does not consider the end of file being encountered as a failure. This has the effect that when <code>std::getline()</code> is attempting to read the third sentence (that does not exist) no fault is reported (despite <code>eof</code> being set) and the loop body is executed for a third time. No characters are read into <code>str</code>, with the result that a blank sentence is added to the linked list. This explains why an unwanted full stop appears when the content of the linked list is displayed.</p>

<p>What is needed is for <code>read_document()</code>â€™s <code>while</code> loop to stop executing as soon as <code>eof</code> is encountered. Possibly the simplest way to achieve this is to modify the <code>while</code> loop controlling statement by calling <code>good()</code> as shown below.</p>

<pre class="programlisting">
  while (std::getline(is, str, '.').good())</pre>
  
<p><code>good()</code> returns <code>false</code> when the stream has failed or when <code>eof</code> is encountered.  This is exactly what is required. The studentâ€™s code will now behave as expected.</p>

<p>There are some aspects of the use of <code>std::shared_ptr</code> that should be drawn to the studentâ€™s attention. When using a shared pointer to point to a newly constructed object, it is best to use <code>std::make_shared&lt;&gt;()</code> as it has the advantages of exception safety and executes more quickly. I suggest the second line of <code>read_document()</code>â€™s <code>while</code> loop should be replaced by </p>

<pre class="programlisting">
  auto s(std::make_shared&lt;sentence&gt;(
    sentence(read_sentence(is)))); </pre>
	
<p>and the first statement of <code>read_sentence()</code>â€™s <code>while</code> loop should be replaced by </p>

<pre class="programlisting">
  auto w(std::make_shared&lt;word&gt;(
    word{nullptr, str}));</pre>
	
<p>It is, however, questionable as to whether <code>std::shared_ptr</code> is required in the studentâ€™s program. <code>std::unique_ptr</code> is an alternative that should be considered as it is faster and occupies less memory. Unfortunately, it is not possible to simply replace <code>std::shared_ptr</code> by <code>std::unique_ptr</code> within the students code, mainly because copying <code>std::unique_ptr</code> is not permitted. Some restructuring of the code would be necessary which I have not undertaken. Instead, I propose a different approach.</p>

<p>From reading the studentâ€™s code, it is clear that extensive use of linked lists is made. While it is an interesting challenge to construct linked lists from first principles, there is no need as the C++ standard library provides such containers ready to use. Doubly linked list have been available since C++98 and singly linked lists since C++11. The advantages of using the library linked lists include the following.</p>

<ul>
	<li>There is no need to explicitly allocate and release memory.</li>
	
	<li>There is no need to directly manipulate pointers.</li>
	
	<li>The lists are generic. They can contain elements of just about any type.</li>
	
	<li>Programs using library linked lists are simpler to read and write and therefore less error prone.</li>
</ul>

<p>I have rewritten the studentâ€™s program to use standard library linked lists.  In keeping with the studentâ€™s approach I have used <code>std::forward</code>, a singly linked list.</p>

<p>It is a simple matter to add elements to the front of an <code>std::forward</code> list; <code>push_front()</code> is provided for that. Unfortunately, that results in the list being populated in the reverse order. The last sentence of the document would be printed first and the last word of a sentence would be printed first. What is needed is for elements to be added to the back of the list. This is not quite so easy as there is no such function as <code>push_back()</code> for <code>std::forward</code> lists.</p>

<p>It may seem slightly odd that <code>std::forward</code> does not provide a function to add an item to the back of the list but this omission results from the desire for <code>std::forward</code> to be as memory efficient as possible. All is not lost. We simply have to keep track of where in the list to insert the next element. When there are no elements in the list, <code>before_begin()</code> provides the appropriate location. When there are elements in the list, it is always the location of the element at the back of the list that is required. This location is conveniently returned by <code>insert_after()</code>; the function used to insert elements at the back of the list. Given this, a simple <code>while</code> loop can be constructed that adds the required elements to the <code>std::forward</code> lists. I use this technique twice in my version of the program, once in <code>read_sentence()</code> and once in <code>read_document()</code> as shown below.</p>

<pre class="programlisting">
  #include &lt;forward_list&gt;
  #include &lt;iostream&gt;
  #include &lt;sstream&gt;
  #include &lt;string&gt;
  using Sentence =
    std::forward_list&lt;std::string&gt;;
  using Document = std::forward_list&lt;Sentence&gt;;
  Sentence read_sentence(std::istream &amp; is)
  {
    Sentence sentences;
    auto position = sentences.before_begin();
    std::string str;
    while (is &gt;&gt; str)
    {
      position =
        sentences.insert_after(position, str);
    }
    return sentences;
  }
  Document read_document(std::istream &amp; is)
  {
    Document document;
    auto position = document.before_begin();
    std::string str;
    while (std::getline(is, str, '.').good())
    {
      std::istringstream is(str);
      position = document.insert_after(position, 
        read_sentence(is));
    }
    return document;
  }
  void write_document(const std::ostream &amp; os,
    const Document &amp; document)
  {
    for (const auto &amp; sentence : document)
    {
      std::string delimiter;
      for (const auto &amp; word : sentence)
      {
        std::cout &lt;&lt; delimiter &lt;&lt; word;
        delimiter = ' ';
      }
      std::cout &lt;&lt; '.' &lt;&lt; std::endl;
    }
  }
  int main()
  {
    Document d(read_document(std::cin));
    write_document(std::cout, d);
  }</pre>

<h2>Commentary</h2>

<p>There were a number of different problems with the code presented, and I think that between them the authors of the critiques covered most of the issues.</p>

<p>As both Jim and James noted, the writer of the code has implemented their own singly linked list; there is no need to do this nowadays! Additionally, there is no separation of concerns with the implementation embedded in the code â€“ if a special sort of list were required it would probably be better to write a separate class to do the list management, templatized on the contained type.</p>

<p>One problem that no-one commented on is that the code contains a potential bug when exiting <code>main</code>. Naive use of smart pointers to build up complex data structures can cause stack overflow on destruction as the destruction of the tree of objects can consume a large amount of stack. In this case the <code>document</code> object <code>d</code> is the root object, its destruction results in calling the destructor of <code>first_sentence</code>, which then destroys the <code>next_sentence</code> member of this object, and so on, recursively down the list of objects.</p>

<p>The bug might be avoided if the compiler is able to perform some tail-call optimisation in the destructor, but this is not possible in all cases.</p>

<p>For example, I found this example crashed for large input when compiled without optimisation with g++ (64-bit) and MSVC (both 32-bit and 64-bit). Enabling optimisation resolved the crash for gcc, and for one of the MSVC builds. This is not desirable behaviour!</p>

<p>So, how might you solve this problem?</p>

<p>In order to resolve the stack overflow you need to write an explicit destructor that iterates through the objects rather than using recursion.</p>

<p>For example, in this case:</p>

<pre class="programlisting">
document::~document()
{
  while (first_sentence)
  {
    first_sentence =
      first_sentence-&gt;next_sentence;
  }
}</pre>

<p>We also ought to make a similar change for the list of <code>word</code>s in <code>sentence</code>, in case we try processing James Joyceâ€™s <em>Ulysses</em>!</p>

<p>This is an unfortunate issue with using smart pointers for managing object graphs, especially as debugging the root cause of a stack overflow can be quite hard.</p>

<p>(Herb Sutterâ€™s talk at CppCon this year touches on some other options that solve the same sort of problem.)</p>

<h2>The winner of CC 102</h2>

<p>I was amused to note that while all three critiques found and fixed the problem they used three slightly different techniques. It is surprisingly hard to use C++ standard input correctly and the range of possible solutions makes it less obvious when a given piece of code is correct.</p>

<p>Paul gave a fairly detailed explanation of the presenting problem with the original code, which would hopefully make the problem and its solution clear to the writer of the code.</p>

<p>Each critique went on to cover additional problems; these included </p>

<ul>
	<li>design issues such as dealing with 'real world' sentences containing punctuation (perhaps more could have been made of this)</li>
	
	<li>preferring use of <code>make_shared</code> </li>
	
	<li>changing to use <code>unique_ptr</code> rather than <code>shared_ptr</code></li>
	
	<li>replacing the list logic with <code>forward_list</code></li>
</ul>

<p>I liked Jamesâ€™ direction in using <code>std::forward_list</code>; this solution has the additional benefit of (silently) solving the stack overflow problem I discuss in my commentary. Hence, by a short head, I have awarded him the prize for this critique.</p>

<p>As always, thank you to all those who entered the competition!</p>

<h2>Code critique 103</h2>

<p><strong>(Submissions to scc@accu.org by Dec 1st)</strong></p>

<p class="blockquote">I am trying to keep track of a set of peopleâ€™s scores at a game and print out the highest scores in order at the end: it seems to work most of the time but occasionally odd things happen...</p>

<p class="blockquote">Can you see whatâ€™s wrong?</p>

<p>The code â€“ scores.cpp â€“ is in Listing 2.</p>

<table class="sidebartable">
	<tr>
		<td>
			<pre class="programlisting">
#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;sstream&gt;
#include &lt;unordered_map&gt;

// Best scores
std::multimap&lt;int, std::string, std::less&lt;&gt;&gt; best_scores;

// Map people to their best score so far
std::multimap&lt;int, std::string&gt;::iterator typedef entry;
std::unordered_map&lt;std::string, entry&gt; peoples_scores;
entry none;

void add_score(std::string name, int score)
{
  entry&amp; current = peoples_scores[name];
  if (current != none)
  {
     if (score &lt;= current-&gt;first)
     {
       return; // retain the best score
     }
     best_scores.erase(current);
  }
  current = best_scores.insert({score, name});
}

void print_scores()
{
   // top down
   for (auto it = best_scores.end();
        it-- != best_scores.begin(); )
   {
      std::cout &lt;&lt; it-&gt;second &lt;&lt; &quot;: &quot;
        &lt;&lt; it-&gt;first &lt;&lt; '\n';
   }
}

int main()
{
  for (;;)
  {
    std::cout &lt;&lt; &quot;Enter name and score: &quot;;
    std::string lbufr;
    if (!std::getline(std::cin, lbufr)) break;
    std::string name;
    int score;
    std::istringstream(lbufr)
      &gt;&gt; name &gt;&gt; score;
    add_score(name, score);
  }
  std::cout &lt;&lt; &quot;\nBest scores\n&quot;;
  print_scores();
}
			</pre>
		</td>
	</tr>
	<tr>
		<td class="title">Listing 1</td>
	</tr>
</table>

<p>You can also get the current problem from the accu-general mail list (next entry is posted around the last issueâ€™s deadline) or from the ACCU website (<a href="http://accu.org/index.php/journal">http://accu.org/index.php/journal</a>). This particularly helps overseas members who typically get the magazine much later than members in the UK and Europe.</p>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
