How to write a custom formatter for std::optional
15:23 27 May 2026

I'm trying to write a custom std::fmt formatter for std::optional. Here's what I have so far:

#include 
#include 
#include 
#include 
#include 

template 
class fmt::formatter> {
public:
  constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); }
  template 
  constexpr auto format (std::optional const& op, Context& ctx) const {
        if(op) return format_to(ctx.out(), "{}", op.value());
        else return format_to(ctx.out(), "-");
    }
};

int main () {
    std::vector> vec{{1}, {2}, {}};
    fmt::print("{:}\n", vec);
    // fmt::print("{:x}\n", vec);      <----- SIGSEGV
}

Godbolt

The formatter works so long as I don't specify how the elements of the vector vec, i.e. the std::optionals, should be formatted. What I want to do is to specify how the uint64_ts should be formatted. In this example, I want them to formatted to hex strings. However, when I try to use my formatter with {:x}, I get a segfault.

Program returned: 139
terminate called after throwing an instance of 'fmt::v6::format_error'
  what():  unknown format specifier
Program terminated with signal: SIGSEGV

I'm not sure how the std::format API works but based on what I could gather, I need to specify two functions for custom types: parse and format. The parse function is responsible for parsing the format specification, i.e. x}. It should consume all the tokens in the format specification that are relevant to it and then return the remaining format specification back. In my case I haven't specified the parse function, so it should return x} as it is (the default behavior of parse).

This remaining specification is then captured by ctx in format. If the op has a value, then the ctx and the value of op is returned. That's what format_to(ctx.out(), "{}", op.value()); does.

This logic seems sound to me, so I don't know why I'm getting a segfault. How should I modify my formatter to get it to do what I want it to do?

c++ fmt stdoptional