Chapter 5 · Constants and Strings
Exercise · Chapter 5

Name-Badge / Greeting Formatter

You're writing the text engine behind a conference name-badge printer. Given a person's first and last name, it assembles every string the badge needs: the org header across the top, the full name "Ada Lovelace", a friendly greeting "Hello, my name is Ada Lovelace", a login "Ada.Lovelace", monogram initials "A.L.", and the name's length for layout. It's a little library of six small, const-correct functions — no main, no I/O — graded by unit tests.

The whole chapter is in the types. Every input a function only reads is a std::string_view (a cheap read-only window — no copying), every function that builds text returns an owning std::string, and the org name, separator, and badge width are constexpr named constants instead of literals sprinkled through the logic. And because Chapter 5 has no loops yet, you build each result by assembling strings — concatenate with +=, peek the first letter with .front(), measure with .size() — never by scanning character-by-character.

Your tasks

  1. badgeHeader() — return the org name. It's the kOrgName constant (a std::string_view); the function returns a std::string, so you copy the view into an owning string: std::string { kOrgName }.
  2. fullName(first, last) — build "First Last" (one space between) by concatenating into a std::string.
  3. greeting(first, last) — build "Hello, my name is First Last". Reuse fullName — don't re-join the names by hand.
  4. username(first, last) — build "first.last", joining with the kUsernameSep constant (not a hard-coded '.').
  5. initials(first, last) — build "F.L." from each name's .front(). Edge case: .front() on an empty view is undefined behavior, so guard each name with if (name.size() > 0) and contribute nothing for an empty one.
  6. nameLength(first, last) — return the length of the full name via .size(). It's unsigned, so static_cast<int>(...) it.

Success criteria

make test prints PASS ✅ all checks and exits 0. The grader in tests/tests.cpp calls every function with normal inputs and edge cases — empty names (initials("", "") == ""), single-character names (fullName("A","B") == "A B"), and the just-a-space full name (nameLength("", "") == 1). Until you fill in the tasks, the stubs return junk and make test shows FAIL ❌ listing each failing CHECK and its line. Turning that red into green is the whole exercise.

Concepts practiced
  • constexpr named constants, including a constexpr std::string_view string label (5.1, 5.2, 5.6, 5.8)
  • Killing magic numbers / literals by naming them (the separator, the width) (5.2)
  • std::string — owning, growable text you build and return (5.7)
  • std::string_view — cheap, read-only parameters that accept literals, strings, or views (5.8, 5.9)
  • Why a view does not implicitly become an owning string — explicit std::string { view } (5.8)
  • .front(), .size(), .empty(), and string concatenation with +=
  • Reused from earlier chapters: functions / headers / header guards (Ch 2), if and static_cast (Ch 4), value-initialization with {} (Ch 1)
Constraints
  • Implement the bodies only. Don't touch badge.h — it's the contract the grader relies on (function names, parameter and return types).
  • Read-only parameters stay std::string_view; text you build is returned as a std::string. Don't change the signatures.
  • Use the named constants from badge.h (kOrgName, kUsernameSep) instead of re-typing their literal values.
  • No loops (Chapter 8) — every task is fixed string assembly. Allowed tools: + / +=, .front(), .size(), if with a relational test (> 0), static_cast.
  • Don't add I/O — this is a pure library. The grader calls your functions directly.
Build & run locally
shell
make           # compile starter/badge.cpp  (it builds out of the box)
make test      # grade YOUR code against the unit tests
make solution  # run the grader against the reference solution
make run       # same as `make test` (a library has no program of its own)
make clean
Hints
Task 1 — view vs. owning string

kOrgName is a std::string_view, but the return type is std::string. A view does not silently become an owning string (that would hide an expensive copy), so make the copy explicit: return std::string { kOrgName };.

Tasks 2 & 4 — concatenating into a std::string

Seed an owning string from the first view, then append onto it. A std::string_view and a char both append with +=:

C++
std::string result { first };
result += ' ';        // a char        (Task 2: the space)
result += last;       // a string_view
return result;

For Task 4 the join character is the named constant: result += kUsernameSep;.

Task 3 — compose, don't repeat

Build the fixed prefix, then append the result of fullName:

C++
std::string result { "Hello, my name is " };
result += fullName(first, last);   // fullName returns a std::string
return result;
Task 5 — the empty-name guard

.front() reads the first character but is undefined behavior on an empty view. Guard each name independently so an empty one simply contributes nothing:

C++
std::string result {};
if (first.size() > 0) { result += first.front(); result += kUsernameSep; }
if (last.size() > 0)  { result += last.front();  result += kUsernameSep; }
return result;

(.empty() would read more nicely, but > 0 keeps us to the relational operators already seen in Chapter 4.)

Task 6 — size() is unsigned

.size() returns std::size_t (unsigned). Returning it directly as int warns under -Wall -Wextra, so convert deliberately: return static_cast<int>(fullName(first, last).size());

