#include <iostream> #include <format> #include <type_traits> #include <string> class KeyValue { public: KeyValue(std::string key, int value) : m_key{ std::move(key) }, m_value{ value } { } auto& getKey() const { return m_key; } auto& getValue() const { return m_value; } private: std::string m_key; int m_value; }; template <> class std::formatter<KeyValue> { public: auto parse(auto& context) { auto iter = context.begin(); const auto end = context.end(); if (iter == end || *iter == '}') { m_out = OutputType::KeyAndValue; // {} return iter; } switch (*iter) { case 'a': m_out = OutputType::Key; break; // {:a} -> key case 'b': m_out = OutputType::Value; break; // {:b} -> value case 'c': m_out = OutputType::KeyAndValue; break; // {:c} -> key and value default: throw std::format_error{ "Invalid KeyValue format specifier. " }; } ++iter; if (iter != end && *iter != '}') throw std::format_error{ "Invalid KeyValue format specifier. " }; return iter; } auto format(const KeyValue& kv, auto& context) { switch (m_out) { using enum OutputType; case Key: return std::format_to(context.out(), "{}", kv.getKey()); case Value: return std::format_to(context.out(), "{}", kv.getValue()); default: return std::format_to(context.out(), "{} - {}", kv.getKey(), kv.getValue()); } } private: enum class OutputType { Key, Value, KeyAndValue }; OutputType m_out{ OutputType::KeyAndValue }; }; struct Point { int x, y; }; template <> class std::formatter<Point> { public: typename std::format_parse_context::iterator parse(std::format_parse_context& context) { auto iter = context.begin(); for (; iter != context.end() && *iter != '}'; ++iter) { if (*iter == '#') { m_hashtag = true; } else throw std::format_error{ "Invalid KeyValue format specifier. " }; } return iter; } typename std::format_context::iterator format(const Point& p, std::format_context& context) { char left = m_hashtag ? '[' : '('; char right = m_hashtag ? ']' : ')'; return std::format_to(context.out(), "{}{}, {}{}", left, p.x, p.y, right); } private: bool m_hashtag = false; }; int main() { KeyValue kv{ "Key", 0 }; Point p{ .x = 1, .y = 1 }; try { std::cout << std::format("{}\n", kv); std::cout << std::format("{:a}\n", kv); std::cout << std::format("{:b}\n", kv); std::cout << std::format("{:c}\n", kv); std::cout << std::format("{:ab}\n", kv); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } try { std::cout << std::format("{}\n", p); //std::cout << std::format("{:#}\n", p); //std::cout << std::format("{:#x}\n", p); } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } }