Skip to content
Topos Uranos

Summary

Input is the boundary through which the world enters a program, and the world does not respect specifications. This lesson teaches how to read without trusting: how to read whole lines with std::getline and avoid the empty line that appears when it is mixed with >>; how to query the state of a stream and use it to distinguish three situations that the lesson Input, output and errors only saw jumbled together (invalid input, the number that does not fit and the end of the data); how to recover from a failed read with clear and ignore; and, as the safest approach, how to read each item as text and convert it with std::from_chars, accepting it only if the conversion consumed the whole text. It ends with a function readInt, with its contract, that the rest of the course can reuse.

Learning objectives

  1. Read whole lines with std::getline, explain why a getline that follows a read with >> returns an empty line, and avoid it with std::ws.
  2. Query the state of a stream after a read with fail() and eof(), and use it to distinguish invalid input, a number that does not fit in its type and the end of the input.
  3. Write a reading loop that recovers from invalid input with clear and ignore, and that ends if the input runs out.
  4. Validate a number read as text with std::from_chars, requiring that there be no error and that the whole string be consumed, and encapsulate the validation in functions with contracts.

The boundary, once more

The lesson Input, output and errors established three facts about std::cin >> n: the read stops, without consuming it, at the first character that cannot continue the number; if it manages to read none, the stream is left in an error state and every subsequent read does nothing; and if (std::cin) tells whether something failed. The lesson Loops added that the condition while (std::cin >> value) ends both when the input runs out and when invalid input arrives. Now, with those tools a program detects that something went wrong, but it does not know what, nor can it do anything but give up.

This lesson starts from a design principle, that of defensive programming at the boundary: every item that comes in is validated before it is used, and the program that validates it distinguishes invalid input, which admits a correction (asking for it again, skipping it, reporting it), from the end of the data, which does not. If this is so, three things are needed: reading the item as it was written, that is, a line; knowing why a read failed; and giving the stream back its ability to read.