Skip to content

Module 16 — Lab 16: the finished application

Levels: [L5 Design → L8 Evaluate] · Extends: Lab 15 · Time: 15–20 hours, in milestones


The brief

The library system becomes an application.

Not a bigger program — an application: one a stranger could navigate, whose catalogue survives being closed and reopened, that behaves sensibly when a file has been edited by hand, whose behaviour is protected by tests, and whose design is written down including the one decision you would reverse.

Everything needed has already been taught. What is new is that the parts have to be present at the same time, and that the last step is judging the result rather than adding to it.

★ The deliverable that is marked first is not the code. It is the design report, read alongside the code it describes — because the code alone demonstrates level 6, and the exit level of this module is 8.


Milestones

Work in this order. Each milestone ends with something you can show, and each is a checkpoint where the next one becomes easier — which is the whole reason for the order.

# Milestone Done when
1 Structure The program builds from a documented command, with a public header that compiles alone, an internal header, and a main.cpp that includes only the public one
2 Persistence Save and load work, a save–load round trip preserves every field (including an empty catalogue), and the format is documented with a version marker and an escape rule
3 Robustness The three hand-edited corruptions below are each rejected with a line number, and a rejection leaves the loaded data untouched
4 Tests Assertions cover invariants, boundaries and failure paths; one deliberate bug was introduced and caught, and the test that caught it is kept
5 Refinement One working file was split along a responsibility boundary, with the tests green before and after and unmodified
6 Evaluation The design report exists, with the decision you would reverse and the cost of reversing it

Requirements

  1. A multi-file layout with a documented build command. Directories if they help; an explicit command either way. A new reader must find the entry point in ten seconds, and the read order must be written down. (Interacts with Module 2: the header/source split, at scale.)
  2. A public header that includes nothing internal, and internal declarations that stay internal. Prove the first by compiling the public header alone.
  3. Definitions in exactly one translation unit, or inline. No duplicate symbols, and every header guarded.
  4. Namespaces separating your interface from your internals — including an unnamed namespace for helpers, so they cannot collide with a same-named function elsewhere.
  5. Persistence: load at start, save at exit, a documented text format, a version marker, and an escaping rule for the field separator.
  6. A save → load round trip that preserves every field, including on an empty catalogue, demonstrated by a test or by program output you paste into your notes.
  7. Handling for a missing file, an empty file, a truncated file and a malformed record — each with a clear message containing a line number, and no crash. (Interacts with Module 13: every one of these is a failure path, and each is tested.)
  8. The corruption log: hand-edit a saved file to break it three ways — truncate it, corrupt a field, delete a record's separator — and record exactly what the program did for each, verbatim.
  9. A test suite with assertions covering invariants, boundaries and failure paths, runnable with one documented command. At least one test must be shown failing after you deliberately introduce the bug it covers.
  10. Every resource managed by RAII, with no leak on any path — including the paths that throw.
  11. UML class diagram matching the code, with composition, aggregation and association notated correctly and multiplicities marked. (Interacts with Module 15.)
  12. The design report: the decisions, the alternatives rejected, the costs accepted, and one decision you would reverse with the reason you did not reverse it.
  13. It builds with no warnings using the course's compile command, from a clean copy of the repository.

The three corruptions (requirement 8)

This is the lab's evidence that the loader takes its input seriously, and it cannot be answered by reading the code — the file has to be broken and the program run.

The corruption How to produce it What you record
Truncated Delete the last half of the last record The exact message, and whether the program kept the records it had already read
Corrupted field Change a year to twenty thirteen The message, and the line number it names
Missing separator Delete one \| from a record The message, and what the program did with the record's remaining fields

Then one more, which is the one that catches real bugs: load a good file, then a corrupt one, into the same running program, and print the record count after each. If the count changes, the loader has no strong guarantee — and examples/module-16/05-untrusted-files/ shows the guarantee stated as output:

11. into a live catalogue : rejected - line 3: malformed record 'Compilers|Aho'
    records before 2, after 2 (the half-read records were discarded)

What makes this lab different from Lab 15

