CS24 Week 3 Lab: Objects, C++, and Testing

Due Date: Tuesday, July 15 at 11:59:59 PM

In this lab, you will refactor the code you wrote last week to conform to an object-oriented style, and to behave more like idiomatic C++. While we can write C in C++ for the most part, C++ offers additional functionality, and there are accepted best practices. In addition to this refactoring, you will find some bugs in an array-based implementation of a list. After completing this lab, you should:

Pair Programming

You may work with a partner for this lab. If you choose a partner, both members must contribute to the same work. That is, you cannot simply divide the lab in half, as everyone is responsible for all the content. Instead, you should use a pair programming style. In pair programming, one person acts as a driver, who takes control of the keyboard. The other person acts as a navigator, who reviews what the driver writes and offers advice. Oftentimes, the driver is occupied with details of the problem (e.g., variables, control flow, etc.), whereas the navigator is concerned with bigger-picture problems (e.g., functions, design, interactions). This works well for problems which are simply too large to fit into one person's head. If you're unfamiliar with the concept of pair programming, you may wish to watch this video on the topic.

With pair programming, oftentimes it works best if both participants are of approximately the same skill level. Generally, if the disparity in skill is large, the end result is frustrating for both parties - it really only works when both parties operate at approximately the same speed. With this in mind, you should select someone of approximately the same skill level.

Reading

This C++ Tutorial offers a lot. Much of the basics are review, given that C++ is a superset of C. If you are solid on C, you can skim through the Basics, Functions(I), Arrays, and Pointers. Just note the new bool data type.

As for new material, you should read the Classes(I) tab in the tutorial. For those with the textbook, Chapters 2.1 - 2.4 in the Dale book cover this material.

Examples

The Rectangle examples that we covered in lecture should prove very useful in understanding this assignment. Below are the relevant files. I would suggest opening the C ones in one browser window (three tabs) and the other in another window (three tabs). Then you can put the analogous files side by side and truly see the differences (as well as similarities).

Part A: Reading Questions

Step A-1: Create Directories and Copy Files

-bash-4.2$ cd
-bash-4.2$ pwd

/cs/student/yourusername

-bash-4.2$ cd cs24
-bash-4.2$ pwd

/cs/student/yourusername/cs24
-bash-4.2$ mkdir lab3
-bash-4.2$ cd lab3
-bash-4.2$ pwd

/cs/student/yourusername/cs24/lab3

Now copy over the files for this week. They are kept in the directory: /cs/student/kyledewey/cs24/labs/3

