Skip to content

Module 16 — Self-Assessment

This module has no quiz score, because the capstone is assessed with the published rubric rather than with marks. What follows is different: a diagnostic.

Answer on paper, then read the answers. Anything you miss points at a module worth revisiting before you write the design report — and the audit at the end is the real decision about whether the work is finished.


Part A — Definitions and vocabulary (one line each)

  1. What distinguishes a public header from an internal one? (A public header is what callers include; it must not require any internal header to compile, and it declares rather than implements. The check is mechanical: compile it alone.)
  2. What is a translation unit? (One source file after preprocessing — the compiler's unit of work. It is why a definition in a header exists once per including file.)
  3. State the One Definition Rule. (One definition per program for anything used in more than one translation unit; many declarations are fine.)
  4. What are the two fixes for a duplicate definition, and what distinguishes them? (Move the definition to a single source file — the default for interface functions; or mark it inline, which is a standard permission for a definition per translation unit, not an optimisation hint.)
  5. What does an unnamed namespace give a name, and what does nm show for it? (Internal linkage; a lowercase t in the symbol table, with (anonymous namespace) in the demangled name.)
  6. What is std::stringstream for? (In-memory parsing and formatting — the tool that lets a file reader use getline throughout and then parse each line field by field.)
  7. What are the three stream flags, and what does each mean? (fail: an extraction or open produced no value, recoverable in principle. eof: end of input reached — normal at the end of a read loop, and set even by a successful final extraction. bad: the stream itself is unreliable, so its data cannot be trusted.)
  8. What does a save → load round trip prove, and what case does it have to include? (That every field survives the format; and the empty catalogue, because every naive implementation assumes at least one record.)
  9. What four categories should a test suite cover? (Invariants, boundaries, both halves of a rule that divides, and failure paths.)
  10. What does an assertion prove that printing does not? (Printing shows a value a human may compare; an assertion fails when the value is wrong, so it works unattended and catches a regression. A printing test cannot distinguish a correct implementation from a wrong one.)
  11. What is the definition of a refactoring? (Improving structure without changing behaviour — which is why a test edited to accommodate one is evidence the behaviour changed.)
  12. What makes a design evaluation credible rather than a preference? (Context first; the chosen option's cost named; a real rejected alternative with its cost; a condition that would reverse the decision; and numbers where numbers exist.)
  13. Why is the design report's reversal line the module's assessment? (The code demonstrates level 6; judging the code is level 8. And a reversal with the cost of reversing it cannot be bluffed — only the author knows that number.)

Part B — Prediction and diagnosis

14. Predict, then check

// main.cpp
#include "catalogue.h"

int main() {
    library::Catalogue catalogue;
    catalogue.add({"Compilers", "Aho", 2006});
    std::cout << library::report(catalogue);
    return 0;
}

catalogue.h declares std::string report(const Catalogue&); and format_record(const Book&) in namespace library::internal. catalogue.cpp defines report and includes record_format.h (which defines nothing but declares format_record); record_format.cpp defines format_record. All three .cpp files are in the build command.

What does the program do, and where exactly is the format helper declared?

Full credit: it builds and runs, because the definition of format_record exists in exactly one translation unit — record_format.cpp — and the declaration catalogue.cpp needs comes from record_format.h. main.cpp cannot see that declaration at all, and does not need to. If format_record's body were in record_format.h without inline, the build would fail at the link step with collect2.exe: error: ld returned 1 exit status on this toolchain, and nm -C on the two objects would show two T format_record(...) lines. That is the whole of the ODR story: the definition's location decides whether it is legal, and the fact that it is only called from one file is irrelevant.

15. Diagnose, in two commands

A build fails with a single line and nothing else:

collect2.exe: error: ld returned 1 exit status

You believe it is one of: a duplicate definition from a header, a source file missing from the build, or a function defined in an unnamed namespace. What do you run, and what would each of the three look like in the output?

Full credit: g++ -std=c++17 -c file.cpp -o file.o for each file, then nm -C *.o and read the lines.

  • Duplicate definition: the same name appears as T ... in two objects. Fix: define it once, or inline.
  • Missing source file: the name appears as U ... in the object that calls it and as nothing anywhere else — no T at all. Fix: add the file to the build command.
  • Unnamed namespace: the caller shows U library::internal::scale(int) and the definer shows t (anonymous namespace)::scale(int) — capital U and lowercase t, with different names. Fix: decide whether the function is private; if it is, delete the declaration; if not, get it out of the unnamed namespace.

Also worth a mark: the compiler may already have told you about the third case, as error: 'int {anonymous}::scale(int)' defined but not used [-Werror=unused-function] — which is why -Wall -Wextra is not optional in multi-file work.

16. Diagnose the loader

1. missing file            : rejected - the file could not be opened
4. count over the limit    : rejected - line 2: declared 1000000 records, over the limit of 10000
6. count that does not fit : rejected - line 2: the declared count '99999999999999999999999' does not fit in a size_t
9. a record deleted by hand: rejected - line 2: declared 3 records, found 1 - a record was added or deleted by hand
11. into a live catalogue : rejected - line 3: malformed record 'Compilers|Aho'
    records before 2, after 2 (the half-read records were discarded)

Name the rule each of these five lines enforces.

Full credit: (1) check that the file opened before trusting anything read from it; (4) validate before allocating — a quantity from a file is not a quantity you can trust; (6) a conversion from text can fail, and std::stoul throws std::out_of_range, which untrusted-input handling must catch; (9) cross-check a declared quantity against the data actually read — a disagreement is itself the finding; (11) parse into a local container and assign only after full validation, so a rejection leaves the program's state untouched. Full credit also notes what is not enforced: a blank line is accepted, because strictness belongs where the ambiguity is, not everywhere.

17. The test that would not fail

void test_cap() {
    std::cout << "  fine at 15 days: " << schedule.pence_for(15, false) << "\n";
}

The cap should be 14 days and the implementation uses 15. What does this print, what would the developer conclude, and what is the smallest change that turns it into a test?

Full credit: it prints 45; the developer compares that with 42 in their head — a comparison the code never makes — and concludes incorrectly if they are not watching for it. The smallest change is a comparison with a failure path: check(schedule.pence_for(15, false) == 42, "the cap holds one day past 14"), plus a counter and a non-zero exit status. Worth extra credit: the boundary cases (0, 1, exactly at the cap, one past it, far past it) and the demonstration method — introduce the bug deliberately and watch the suite fail, which is how examples/module-16/06-tests-that-assert/ proves its own value.

18. The question that decides whether the capstone is done

Write your own design report's reversal, in the five-line form, using your capstone. Include the cost of reversing it.

Full credit: a real decision, a real cost for the option you chose, a real alternative with its own cost, and a reversal whose cost you can state — a number of files, a migration, a rewrite. Partial credit for a genuine reversal without a cost. No credit for a report with no weakness, or for a reversal that is really a compliment ("I would have started earlier").


Marking yourself

Part What a mark means
A (1–13) One mark each, for substance rather than phrasing. These are diagnostic: every miss names a module to revisit
B (14) The definition's location identified as what makes it legal, and the failure mode named
B (15) Two commands, three signatures: T twice, U with no T, and U against lowercase t
B (16) Five rules named, plus the blank-line exception
B (17) Why the printing test cannot fail, and the smallest change that fixes it
B (18) A real reversal with its cost of reversal

13 of 18 is comfortable, and questions 15, 17 and 18 are not optional — they are the exit level (8) in three different forms: diagnosing from symbols, knowing what a test must do, and evaluating your own design with a number attached.

Then run the audit, which is the actual decision about whether the capstone is finished. Every box needs evidence you can point at.

Structure

  • [ ] It builds from the documented command in a clean copy, with no warnings
  • [ ] The public header compiles alone, with no internal include
  • [ ] No duplicate symbols, and every header guarded

Persistence

  • [ ] The format is documented, with a version marker and an escape rule
  • [ ] A round trip preserves every field, including on an empty catalogue — output pasted into my notes
  • [ ] Missing, empty, truncated and malformed files each produce a line-numbered message
  • [ ] A rejected load left the previous data unchanged, shown by counts, not claimed

Protection

  • [ ] The suite covers invariants, boundaries and failure paths, and has never been edited to pass
  • [ ] I introduced a deliberate bug, saw the suite fail, and kept the test that failed
  • [ ] A second run of the whole suite, from a clean build, passes

Design and evaluation

  • [ ] Every resource is owned by RAII, with no leak on the throwing paths
  • [ ] The UML diagram matches the declarations, including multiplicities
  • [ ] Every pattern names the requirement that justified it, and one is documented as declined
  • [ ] The report names the cost of each choice, the option rejected, and one decision I would reverse with the cost of reversing it
  • [ ] One claim in the report is a file count from an extension I actually attempted

If any box is unticked, the section that covers it is in Concepts — structure in sections 1–4, persistence in 5–8, protection in 9–11, and evaluation in 12–14 — with the diagnostics in Debugging and the rubric in Projects.


Continue

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