Lab 15 asked for a design that survives the next change. Lab 16 asks for an application a stranger can use, trust and maintain — which adds three things no earlier lab required:

  • A boundary with the outside world. A file is user input; the program has to behave correctly when the input is hostile, not only when it is what you wrote.
  • Evidence rather than intention. A round trip is proof; "I check the stream" is not. The corruption log is proof; "the parser is robust" is not.
  • An evaluation of your own finished work. The report's reversal line is the assessment, and there is no way to bluff it: the cost of the reversal is a number only the author knows.

What to deliver

  • The application: all the files, with the build command documented.
  • The format documentation: the marker, the escaping rule, and what happens to a record that violates it.
  • The test suite, and the record of the deliberate bug you introduced and the test that caught it.
  • The corruption log, verbatim output.
  • The UML class diagram.
  • The design report, ending with the decision you would reverse and its cost.

Self-assessment checklist

It builds and runs

  • [ ] Builds from the documented command with g++ -std=c++17 -Wall -Wextra -Werror, no warnings
  • [ ] Builds from a clean copy of the repository, with nothing but the documented command
  • [ ] The public header compiles when placed alone in a one-line .cpp file

It persists

  • [ ] The catalogue loads at start and saves at exit
  • [ ] The format is documented, with a version marker and an escape rule for the separator
  • [ ] A save → load round trip preserves every field, demonstrated, including an empty catalogue
  • [ ] A missing, empty, truncated and malformed file each produce a message with a line number
  • [ ] A rejected load leaves the previously loaded data unchanged, demonstrated by counts

It is protected

  • [ ] Tests cover invariants, boundaries and failure paths, and run with one documented command
  • [ ] One deliberate bug was introduced, the suite failed, and the test that failed was kept
  • [ ] The suite has never been edited to accommodate a refactoring

It is designed

  • [ ] Every resource is owned by RAII, with no leak on the throwing paths
  • [ ] The diagram matches the declarations, including multiplicities
  • [ ] Every class's responsibility is one sentence, without "and"
  • [ ] Every pattern names the requirement that justified it, and one is documented as declined

It is evaluated

  • [ ] The report names the decisions, with the cost of each choice and of the option rejected
  • [ ] The report names one decision I would reverse, and what reversing it would cost
  • [ ] One claim in the report is a file count from an extension I actually attempted

Common ways this lab goes wrong

Symptom Cause
Every file is one long main.cpp split into three files with the same content The split followed line count, not responsibility
The public header includes internal headers "just in case" The interface was never decided, so nothing is private
The round trip passes but the file is unreadable The comparison tested two empty catalogues — the empty case needs a populated counterpart
The loader rejects files a text editor produced Strictness applied everywhere instead of where the ambiguity is
The test suite passes on every version of the code The tests print rather than assert
The corruption log says "handled gracefully" The program was not actually run against a corrupted file
The design report has no weakness in it The evaluation was not done
The reversal has no cost attached The reversal was a rhetorical flourish rather than a considered decision

Reference solution

No solution file is published for this lab. The capstone is a term's work rather than an evening's, and a published system would be a project to read instead of one to build. What you check your work against instead is the rubric — deliberately identical to the requirements above, so that the published criteria are what your own submission is measured by.

The model code for the skills this lab needs is in examples/module-16/, one program per skill, each already walked through on the concepts page where it is used:

Program What it models
01-multi-file-layout The file layout's one promise: main.cpp includes the public header and nothing internal
02-one-definition-fixed-twice A duplicate definition, and the two ways to fix it
03-stream-state An unchecked open that fails silently, and how the state is checked instead
04-persistence-round-trip A save-load-compare round trip, including the empty catalogue
05-untrusted-files A hand-edited file: missing, empty, truncated, or with a wrong record count
06-tests-that-assert The same test suite against a correct implementation and a clone with one rule changed

The rubric in Projects is the acceptance criteria for this lab — deliberately identical to the requirements above, so that the published rubric is what you can check yourself against.


Continue

Previous: Exercises · Next: Challenge problems · Module home: Module 16