Stretch goals
  • Make the username lowercase ("Ada.Lovelace""ada.lovelace"). That needs to walk each character, i.e. a loop (Chapter 8) plus std::tolower.
  • Add padBadge(text) that centers text inside kBadgeWidth columns — the constexpr int kBadgeWidth is already declared in badge.h waiting for you.
  • Trim surrounding spaces from a name with string_view's .remove_prefix() / .remove_suffix() before building (notes 5.9) — needs a loop to find the spaces.
  • Validate input and reject empty both names with an error path (Chapter 9's assert / error handling).
starter/badge.cpp C++
// ============================================================================
// Chapter 5 — Constants and Strings · Project: Name-Badge / Greeting Formatter
// starter/badge.cpp  —  YOU EDIT THIS FILE.
// ============================================================================
//
// Fill in the six >>> YOUR CODE HERE <<< blocks. Each maps 1:1 to a TASK in the
// README. The function signatures and the named constants come from ../badge.h
// (already complete) — read it first; it explains WHY each type was chosen.
//
//   make build   compile this file (it already compiles — stubs return junk)
//   make test    grade YOUR code  ->  RED until you finish, then GREEN
//   make solution / make test-solution   the reference, if you get stuck
//
// Right now every function returns a placeholder, so `make build` succeeds but
// `make test` FAILS. Turning that red into green is the exercise.
//
// REMINDERS for this chapter:
//   * std::string_view params are read-only windows — cheap to pass, no copy.
//   * Build text by CONCATENATING with + (or +=) into a std::string you return.
//   * A std::string_view can be appended to a std::string directly.
//   * .front() reads the first char; .size() gives the length (UNSIGNED).
//   * NO loops — every task is a fixed bit of string assembly.

#include "../badge.h"     // the contract: signatures + the kOrg/kUsername/kBadge constants

// ─── TASK 1: return the organization name ───────────────────────────────────
// Return the kOrgName constant from badge.h. It is a std::string_view; this
// function returns a std::string, so the view is copied into an owning string
// on the way out (that is exactly what the caller wants — its own text).
//
//   >>> YOUR CODE HERE <<<
//
// ────────────────────────────────────────────────────────────────────────────
std::string badgeHeader()
{
    return {};   // STUB: empty string. Replace with the real header.
}

// ─── TASK 2: build "First Last" ─────────────────────────────────────────────
// Concatenate first, a single space " ", and last into one std::string.
// Hint: std::string result { first }; then result += " "; then result += last;
//       (a std::string_view appends straight onto a std::string with +=).
//
//   >>> YOUR CODE HERE <<<
//
// ────────────────────────────────────────────────────────────────────────────
std::string fullName(std::string_view first, std::string_view last)
{
    (void)first;        // STUB: silence "unused parameter" until you use them.
    (void)last;
    return {};          // replace with "First Last".
}

// ─── TASK 3: build "Hello, my name is First Last" (REUSE fullName) ──────────
// Don't re-assemble the name by hand — call fullName(first, last) and build the
// greeting around it. Composing small functions is the point.
//
//   >>> YOUR CODE HERE <<<
//
// ────────────────────────────────────────────────────────────────────────────
std::string greeting(std::string_view first, std::string_view last)
{
    (void)first;
    (void)last;
    return {};          // replace with "Hello, my name is First Last".
}

// ─── TASK 4: build "first<sep>last" using kUsernameSep ──────────────────────
// Join first and last with the kUsernameSep constant ('.') from badge.h —
// do NOT hard-code a '.' here; use the named constant so the separator lives in
// one place. Result for ("Ada","Lovelace") is "Ada.Lovelace".
//
//   >>> YOUR CODE HERE <<<
//
// ────────────────────────────────────────────────────────────────────────────
std::string username(std::string_view first, std::string_view last)
{
    (void)first;
    (void)last;
    return {};          // replace with "first.last".
}

// ─── TASK 5: build initials "F.L." with an EMPTY-name guard ─────────────────
// For each name: IF it is non-empty (.size() > 0), append its .front() character
// followed by kUsernameSep. If it is empty, append nothing for it (do NOT call
// .front() on an empty view — that is undefined behavior). Use an `if` guard.
//   ("Ada","Lovelace") -> "A.L."   ("","Lovelace") -> "L."   ("","") -> ""
//
//   >>> YOUR CODE HERE <<<
//
// ────────────────────────────────────────────────────────────────────────────
std::string initials(std::string_view first, std::string_view last)
{
    (void)first;
    (void)last;
    return {};          // replace with the initials string.
}

// ─── TASK 6: report the length of the full name with .size() ────────────────
// Build the full name (reuse fullName!), then return its .size(). Because
// .size() is UNSIGNED (std::size_t) and this returns int, convert it:
//   return static_cast<int>(theName.size());
//
//   >>> YOUR CODE HERE <<<
//
// ────────────────────────────────────────────────────────────────────────────
int nameLength(std::string_view first, std::string_view last)
{
    (void)first;
    (void)last;
    return 0;           // replace with the real length.
}
Run
Submit
Run in your browser — coming soon For now: copy or download the files and use make test locally (see “Build & run locally” above).