Journal Articles

CVu Journal Vol 14, #2 - Apr 2002 + Student Code Critiques from CVu journal.
Browse in : All > Journals > CVu > 142 (12)
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

Author: Administrator

Date: 05 April 2002 13:15:51 +01:00 or Fri, 05 April 2002 13:15:51 +01:00

Summary: 

Body: 

Prizes provided by Blackwells Bookshops & Addison-Wesley

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

Note that 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.

Student Code Critique 14: The Entries

Things go in cycles. I had three competition submissions this time round but one completely forgot to include their name. It is important that your name is somewhere in the actual submission because it other wise gets lost when I detach the attachment. As I have mentioned this before I am going to be tough and disqualify that entry (don't take it too hard, it just makes it easier for me to select a winner.) Gary Joy gave it a good shot but on balance I think Chris Main comes out ahead. Perhaps Chris would contact me to discuss his prize.

I also include a submission from our old friend who writes under the pseudonym of The Harpist.

The original code

I always try to fit this code in, but doing so this time has unpleasant consequences like what else should I drop to make the page count. So this time you will either have to dig out your last issue or visit the ACCU web site.

Critique from Gary Joy

Hello, I am a relatively new (6 months now…) C++ programmer and I thought I'd have a go at the critique competition. I am not especially confident so this should be a good learning process for me; to that end feel free to replace 'I like to…' with 'I think it's good practice but I'm worried that I'm wrong…' in my comments. ;-)

I appreciate that limitations dictate what you can print so I will refrain from 'comments' comments etc. except to say that the code really belongs in five files: Hand.h, Hand.cpp, Card.h, Card.cpp and Main.cpp.

Card class

To make it work:

The closing brace for the constructor is missing.

The local variable face_value used in the constructor is not defined.

I am using VS 6 and I don't need std:: for tolower() and strchr(). Reading the documentation it states that #include <cstring> should make it available in the std:: namespace. I await the next issue...

To make it better:

Keep the variable names in .h and .cpp files consistent. (i.e. constructor argument is face_value versus face_val - this is probably why face_value was not defined.)

operator() is defined but never used for Card. (It could be used in calculate_total().)

Hand class

To make it work:

#include Card.h is missing quotes ("") around the filename.

To make it better:

// No need for 'tors' - I like to have them anyway.

FG: Actually there are good reasons for not defining 'tors' if the compiler will do the right thing. It allows the compiler more scope for optimization.

The add_card argument is not named - I know that you do not need to include a name but I like to.

FG: But why? In this case what parameter name would add to the reader's understanding of the prototype? My preference is for using names where they add understanding but not otherwise.

calculate_total and show_total could be const functions.

FG: I would be stronger, and say they should be. It is almost invariably a design error for a non-mutating member function not to be a const member.

Keep names consistent - call the ostream bjout instead of strm in the show_total prototype. show_total doesn't need to return anything.

FG: That is true but many people like to use this form because it makes it easier to use the function to implement operator<<().

I like to initialise variables: valid and face_value. input_hand could be split into two functions to simplify it.

main()

To make it better:

Initialise answer.

General Comments

There are no destructors. Even if they are empty I like to have a destructor for every class.

FG: See my earlier comment. Indeed, an insistence on providing a destructor coupled with the frequent insistence that functions are not defined in the class interface can lead to awkward code elsewhere.

After the #include of parts of MFC add a comment to indicate why... (E.g. #include <cstring> // (strchr)) This is more of a suggestion than a critique.

FG:If it were MFC I think I might but that is part of The C++ Standard Library and I would expect programmers to be fairly familiar with that.

Don't use using namespace std (at the very least not in headers). Not only does it save including lots of stuff that you don't need but it also makes it clear what you are using from the std namespace. Either use using std::cout; or qualify within the code (std::cout).

FG: Definitely not in global space in a header. However I am happy when the directive is inside a user namespace or an implementation file.

Being Picky :-)

