C/C++ Arena

Step 4 of 7

A Result type

optional says that something failed, but not why. When there are several ways to fail, the caller (and the user reading an error message) needs to know which one happened. A small Result type carries either a value or an error description. C++23 standardizes the idea as std::expected<T, E>; you'll often see hand-written versions like this one.

#include <iostream>
#include <optional>
#include <string>

struct AgeResult {
    std::optional<int> value;
    std::string error;
    bool ok() const { return value.has_value(); }
};

AgeResult parse_age(const std::string& s) {
    if (s.empty()) return {std::nullopt, "empty"};
    int v = 0;
    for (char c : s) {
        if (c < '0' || c > '9') return {std::nullopt, "not a number"};
        v = v * 10 + (c - '0');
        if (v > 150) return {std::nullopt, "unrealistic"};
    }
    return {v, ""};
}

int main() {
    for (const char* in : {"42", "", "4x", "999"}) {
        AgeResult r = parse_age(in);
        if (r.ok()) std::cout << "'" << in << "' -> " << *r.value << "\n";
        else std::cout << "'" << in << "' -> error: " << r.error << "\n";
    }
}
'42' -> 42
'' -> error: empty
'4x' -> error: not a number
'999' -> error: unrealistic

How it works

Your task: money

The same structure, in this order: check the leading $ (also catches the empty string), then check there's at least one character after it, then check that every character after it is a digit while building the value, then compare with the cap. Checking the cap inside the loop protects you from overflow on inputs like $99999999999.

Your turn: write Result parse_money(const std::string& s) for amounts like "$2700":

Previous: optional for "maybe" Next: Passing errors up