CMSI 386
Homework #4
Due: 2018-11-20

For this homework assignment, you'll be both composing some interesting C++ programs, as well as answering questions about scope, lifetimes, memory management, threads, generics, and function argument passing.

Readings

Please read:

Instructions

You will be adding to the private GitHub repository you started in the previous assignment. All your work should be included in a new folder called homework4 which will appear at the top level of the repo:

  .
  ├── README.md
  ├── .gitignore
  ├── homework1/
  │   └── (existing homework 1 files)
  ├── homework2/
  │   └── (existing homework 2 files)
  ├── homework3/
  │   └── (existing homework 3 files)
  └── homework4
      ├── written-exercises.md
      ├── queue.h
      ├── queue_test.cpp (this is given to you below)
      ├── say.cpp
      └── wordcount.cpp

I encourage you to work in pairs. Please submit only one solution set for your team. It does not matter under whose GitHub account your work is stored. What matters, if you wish to not receive a zero, is that I can clone your repo and run my tests. Since the repo is to be private, please allow me as a contributor to your repo (so I can run and comment on your work). My github name is rtoal.

Make sure you are using a linter. Non-linted code will be severely marked down to the point where your grade is not a passing one.

Your homework submission will be the state of your repository on the branch master, at the due datetime, which is 11:59 p.m. on the due date in the America/Los Angeles time zone.

Setting up your Project

Here are instructions for creating your project. I assume you are already signed up with GitHub, already know how to create private repositories and clone them, and know the basics of git.

  1. Create the homework4 folder.
  2. Add whatever lines are appropriate to your .gitignore file (in the top-level folder of your repo). The files you need to add vary greatly depending on what C++ enviroment you decide to use. Visual Studio, XCode, and other tend to create a bunch of project files. Intermediate files have all kinds of different suffixes depending on your operating system. It is up to you to do your research and KEEP YOUR REPOSITORY CLEAN.

The Problems

Write answers to Problems 1–6 in the file written-exercises.md in lovely Markdown format. Use this opportunity to learn Markdown if you don’t already know it. For Problems 7–9, you will find instructions telling you where to place your answer in the problem descriptions below.

  1. (5 pts) Given the C++ declaration:
    struct {
      int n;
      char c;
    } A[10][10];
    
    On your machine, find the address of A[0][0] and A[3][7]. Explain why these values are what you found them to be.
  2. (5 pts) Explain the meaning of the following C++ declarations:
    double *a[n];
    double (*b)[n];
    double (*c[n])();
    double (*d())[n];
    
  3. (5 pts) Consider the following declaration in C++:
    double (*f(double (*)(double, double[]), double)) (double, ...);
    
    Describe rigorously, in English, the type of f.
  4. (5 pts) What happens when we “redefine” a field in a C++ subclass? For example, suppose we have:
    class Base {
    public:
      int a;
      std::string b;
    };
    
    class Derived: public Base {
    public:
      float c;
      int b;
    };
    
    Does the representation of a Derived object contain one b field or two? If two, are both accessible, or only one? Under what circumstances? Tell the story of how things are.
  5. (5 pts) What does the following C++ program output?
    #include <iostream>
    int x = 2;
    void f() { std::cout << x << '\n'; }
    void g() { int x = 5; f(); std::cout << x << '\n'; }
    int main() {
      g();
      std::cout << x << '\n';
    }
    
    Verify that the answer you obtained is the same that would be inferred from apply the rules of static scoping. If C++ used dynamic scoping, what would the output have been? (Note: you may need to read about static vs. dynamic scoping.)
  6. (5 pts) Suppose you were asked to write a function to scramble (shuffle) a given array, in a mutable fashion. Give the function signature for a shuffle function for (a) a raw array, and (b) a std::array.
  7. (20 pts) Write a C++ application, in the file wordcount.cpp that reads standard input and displays a word count table to standard output. Normalize and lowercase the file text. Sort the output by number of occurrences descending. For example, if the input file is:
    The tests, my lord,have failed! FAILED
        have   the   tests,   they   have.
    
    then your program should output:
    have 3
    failed 2
    tests 2
    the 2
    lord 1
    my 1
    they 1
    
    “Words” are made up of Unicode letters only. Every other character is ignored. Use whatever wonderful functions you can find from the standard library. (In fact, one of the learning objectives for this problem is that you gain experience by looking through the standard library.)
  8. (20 pts) Implement the famous say function from the previous two homeworks, in C++. Put the function, together with a main function loaded with assert calls to validate that it works, in the file say.cpp.
  9. (30 pts) Write, in the file queue.h, an implementation of a generic singly-linked queue (template) class, implemented using nodes and pointers. The queue object should have three fields: (1) a pointer to the head node, (2) a pointer to the tail node, and (3) the current size of the queue (its number of elements). You may use smart pointers or raw pointers (whichever you would enjoy practicing with more). Support move semantics but prohibit copying. Expose public methods enqueue, dequeue, and get_size, and make sure queues can be written to ostreams with the << operator. Throw the standard exception underflow_error when trying to dequeue from an empty queue.

    For reference, your queue objects should look like this:

    singlylinkedqueues.png

    Use the following test program. Note that since it is essentailly impossible for a unit test to check that you have prohibited copying, because this is asking you to detect compile-time errors, I’ve put a note in the comments that should help your testing.

    queue_test.cpp
    #include <iostream>
    #include <string>
    #include <cassert>
    #include "queue.h"
    
    using namespace std;
    
    void test_empty_queue_has_zero_size() {
      Queue<string> q;
      assert(q.get_size() == 0);
    }
    
    void test_some_insertions_and_deletions() {
      Queue<string> q;
      q.enqueue("one");
      assert(q.get_size() == 1);
      q.enqueue("two");
      q.enqueue("three");
      assert(q.get_size() == 3);
      assert(q.dequeue() == "one");
      assert(q.get_size() == 2);
      assert(q.dequeue() == "two");
      assert(q.dequeue() == "three");
      q.enqueue("four");
      assert(q.get_size() == 1);
    }
    
    void test_no_copies() {
      Queue<int> p;
      Queue<int> q;
      // No way to test this at run time, but uncomment the following lines
      // and look for compile time errors.
      // p = q;
      // Queue<int> r(p);
    }
    
    Queue<int> one_two_three() {
      Queue<int> q;
      for (int i = 1; i <= 3; i++) q.enqueue(i);
      return q;
    }
    
    void test_moves() {
      // Assignment of a temporary is a move
      Queue<int> p = Queue<int>();
    
      // Construction via a function return call is a move
      Queue<int> q = one_two_three();
      assert(q.get_size() == 3);
      assert(q.dequeue() == 1);
    
      // Test move assignment
      q = one_two_three();
      assert(q.get_size() == 3);
      assert(q.dequeue() == 1);
    }
    
    void test_dequeue_from_empty_queue_throws_underflow_error() {
      Queue<bool> q;
      try {
        q.dequeue();
        assert(false);
      } catch (const underflow_error& ue) {
        assert(true);
      } catch (...) {
        // Caught the wrong exception
        assert(false);
      }
    }
    
    int main() {
      test_empty_queue_has_zero_size();
      test_some_insertions_and_deletions();
      test_dequeue_from_empty_queue_throws_underflow_error();
      test_no_copies();
      test_moves();
      cout << "All tests passed\n";
    }