There is no validation of the input:

  1. It is should not be possible to have five aces. (I guess they use several decks at casinos...)

  2. The input stream is not managed, i.e. if I type in "2kay3ttay5a2345n", I will play three games in the blink of an eye.

I think that the OOP design is reasonable - the code could be readily re-used as part of another game - e.g. by deriving a class from Hand, (Poker_Hand) we could play poker instead. (With only a tiny change to the main() function).

FG: I would disagree because derivation should only be applied to things that were designed with that in mind. The lack of a virtual destructor strongly suggests that the writer had no intention that his class should be a base class.

From Chris Main

My first comment on the code is the one made by Catriona O'Connell in the previous Critique: it exhibits "the intertwining of user input and calculations".

In class Hand there are four public member functions. Two relate to input and output (input_hand(), show_total()) and two to the calculation (add_card(), calculate_total()). This distinction is clarified by the observation that the latter two are only ever called by the former two, and not by any client of Hand.

I suggest removing input_hand() and show_total() from Hand and making them standalone functions, declared in a separate header file (named handIO.h say). They would need additional parameters, Hand& and Hand const& respectively. I would remove the existing stream parameters as they are promising more than they are delivering. The implementation of the functions is really only appropriate for cout and cin.

I think there is scope for improving the function names. add_card() could become simply add() since I think hand.add(aCard) reads better than hand.add_card(aCard). Likewise input_hand() could be replaced by input(). I would change calculate_total() to total() since the function is returning the value of an attribute of the class - whether we perform the calculation of that value in that particular function is an implementation detail. This detail is of no interest to clients, so it ought to be concealed from them. [I also think value() would be a more exact name than total(), but that is a rather more subjective opinion.]

Hand encapsulates a vector of Cards, which sounds good. However when we inspect class Card more closely we discover that it only encapsulates a card value without encapsulating the card's name. This is an important distinction in this application because Hand::calculate_total() sometimes needs to know whether there is an ace in the hand. It does this in the submitted code by making an assumption about the value assigned to an ace by Card. The algorithm for calculate_total() additionally depends upon the value assigned to an ace because that determines whether it should increment or decrement by 10 when adjusting the value of the hand. For these two reasons, the value assigned to an ace cannot be encapsulated internally within Card.

We could re-design so that the ace value is visible to both Card and Hand, but I think a cleaner design results from encapsulating that value internally within Hand (cleaner in the sense that there are fewer interdependencies). In this scheme, Card would encapsulate just the name of a card. An obvious choice would be to make it an enum. This may be good enough, but it must be borne in mind that in C/C++, unlike Pascal/Ada, an enum value is a thinly disguised integer which can be used in arithmetic operations without explicit casting. In the light of the exchange between Paul Whitehead and Francis Glassborow (C Vu 13.6, Dec. 2001) on ADTs, we may prefer to make it a class exporting static const values, with constructors disabled so that no other valid values can be created. (A default constructor creating an invalid value should be made available so that the class can be used with STL containers).

Again, I would separate the user input aspects of Card into a standalone function input(Card&). How should we map a character input by a user to a Card? The answer is in the question: a map. I really liked Colin Hersom's proposal to do this in the previous Critique, and I was only sorry that he didn't carry it through fully. His comment "See how it would have been neater if we used a class?" would more accurately have been stated "See how messy it is without a class?" If we make the map<char, Card> a private member variable of a wrapper class it can be populated in the constructor of the wrapper class. Then within input(Card&) all we need to do is declare a static local variable of the wrapper class.

The submitted code throws an exception when an invalid character is entered. I consider this unduly aggressive; miss-typing by a user should be expected by the program rather than treated as an exceptional occurrence. Instead the map-wrapper class could have a member function lookup(char, Card&) returning a bool to indicate whether it can map the character to a Card.

