Journal Articles

CVu Journal Vol 17, #5 - Oct 2005 + Student Code Critiques from CVu journal.
Browse in : All > Journals > CVu > 175 (15)
All > Journal Columns > Code Critique (70)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. 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/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Student Code Critique Competition 36

Author: Administrator

Date: 06 October 2005 05:00:00 +01:00 or Thu, 06 October 2005 05:00:00 +01:00

Summary: 

Body: 

Prizes provided by Blackwells Bookshops & Addison-Wesley

Please note that participation in this competition is open to all members. The title reflects the fact that the code used is normally provided by a student as part of their course work.

This item is part of the Dialogue section of C Vu, which is intended to designate it as an item where reader interaction is particularly important. Readers' comments and criticisms of published entries are always welcome, as are possible samples.

Before We Start

Remember that you can get the current problem set in the ACCU website (http://www.accu.org/journals/). This is aimed at people living overseas who get the magazine much later than members in the UK and Europe.

Student Code Critique 35 Entries

Here is a C++ header file with a number of potential problems. Please critique the code to help the student identify the problems and to help them to provide some better solutions.

Note: the class Report is not shown. It contains a large amount of data, which can be explicitly deleted by a call to ClearAll.

// Reports : vector of reports
class Reports : public map<Data*, Report>
{
public:
  Reports() : nIndex(0) {}
  void ClearAll()
  {
    for (iterator iter=begin();
         iter != end(); iter++)
      (*iter).second.ClearAll();
  }

  Report& GetReport(int nReport)
  {
    int nSize = size();
    assert(nReport < nSize);
    if (nIndex == 0 || nIndex > nReport || 
                    nIndex >=(nSize-1))
    {
      iter = begin();
      nIndex = 0;
    }
 
   for (; iter != end(); iter++, nIndex++)

    {
      if (nIndex == nReport)
        return iter->second;
    }
    // keep compiler happy
    return *((Report*)0);
  }

protected:
  int      nIndex;
  iterator iter;
};

From Simon Farnsworth

Firstly, I should apologise for the poor quality of this critique. I've only recently started using the STL, so my understanding is limited. I'm looking forward to seeing and learning from more expert critiques.

My immediate reaction upon seeing this code was confusion: the comment says that Reports is a vector, but the code indicates that it is a map. While both are containers, map is associative (in which you reference elements by a key), while vector elements are referenced by position. Given the presence of GetReport(int nReport), I shall assume that the comment is right, and that the code is an attempt to induce map to behave like vector.

It is possible that the goal is to have a type that is a map in some contexts, and a vector in others. If this is the case here, the student would perhaps be better advised to write a container which includes a map and a vector as elements, and provides those operations on map and vector that are needed.

If this was intended as a vector, not a map, then changing the code to begin: class Reports : public vector<Report> removes the need for GetReport(); vector provides operator[] to retrieve items.

GetReport() is a mess: it permits nReport to be negative, but does not handle this case (nReport should probably be unsigned, not int). It also relies on the fact that map is a sorted associative container to ensure that a report's number does not change regularly; since there are no guarantees about the values of the keys used, this could lead to unexpected behaviour. Finally, it uses casts to trick the compiler into returning a bad reference. The comment implies that this was done to silence a compiler warning; in this case, the warning is generated since GetReport() can fail to return a report. However, vector's operator[] will handle this case for us.

Finally, a couple of style notes: (*iter). is better written as iter->, and preincrement is generally preferred to postincrement, as the code is slightly simpler (although the optimizer should fix this for you).

Putting this all together results in the following code:

// An exception thrown whenever a requested
// report cannot be found

class NoSuchReport {
    private:
    unsigned report_number;

    public:
    NoSuchReport(unsigned nReport) : 
      report_number(nReport) {}
    unsigned getReport()
    { return report_number; }
    // Compiler can safely autogenerate the 
    // big four.
};

// Reports : vector of reports
class Reports : public vector<Report>
{
    void ClearAll()
    {
        for(iterator iter = begin();
            iter != end(); ++iter)
        {
            iter->second.ClearAll();
        }
    }
};

Should GetReport be needed for legacy reasons, it is easy enough to implement as follows:

Report &GetReport(unsigned nReport)
{
    return (*this)[nReport];
}

Commentary

This code looks relatively innocuous on a first reading, but repays further thought as there are many problems lurking within this relatively short piece of code.

To my mind the two most serious problems are (1) the confusion about whether this class contains a map or a vector and (2) ownership issues.

The first criticism concerns access to the objects of the class. Does Reports represent an array or an associative container? It may be both, but there is a fundamental asymmetry about the class as currently coded since all the associative behaviour comes form inheritance and the array like behaviour by an additional method. My inclination is to suspect the inheritance from map and I would prefer the class to contain a map as member data and provide methods to manipulate it.

There are several ownership issues. Firstly, the default copy constructor and copy assignment operator make it possible to copy the data structure. This is probably not behaviour the writer expected and is likely to be expensive; as the map contains the reports each one will be copied. There are two standard solutions to this particular issue: either provide a private definition of these two methods (but no implementation), or inherit from a non-copyable class such as boost::noncopyable.

The second ownership issue concerns the member data nIndex and iter. These members seem to be designed to optimise iteration over the elements of the class by keeping state between successive calls to GetReport but this is unreliable if the collection changes between calls to this function. I am also unhappy that member data like this is made protected rather than private. If the performance of the iteration actually is an issue (as determined by some performance measurements) then a better solution would be to provide an array-like iterator for the class following the usual STL pattern.

The third question I have about ownership is the onus seeming to be on the user of this class to call ClearAll() before the Reports object is destroyed. I would prefer this to be linked to the class's destructor for safety.

The final comment I would wish to raise with the writer of the code at this point would over the return of a null reference when GetReport fails. References never should be null, and returning one makes the code non-portable and well as unreliable. It would probably be better to indicate failure by throwing an exception, perhaps std::out_of_range, rather than twisting the language simply to remove the compiler warning.

The Winner of SCC 35

The editor's choice is:

Simon Farnsworth

Please email to arrange for your prize.

Student Code Critique 36

(Submissions to by Nov 1st)

Here is a C program generating a couple of prime numbers as part of an exercise on encoding/decoding with public and private keys. There are two bugs with the program: it produces the same output each time it is run with one compiler (MSVC) and it loops forever with another (gcc). Please critique the code to help the student resolve both these problems with the algorithm. Additionally suggest any improvements to the coding style and point out any other issues with the algorithms used. You can also broaden the critique to include a C++ solution if this may assist the student with their original task.

#include <stdlib.h>
#include <stdio.h>
int main()
{

//need to generate number, then find out 
//whether it is a prime, twice.
//then need to generate e and see if it is a
//factor of n.
  int i1, rem1, i2, rem2, i3, rem3, rem4;
  int p, q;
  int n, phi, e;

  //These are the two prime numbers output
  int m, d;

  i1 = 0;
  i2 = 0;
  i3 = 0;
  while(i1!=1)
  {
    p = 100 + 99*rand()/((double)RAND_MAX+1);
    //p is random number between 100 and 200.
    
    i1=p-1;
    rem1 = p%i1;
    
    //find out whether the number is prime
    while(rem1!=0)
    {
      i1--;
      rem1 = p%i1;
    }
  }

  while(i2!=1)
  {
    q = 100 + 99*rand()/((double)RAND_MAX+1);
    i2=q-1;
    rem2 = q%i2;
    while(rem2!=0)
    {
      i2--;
      rem2 = q%i2;
    }
  }

  n = p*q;
  phi = (p-1)*(q-1);
  // phi is the number of primes less than n!
  
  //e picked such that gcd(e, phi) = 1
  while(i3!=1)
  {
    e = phi*rand()/((double)RAND_MAX+1);
    //e is a random number between 0 and phi. 
    
    i3=e;
    rem3 = phi%i3;
    rem4 = 1;

    //this loop finds the highest value of i3 
    //which divides both numbers. It needs to
    // be 1, so they are relatively prime
    while(rem4!=0 )
    { 
      i3--;
      rem3 = phi%i3;
      
      if(rem3==0) 
        rem4 = e%i3;
    }
  }
  
  //the loop will find the value of m such 
  //that e*d mod phi = 1.
  m = 0;
  
  while((e*d)%phi !=1)
  {
    d = (m*phi+1)/e;
    m++;
  };
  printf( "(m,d) is (%i,%i)\n", m,d );
  return 0; 
}

Notes: 

More fields may be available via dynamicdata ..