C/C++ Arena

Step 2 of 7

Formatting your own types

std::format knows the built-in types and strings. To make it understand your type, you specialize std::formatter for it, the same way you taught streams with operator<<.

The easiest way is to reuse an existing formatter: inherit from std::formatter<std::string> (which already parses width, alignment and fill), build your text, and pass it to the base class's format. Then every spec that works for strings works for your type too.

#include <format>
#include <iostream>
#include <string>

struct Temp {
    double celsius;
};

template <>
struct std::formatter<Temp> : std::formatter<std::string> {
    auto format(const Temp& t, std::format_context& ctx) const {
        std::string text = std::format("{:.1f}C/{:.0f}F", t.celsius, t.celsius * 9 / 5 + 32);
        return std::formatter<std::string>::format(text, ctx);
    }
};

int main() {
    Temp today{21.5};
    std::cout << std::format("today: {}\n", today);
    std::cout << std::format("[{:>14}]\n", today);     // width and alignment come for free
    std::cout << std::format("[{:-<14}]\n", Temp{-3});
}
today: 21.5C/71F
[     21.5C/71F]
[-3.0C/27F-----]

The text 21.5C/71F is 9 characters, so {:>14} pads it with 5 spaces on the left, and {:-<14} pads with dashes on the right.

How it works

Your task: cents to dollars

Split the cents with / 100 and % 100, and zero-pad the cents part to two digits: std::format("${}.{:02}", c / 100, c % 100). Then pass the result to the base formatter so {:>10} and friends work on Money too.

Your turn: write a formatter for Money (an amount in cents, never negative here) that shows it as dollars: Money{1205} is $12.05, Money{7} is $0.07.

Previous: std::format Next: chrono durations