-bash-4.2$ cp /cs/student/kyledewey/cs24/labs/3/* .

Note that there is a '.' at the end of that line. cp means copy. /cs/student/kyledewey/cs24/labs/3/* means all of the files contained in the directory /cs/student/kyledewey/cs24/labs/3. The '.' means to copy from that location to the current location.

Now check to make sure the copy worked correctly:

-bash-4.2$ ls

You should see a set of files listed that will be used for this warm-up.

You must also copy of the *.c and *.h files from last week, like so:

-bash-4.2$ cp ../lab2/*.c ../lab2/*.h .

Step A-2: Answer Reading Questions

The file reading_questions.txt contains a series of questions you must answer. More instructions are in that file.

Part B: Refactor Code

In this portion, you will refactor some C code to behave more like idiomatic, object-oriented C++ code. I will step you through Card.cpp, and you will have to implement Deck.cpp yourself. The files Card.h and Deck.h have been provided for you, so you know exactly what needs to be implemented in Card.cpp and Deck.cpp.

For this portion, you should assume that you need no error checking. This is in contrast to the previous week, where adding error checking was a potentially significant portion. Part of this is because we can no longer signal errors in certain contexts, as with the old make_card. Another part is that certain kinds of errors are no longer possible. For example, with the Deck class, we can only create decks through the constructor, which is assumed to be correct. We also have things set that you cannot mutate cards once created, so whole classes of errors are now impossible.

In multiple places, you will need to modify code that was previously provided for you in order to get things to compile. This is expected.

Step B-1: Card.cpp

  1. Copy problemhelpers.c to be Card.cpp:
    -bash-4.2$ cp problemhelpers.c Card.cpp
    
  2. Change the #includes to be C++ style. For example, #include <stdio.h> becomes #include <cstdio>.
  3. Delete any of the functions that involve the deck; those will be implemented in the Deck class you will implement in the next part.
  4. Add Card:: before each of the function (now method) names (after the return type) This tells the compiler that the method you are implementing is from the class Card, which allows the method to access the instance variables.
  5. Anywhere the code refers to the "card" struct, replace that with "Card".
  6. Adjust the input arguments to get rid of the hidden card* argument (which you can still access using the this keyword). For example, with compare, the first card argument argument should go away, as it is now implicitly the object on which the compare method was called. That is, this is implicitly the first argument.
  7. Change the implentation of make_card to be a constructor. Make sure you remove the malloc line, because it is called along with new. The new call is not inside the constructor.
  8. Add in the getters, e.g., get_suit. Remember to place Card:: in between the return type and the method name.
  9. Replace all malloc calls with new calls. While malloc can still be used in C++ programs, it is generally good practice to use new instead.
  10. Finally, you need to provide a print function. Previously, code just printed out the individual fields whenever necessary. Now that we have methods, this functionality should be put right onto the Card class.

Once you have finished, you are ready to compile. But wait! You only have one file, and it doesn't have a main!?!? How can you compile? Well, there is a way to compile only one file when your program is not finished yet. This doesn't allow you to test it fully, but it does allow you to get a lot of things fixed that cause compiler errors. The key is the -c flag. Use that flag now, along with clang++ because we are using C++:

-bash-4.2$ clang++ -c Card.cpp

If it says that an instance variable was not defined, you probably forgot the Card:: before the method name.

Step B-2: Deck.cpp

The Deck.h header file has been provided for you. Looking closely at that, implement Deck.cpp using the same instructions as with Card.cpp. Note one large difference with Deck.h: The array of Card objects is now an array of Card pointers. This is because of limitations in how you can call new with arrays of objects. When it is an array of pointers, you can call the Card constructor individually on each card. If you make it an array of Card objects, all cards will be created using a constructor with now arguments (the default constructor). Then you have to take a second pass and initialize them all. Your Deck constructor will create the standard deck of cards.

Step B-3: main.cpp

Now you need to change over problemmain.c. It is currently implemented in C.

  1. Copy problemmain.c to be main.cpp
  2. Now you need to switch over the #includes. First replace the existing C library includes with the C++ way of including those C libraries (cstdio rather than stdio.h). Then add using namespace std; where stdlib used to be (you no longer need stdlib). Finally, add the #includes for Card.h and Deck.h, and remove the #include for problemhelpers.h. There is also an equivalent to #include <stdio.h> - it is #include <iostream>. But, since we aren't using any C++ methods for performing I/O (reading from a file, reading from the keyboard, or print to the screen), we don't need it.
  3. Change the declarations to be Card and Deck rather than card and deck
  4. Use new instead of malloc
  5. Call the Card constructor rather than make_card
  6. Change all function calls into object-oriented instance method calls. This means putting the variable in front of the method name.
  7. Instead of printing by accessing the fields of the struct, you will need to call the printCard method of the Card class. Look at my earlier example in the code for how to do that.
  8. Now you can compile and test your code. Note that we are using the C++ compiler now, not C compiler.
    -bash-4.2$ clang++ main.cpp Card.cpp Deck.cpp
    

Part C: Find Bugs

Your next task is to find the bugs in the AList class through testing alone. If you need to, go back and reread the relevant portion of the first lab, which described different ways of finding bugs. In this case, we are looking for a bug in the implementation of a class with well-defined inputs and behaviors. You are not trying to anticipate all of the different errors a user could make.

This class is a list backed by an array. You may insert an element anywhere in the list, and the rest of the elements will be pushed later in the list. When you are testing a program of this nature, for each function, you need to look at different valid and invalid inputs. Think about a list of 10 elements. What are the valid inputs, and what are the invalid inputs? You need to test at the boundary between the invalid and valid ones - two tests at each boundary. You can also test lists of different numbers of elements, including an empty list, a list with one element, and a list with many element.

In order to create test cases, instead of running the program and testing it interactively, you will place your test cases in main. We have provided a starting main for you - test_list.cpp. This does not trigger any error conditions. Your job is to add code to that file until it triggers an error message. Once you find that error message, it will tell you how to save your .cpp file. There are two things you need to do at this point:

  1. Copy your bug-revealing code to the appropriate file:
    -bash-4.2$ cp test_list.cpp bug_[n].cpp
    
  2. Remove all printouts that are caused by the main code (other than the one that reports the bug).

To compile and test the code:

-bash-4.2$ clang++ test_list.cpp AList.o
-bash-4.2$ ./a.out

One word of warning: We will change the printf's in AList.o in the test environment, so do not hard-code any of the printf's into the file containing main. You will receive no points.

There are four bugs in all. If you cannot find all the bugs, you should submit an empty file, perhaps made by using touch (described in the first lab).

Submitting your Work

First, fill out the required fields in the provided README.txt file. Once you have filled this in, you may submit everything with the following command:

-bash-4.2$ turnin lab3@cs24 README.txt reading_questions.txt Card.cpp Card.h Deck.cpp Deck.h main.cpp bug_1.cpp bug_2.cpp bug_3.cpp bug_4.cpp

Acknowledgements

All content was graciously provided by Professor Diana Franklin, with slight formatting-related adaptation.