Exercise 6: STL & Templates

Due:   Thursday, July 30 by 11:59 pm
Rating:   3 (note)
Learning Objectives:

Problem Description

Write a C++ program that reads a text file whose name is given as a single command-line argument, counts how many times each string appears in the file, and then prints every string alongside its count in sorted order. For this exercise, a "string" is any run of non-whitespace characters, and two strings count as the same only if they match exactly, including capitalization.

Reading the file is the interesting part: rather than reading strings directly, you must factor the read-and-parse step out into a helper template function with exactly this prototype:

template <typename T> bool ReadValue(ifstream& in, T* const output);

Each call to ReadValue reads a single token from the input stream in, attempts to parse that token as a value of type T, and stores the result in *output. It returns true on success and false if either the read or the parse fails (for example, a conversion error or reaching EOF). Your main should count strings by repeatedly calling ReadValue<string> until it returns false.


Output Format

Print one string per line, sorted using the ordinary string ordering (operator<). Each line is the string, followed by a single space, followed by its count, with no other whitespace before the string or after the count.


Example

The short examples below use trimmed-down inputs so they are easy to follow. The files in the starter code are longer, but the behavior is the same.

First, counting strings. If you run ./ex6 words.txt where words.txt contains:

cat dog cat bird cat dog

the program prints:

bird 1
cat 3
dog 2

The same counting logic also works for other types. Reading ints from a file that contains:

3 1 3 2 3

would print:

1 1
2 1
3 3

We come back to how that second version works in the Implementation Notes; the only thing that changes is the type you hand to ReadValue.

The starter code gives you quotes.txt, nonewline.txt, and numbers.txt so you can try the real inputs right away.

Implementation Notes

Reading the File

You do not need to do any extra processing of the input, such as stripping whitespace or punctuation; the stream extraction operator (>>) already splits on whitespace and does the parsing for you. To distinguish a clean end-of-file from a real error, investigate the stream's good(), bad(), and eof() methods. More information on reading files in C++ .

Writing the Template

Good template code assumes as little as it can about the type it gets instantiated with. Here, the only things you should count on are that T has a default constructor and an operator>> that reads from an input stream. Don't reach for anything that only makes sense for a string.

That is exactly what the int example above is showing. If main declared int value; and called ReadValue<int>(infile, &value) into a map<int, size_t>, the same ReadValue would count integers instead of words. The template body never changes; only main does. That is a handy way to sanity-check your own code: if it still works after swapping in int, you know you didn't hard-code string anywhere.

Style Focus

Robustness

This program takes input from both the command line and a file, and either can fail: a missing argument, a filename that can't be opened, or a stream error mid-read. Check for these, and on an unrecoverable error print a useful message to standard error and return EXIT_FAILURE from main. For this exercise you may treat a parsing error as unrecoverable.

Submission

Submit your work by creating an ex6-submit tag in your exercise repo before the assignment deadline. Your code must live in ex6/ex6.cc, matching that path and capitalization exactly. Any other files in the ex6 folder are ignored, so you may leave extra files there.


Requirements for Full Credit

For full credit, your code must:

  • Compile without errors or warnings on CSE Linux machines (lab workstations, attu, or CSE home VM) with the command:
    $ g++ -Wall -g -std=c++17 -o ex6 ex6.cc
  • Have no runtime errors, memory leaks, or memory errors (g++ and valgrind).
  • Have a comment at the top of your .cc file with your name(s) and CSE or UW email address(es).
  • Be pretty: the formatting, modularization, variable and function names, commenting, and so on should be consistent with class style guidelines. Additionally, the linter shouldn't have any complaints about your code (cpplint.py).
  • Be robust: your code should deal with hard-to-handle/edge cases and bogus user input (if there are any) gracefully.