Skip to content

Exercise 7 of 10 · Strings and formatting

The Scoreboard

What you will make

A game scoreboard where every name, score and bar lands in the same column, built by giving each value a fixed width before it prints.

The one new idea: std::setw and std::left/std::right line values up in columns

Every receipt, timetable and report you have ever read is text with values dropped into it at fixed widths. iostream's manipulators are how C++ writes that, and they behave a little differently from the format strings in most other languages — one setting sticks around until you change it, the other resets after a single value.

Go straight to the code ↓

A field width for the very next value

Every std::cout line so far printed each value at whatever length it happened to be. 12 took two characters, Ben took three, and nothing lined anything up under anything else. As soon as you care what a row looks like next to the row above it, that stops being good enough.

std::setw, declared in <iomanip>, gives the single next thing sent to std::cout a fixed-width field:

C++
std::cout << std::setw(6) << 42 << std::endl;

That prints 42 preceded by four spaces, filling out a field six characters wide. The width applies once and then resets — the value straight after it goes back to printing at its own natural length, with no field at all, unless you give it its own std::setw too. That is worth saying twice, because it is the single detail most likely to catch you out: std::setw is not sticky.

std::left and std::right are the opposite: they stick

Alignment is a separate setting from width, and it behaves the opposite way.

C++
std::cout << std::left << std::setw(7) << player
          << std::right << std::setw(3) << score;

std::left tells the stream to pad on the right of whatever comes next, pushing the value itself to the left edge of its field. std::right does the reverse. Once you set one of them, it stays in effect for every value printed afterwards — through as many std::cout calls as you like — until something sets the other one. That is why this line only needs std::right written once, even though it is about to format two more numbers on the cafe receipt later in this path: say it once, and it governs everything numeric that follows.

A worked example

A shelf label rather than a scoreboard, so the answer to this one stays yours to write:

C++
#include <iostream>
#include <iomanip>
#include <string>

int main() {
    std::string fruit = "Plum";
    int count = 7;
    std::cout << "[" << std::left << std::setw(6) << fruit
              << "][" << std::right << std::setw(4) << count << "]" << std::endl;

    fruit = "Fig";
    count = 112;
    std::cout << "[" << std::left << std::setw(6) << fruit
              << "][" << std::right << std::setw(4) << count << "]" << std::endl;
}
Output
[Plum  ][   7]
[Fig   ][ 112]

The square brackets only exist to make the padding visible. Plum is four characters in a six-wide field, so two spaces trail it; Fig is three, so three spaces do. Both closing brackets land in the same column regardless, and so do both numbers, whether they are one digit or three.

Your turn

The editor holds a scoreboard with three players on it. Each one gets a name, a score, and bar = std::string(score, '#'), which builds a string of that many # characters — the two-argument std::string constructor here means "this many copies of this one character," and it has nothing to do with the algorithms library the compiler in this playground does not have; it is an ordinary part of <string>.

Press Run before changing anything. Ana and Ben print as tidy rows. Cleo's row is ragged, because her line still sends three plain values to std::cout with no width on any of them:

C++
std::cout << player << " " << score << " " << bar << std::endl;

Rewrite that one line to match the two rows above it, using std::setw and std::left/std::right exactly as they do. Change nothing else.

Then change Cleo's score to 11 and run it again. Her bar should grow and her row should stay in column — that is the proof the layout is being worked out rather than typed in. Put the 5 back afterwards.

If something goes wrong

If a row prints but drifts out of column, the likeliest cause is a missing std::setw on one of the two values — remember it resets after every single item, so both the name and the score need their own.

If the whole row is pushed to the left, including the score, std::right was left out before it and the alignment left over from the name carried on to the number as well.

Nothing here can break. Change a number, press Run, and read your own board.

Write your code

Runs in your browser. Press Run (or Ctrl/Cmd+Enter) and the output is checked for you.

Ctrl/Cmd+Enter to run

Press Esc then Tab to move keyboard focus out of the code editor.

Ready
Output will appear here after you run your code.

The runtime is starting in the background. You can type now — it will be ready before you are.

The answer appears here once you have run your code at least once.

Things that often go wrong here

Expecting std::setw to stick around for the next value
std::setw(7) << player only widens player. The very next thing sent to std::cout — even std::right on its own — is back to width 0, its unpadded natural length. Each value that needs a fixed width needs its own std::setw right before it, every single time.
Forgetting that std::left and std::right are sticky
Unlike std::setw, an alignment flag stays set until something changes it. Set std::left once for the name and never set std::right afterwards, and the score is left-aligned too — 12 sits hard against Ana instead of sitting three characters over, right where PTS expects it.
Writing std::setw(score) by mistake instead of std::setw(3)
std::setw takes the width of the field, not a value to print. std::setw(score) asks for a field as many characters wide as the score itself — for Ana that is a field 12 characters wide holding a 2-character number, so it drifts instead of lining up under PTS.
Reaching for std::string::size() to pad the bar
The bar is meant to print at its natural length — the whole point of a bar chart is that a bigger score draws a longer bar. Adding a width to it would flatten every score's bar to the same length and defeat what the chart is for. Only the name and the score columns need a fixed width here.

Want a blank editor instead? Open the C++ playground.