Turning then to the implementation of Hand, what container should we use? What should it contain? Do we want to make it a pointer so that we can use the pImpl/CheshireCat/Handle-Body/Bridge idiom/pattern (delete according to taste)? There are a number of valid options, depending upon the exact use we wish to make of our Hand class. We could keep these options open by making Hand a template, to be instantiated with the container implementation class. To describe how to do this is beyond the scope of a code critique; I will try to write a separate article.

If, as I have argued, Card encapsulates a name rather than a value a map is likely to be a more suitable container than a vector. We sometimes need to find aces in the hand, and find is optimised for maps (or should that be maps are optimised for find?). [For the paranoid, and it probably pays to be paranoid in relation to gambling card games, representing Card as a name rather than a value gives us the opportunity to check for five of the same card in a hand.]

How should calculate_total() accumulate the value of the hand? The answer is in the question (if we #include <numeric>):

unsigned int value =  std::accumulate(cards.begin(), cards.end(),
      0, AccumulateCardValue(cardValues()));

What is cardValues()? A function that returns a reference to another Colin Hersom style lookup map, wrapped in a class and declared as a static local variable within the function. The case for such a map rather than a switch-statement is stronger here: in the switch-statement submitted a calculation is performed every time a numerical value is looked up:

numerical_value = face_val - '0';

The syntactic tidiness of the switch fall through conceals the potential for inferior performance. Note that the common (in my experience) practice of declaring cardValues() as a private member function, in this case a static one, clutters the header file unnecessarily. Instead it can be declared within an unnamed namespace in the implementation file (equivalent to a static function in C).

What is AccumulateCardValue? A function object class more succinctly listed than described:

class AccumulateCardValue {
  public:
    AccumulateCardValue(
            const CardValues& cardValues)
      : values(cardValues){}
    unsigned int operator()(
          unsigned int accumulatedValue,
          Cards::value_type const& card) {
// Exact implementation depends upon
// Cards::value_type
// In pseudo code:
// card found = values.lookup(card name, card
// value) if not card found then throw
// exception return accumulatedValue + 
// (card value * number of cards)
    }
  private:
    const CardValues& values;
};

In this case it is an unexpected occurrence for the lookup to fail, so it is legitimate to throw an exception. I would simply derive my exception class from std::exception rather than build my own as done in the submitted code.

A final observation on calculate_total(): there is no need to check for aces during the accumulation loop, and indeed no need to check for them unless the hand value thus accumulated is less than 12.

I suspect some readers will remain uneasy about my proposal to strip the value attribute from the Card class. My solution is fine in the case that we only ever want to calculate the value of a hand once, when the hand is complete. Well, maybe that is all our application requires. It is, though, easy to imagine there might be a requirement to show the value of the hand after each card is added. In that case it is obviously sensible to cache the value of each card the first time it is looked up. This can easily be achieved within my design by changing the class of the objects held by the container, adding a value attribute to those objects. This value attribute could be initialised when the card is added to the hand (here we see the value of encapsulating card values in the cardValues() function), and the implementation of AccumulateCardValue is made simpler correspondingly. At this stage you may realise the usefulness of making the container implementation class a template parameter, and if you've used the pImpl idiom your clients won't even be affected by such a modification.

From Anon2

Card.h

I would first of all replace the class name Card with CardValue, as strictly speaking this is the entity being modelled, rather than an actual card. The original program does not deal with actual cards (which would be specified with the two parameters of suit and denomination). It only specifies a means of identifying a card's denomination in order to calculate the card's value in the game. In other card games the suit may come into it, and a Card class could consist of an enumeration enum SUIT{HEARTS, DIAMOND, CLUBS, SPADES}; and two member variables - Suit, of type SUIT, and Denomination, an int denoting 1 (Ace) to 13 (King).

FG: I think those all upper case spellings are a defect. They result in making code unnecessarily fragile in the presence of the pre-processor and they make code harder to read. And why is the suit an enum but not the denomination?.

The constructor of Card does not need to be made explicit. A constructor of a class C able to take a single parameter of a type TYPE is made explicit to prevent the compiler using it to perform an automatic type conversion from TYPE to C in a function call or in an expression. In such a case a C or C & is expected, but a TYPE is passed. The compiler creates code that invokes that constructor to produce an instance of the expected class C. An example from the STL is the vector class, which has a constructor taking as a first parameter an integral value, followed by two parameters with default values, the first one of which is an instance of the vector's parameterised type T, as created by invoking its default constructor T(). Calling this constructor with a value N as a single parameter results in a vector of N elements of the object T(). If T were the type string this would give a vector of N null strings, or if T was an int it would give a vector of N zeros, and so on. The question is does it make sense or does it cause any confusion to create such an object when the user specifies an integer value N where a vector<T> is expected. As an example suppose we had a function void output(const vector<T> & v) which just outputted on separate lines the elements in the vector v. If T = string or T = int the user may call output(5), expecting the number 5 to be treated as a single element vector, and so get "5" appearing on a line by itself. However what they would get is 5 blank lines if T = string, and 5 zeros on separate lines if T = int. To avoid this confusing situation the STL vector class declares the above constructor to be explicit. Then output(5) cannot compile (on VC++ 6.0 the error reports that "no constructor could take the source type").

There is no comparable ambiguity in the case of the Card class, a char can always be substituted for a Card object without being counter-intuitive as in the case of the vector class above. Therefore I would not make the constructor explicit. (Note of course not all chars can be successful in becoming a Card, but that's what the class BadFaceValue is for). Anywhere a Card object is expected a char can be passed without confusion, and when the char represents an invalid denomination, the conversion will fail with the BadFaceValue(char) exception being raised.

FG: A consequence of that decision is that functions that take a Card argument have to validate the argument and throw an exception if it is not valid. Throwing an exception is a runtime event and we should follow the rule of checking things as early as possible. I think that the Card constructor from char should be explicit unless you can provide some cogent reason why implicit conversion form a char to a Card should be allowed.

The Card class does not need the get_numerical_value function, as the int conversion operator does the same thing. A Card (or CardValue as I suggested above) object is directly identified with an int value, and using the (int) cast makes sense in whatever context.

FG: I would argue exactly the other way and remove that conversion operator. I do not want functions that expect an int to accept a Card. I would apply similar reasoning to the provision of a char conversion operator. Indeed one major inconsistency in the authors design is that he inhibits implicit inward conversions while explicitly providing for the more dangerous implicit outward conversions.

Similarly, the class BadFaceValue does not require the function get_face_value, as the char() conversion operator does the same thing. Indeed the program nowhere calls the get_face_value function, but only uses the char() conversion (in the Hand::input_hand function). Strictly speaking there's no reason for the class BadFaceValue at all, as all it is a char with a default value of '?' which is never used, and a char exception could just as well be thrown in Card::Card, and that char exception caught in the catch block of Hand::input_hand. However I agree with the author's approach as it improves code clarity. One final point though - as Card is the only class which uses the exception perhaps it would be an idea to include the exception class definition within the scope of the Card class. In Hand::input_hand this would mean the catch block would read catch(Card::BadFaceValue fv)).

The use of an exception class is a commonly used strategy for dealing with constructor failures as the constructor cannot return a value to the client to indicate failure (the client in this case being the Hand::input_hand function - the try block in that function encloses code where a Card object is constructed).

As far as I know a semi-colon is not needed after the closing brace of a namespace declaration, although VC++ 6 accepts it.

FG: Strictly speaking, an error that requires a diagnostic.

Hand.h

The using directive introduces all std scoped names into the file's scope, but the only names needed are istream, ostream, cin, cout. using declarations (i.e. using std::cin etc) for these would suffice.

FG: Even using declarations at global scope in a header file impose names on all who #include the header into their own code.

The public interface for class Hand consists of the two functions input_hand and show_total (the client main only calls these two functions). The other two functions, add_card and calculate_total, should therefore be made private. I note the declaration of add_card specifies a whole Card object, but objects should always be passed by reference, and made const if they are not intended to be changed by the function. Thus we would have add_card(const Card & card); as the declaration of add_card. Because of the operator char() defined for class Card, add_card could just as well take a char parameter, but Card is clearer as it is a vector of Cards that is stored in the object; going from the user input to creating a Card is where the char() conversion should take place, not in going from a Card to a collection of Cards. Lastly on add_card, as it so short a function it could be made inline by defining it within the class definition (or prefixing the definition in Hand.cpp with the word inline). The functions calculate_total and show_total can be declared as const member functions of Hand as they never change the member variables of the object on which they are called.

FG: Many sets of coding guidelines require that, at a minimum, all inline definitions should be signed off. Furthermore, placing an inline definition in the .cpp file is at best a waste of time because the compiler will not see it at the point of use.

Hand.cpp

In the function input_hand the user is just given the prompt message again when they type in an invalid number of cards. Thus the while loop should be restructured to include a helpful error message. The use of a char instead of an int here reduces some of the stream problems, though its not watertight. (Incidentally I find stream IO on a command prompt quite tricky - in Windows programming processing input is much simpler, as the user can type whatever they want, you can restrict it to digit characters by specifying a property of the edit control (in Win32, the ES_NUMBER control style), and you can query the edit control for how much data is in its buffer, and so allocate a large enough buffer in your own program, then get the data into that buffer, and then examine its contents to see if its acceptable for that particular input, typically reporting an error message box if its not - a reasonably straightforward procedure in comparison with using the stream libraries facilities).

Further on into the input_hand function, where user input is being turned into Card objects, the line add_card(Card(face_value)); could now read add_card(face_value), due to the removal of explicit in Card.h. An exception will be thrown just the same if a bad face value is entered. I would suggest also that catch(BadFaceValue fv) be changed to catch(BadFaceValue & fv) as (unless someone can enlighten me with a counterexample) whole objects need never be passed to functions.

FG: I do not understand that last sentence. We should always prefer catch by reference but that is to avoid potential slicing when an exception thrown has been derived from the type being caught.

In the function calculate_total the calls to i->get_numerical_value() can be replaced by (int)(*i), as a Card object (preferably CardValue as mentioned above) can be converted to an int.

FG: I strongly prefer the use of a named function. I dislike casts unless they are essential, and even more strongly dislike old C-style casts. They simply stop the compiler from helping get code correct.

Overall Structure

The author mentioned that the only information needed from the Hand class was the total hand value. It could be defined this way, with int total; replacing the vector<Card>. There would then be no need for a separate calculate_total function, and the function input_hand would tally the total score as the cards were entered, setting a flag contains_aces if an ace was detected, and adjusting the final score (in the new way as the author suggested). In the region of the add_card call in input_hand the code would thus look like :

try{
//...etc...
Card card(face_value); 
    // could except with BadFaceValue
  if (tolower(face_value) == 'a')
    contains_aces = true;
  add_card( card );
  total += card;  
// uses Card's int conversion
}
catch(BadFaceValue & fv){ }

This approach would result in a faster execution time (between the end of the user's input and display of the result - only tiny fractions of a second in this example - though in real world applications, a similar trade-off may involve much larger time scales).

FG: I doubt that such an optimisation will have any significant impact on real code that involves user i/o. Once, many years ago, I had to very carefully code a problem where a name being typed in had to be located in a given list on the fly so that as soon as the possibilities had been reduced to not more than eight the user would be given the opportunity to select from that list. These days I would not bother with any fancy coding because modern platforms would do so much processing between keystrokes that a simple, easy to maintain, solution would meet the design requirements.

However as two contributors to Code Critque 13 mentioned it is usually preferable to separate obtaining the user input from processing it, e.g. by using two separate functions such as the input_hand and calculate_total functions above. It is less efficient in terms of execution time, as the same for loop is traversed twice, but it is clearer to understand, and therefore preferable if performance is not critical.

Being retrograde, I describe a non-class based solution to the original problem, which is similar to this solution (has comparable number of lines of code and separates obtaining user input from processing it).

Step 1 : Request number of cards in hand (in var num_cards), using function GetNoOfCards.

Step 2 : Call function bool GetHand(int num_cards, vector<char> & hand), which returns true if required number of cards in hand was entered without user cancelling, and false if the user cancelled. GetHand places the entered cards in the vector of chars passed in if the user didn't cancel.

Step 3 : If user cancelled in Step 2, then skip Steps 4 - 5 below.

Step 4 : Call function bool GetHandValue(const vector<char> & hand, int & hand_value) which returns true if not bust (i.e. hand value is <= 21) and false if bust (i.e. hand value > 21). Whether bust or not, the hand value is stored in hand_value.

Step 5 : If false is returned in Step 4 above then output "You busted with : hand_value", otherwise output "Your hand is : hand_value".

Step 6 : Ask user if they want to continue. If yes loop back to Step 1, else end program.

We then have a main that looks like:

int main(void){
  vector<char> hand;
  int hand_value;
  char input;
  do{
    if(GetHand(GetNoOfCards(),hand)){
      if(GetHandValue(hand, hand_value))
        cout << "Your hand is : " 
            << hand_value << endl;
      else
        cout << "You busted with : " 
            << hand_value << endl;
    }
    cout << "Continue ? (y/n) : " ;
    cin >> input;
  }
  while(tolower(input) == 'y');
  return 0;
}

The full code is in the electronic version on our web site.

From The Harpist

More often than not, critiques of source code focus on the code itself. I think that is often a mistake. We need to get to the fundamental design issue and when we have a hold of that the code often writes itself. With this in mind let us look at the source code Francis presented for critique this time.

A quick read of the code should start raising questions in your mind. If it is correct to make constructors explicit (which it generally is) what are those conversion operators doing there? What is the purpose of the three classes? More fundamentally, what is the code achieving? So back to the drawing board.

We want to input the denominations (though not suits) of between two and five cards and evaluate them as a blackjack hand. Apparently we need not consider special hands, just the best total that the hand will make. The first question I ask is why ask the user for the number of cards in the hand? Why not just count them in? We know we need two and we know that when we have five that is it. If we are going to count them in we need some way for the user to signal that the hand is complete. Let me use a common technique and simply use any invalid key to terminate input. Of course we will not allow termination until two cards have been put in.

Now let me make a list of the tools I need:

  • Something to give the user instructions.

  • A function to get an input and validate it.

  • A function to evaluate a hand and store the result (note to self, I must track the special case of an ace because that is two valued)

  • An output function

  • A driver.

Let me write the driver first:

int main(){
  try{
    instructions();
    do{
      evalhand eh;
      eh.result();
    } while(again("Do you have another?")
  }  
  catch(...) {
    std::cout << "Stopping \n" << std::endl;
  }
  std::cout << "That is it." << endl;
  return 0;
}

I always put most of my main in a try block so that I will get clean up if some exception is thrown that I had not prepared for. It costs little so I can see little reason to do otherwise. Now let me write the various bits needed to allow that code to compile and link to produce an executable.

void instructions(){
  std::cout << "Ask me about it." << std::endl;
}

In real life you implement that function somewhat better, but you can refine that as you go and easily respond to user problems with understanding your instructions because you have isolated that task in its own function. I would rip out all the string literals and provide them through a file that could be customised for different human languages. The instructions function is a special case because it is all text and so could be provided in English, French, Spanish etc. versions.

Now what can we deduce from the evalhand eh; statement? Yes, evalhand must be a type. So we get (with a little thought):

class evalhand {
  int total;
  bool hasanace;
public:
  evalhand();
  void result();
};

What has that constructor for evalhand got to do? It has to read in data, card by card until finished keeping a running total, and a record of whether the hand contains an ace. Reading in a card is a service routine so we hide it away in the unnamed namespace of the implementation file for evalhand. I want to get a value back and a validation code. Something like this:

namespace{
  bool get(char & c) {
    std::cin >> c;
    return std::strchr("23456789tjqkaTJQKA", c)
  }

Note that that string literal used to show valid input characters may need adjusting for non-English speaking versions. While I remember, I can also use the following as another service function:

  void validinputs(){
    std::cout << "Type the first letter of"
          << " Ace, King, Queen, Jack, Ten"
          << " and 2 to 9 for the others."
          << std::endl;
}

I would polish the wording for my release version.".

It is about time to write the evalhand constructor (at file scope):

evalhand::evalhand():total(0), hasanace(false) {
  char card;
  std::cout << "First card please : ";
  while(!get(card)) {
    std::cout << "Input not recognised\n";
    validinputs();
  }
  total += value(card, hasanace);
  std::cout << "Second card please : ";
  while(!get(card)) {
    std::cout << "Input not recognised\n";
    validinputs();
  }
  total += value(card, hasanace);
  std::cout << "Any key other than one"
       "representing a card ends input.\n"
  for(int count=0; count<3; ++count){
    if(!get(card)) break;
    total += value(card, hasanace);
  }
}

I am leaving refactoring the common code in the above as an exercise for the reader. Note that I do not need exceptions. I avoid exceptions unless the problem detection and solution are necessarily remote from each other.

I slipped in another service function there so I will need to add that to my unnamed namespace:

  int value(char c, bool & isace){
    int val = 10;
    c = tolower(c);
    if(c=='a'){
      isace = true;
      val = 1;
    }
    if(c>'1' && c<='9') val = c-'0';
    return val;
}

Note that I can skip most of the tests by initialising val to the value of face cards . The get function has already validated the input.

Now to that member function:

void evalhand::result() {
  if(total<11 && hasanace) total += 10;
  if(total>21) std::cout << "You busted. "
  std::cout<<"Your total was: " << total << '\n';
}

Those who have tracked my code to here will realise that there is one more function to go. Well you should have a function such as again in stock. If you haven't it is time you wrote one. That gives me an idea. There are dozens of little utility functions like again. What about an ACCU library of them? They are tedious to write and many of them have little catches that need some thought. In this case even partial internationalisation needs a little care.

That is it. Please feel free to rip into my code and my design. However next time you do a code review think about the design issues first. That means you must focus on why the code is being written. You may not agree with the XP philosophy but there is a great deal to be said for leaving tomorrow's problems till tomorrow. Trying to solve them early often leads to undesirable complexity.

Student Code Critique 15

There is a problem with this code to reverse a given string in place with the use of pointers. It gives a segmentation fault whenever it tries to swap two characters in the string. The fault comes in the line indicated in the following code:

Please note that this is C code so those of you who know that this problem is trivial in C++ can add the C++ solution but for competition purposes you need to do two things. First explain and correct the problem. Then supply a well-structured C solution to the problem to demonstrate to the student how good code would look. Note, that this is exactly how any competent instructor should work - correct the students work and then give a good solution. Better instructors would ask students to do a code review on the instructor's own code.

By the way, many systems will actually accept and execute the student's compiled code producing exactly the output he expects. He has been lucky that the platform he is using demands higher standards.

#include <string.h>
void swap(char *a, char *b){
  char tmp;
  tmp = *a;
  *a = *b;  
//that is where the seg. fault comes
  *b = tmp;
}
void reverse(char *str){
  char *a, *b;
  for(int i=0; i<strlen(str)/2; i++){
    a = &(str[i]);
    b = &(str[strlen(str)-i-1]);
    printf("swapping %c & %c...\n" ,*a, *b);
    swap(a, b);
  }
  printf("the reversed string is: %s\n", str);
}
main() {
  reverse("cheese");
}

Notes: 

More fields may be available via dynamicdata ..