Add std::format helpers for pointers, optional, variant, and support an extension mechanism using format_impl() that doesn't require manually specializing std::formatter

This commit is contained in:
Nikita Lisitsa 2026-08-14 16:46:09 +03:00
parent 17be2f3aaa
commit 75df1be94d

View file

@ -0,0 +1,118 @@
#pragma once
#include <format>
#include <memory>
#include <optional>
#include <variant>
namespace psemek::util
{
template <typename T>
concept has_format_impl = requires (T x, std::format_context & format_context)
{
format_impl(x, format_context);
};
}
namespace std
{
template <typename T, typename Char>
requires ::psemek::util::has_format_impl<T>
struct formatter<T, Char>
{
constexpr auto parse(std::format_parse_context & ctx) const
{
return ctx.begin();
}
template <typename FormatContext>
auto format(T const & x, FormatContext & ctx) const
{
return format_impl(x, ctx);
}
};
template <typename T, typename Char>
requires (!std::is_same_v<T, void> && !std::is_same_v<T, Char>)
struct formatter<T *, Char>
: formatter<void *>
{
using base = formatter<void *, Char>;
template <typename FormatContext>
auto format(T const * ptr, FormatContext & ctx) const
{
if (ptr)
return base::format((void *)ptr, ctx);
else
return std::format_to(ctx.out(), "(null)");
}
};
template <typename T, typename Char>
struct formatter<unique_ptr<T>, Char>
: formatter<void *>
{
using base = formatter<void *, Char>;
template <typename FormatContext>
auto format(unique_ptr<T> const & ptr, FormatContext & ctx) const
{
if (ptr)
return base::format((void *)ptr.get(), ctx);
else
return std::format_to(ctx.out(), "(null)");
}
};
template <typename T, typename Char>
struct formatter<shared_ptr<T>, Char>
: formatter<void *>
{
using base = formatter<void *, Char>;
template <typename FormatContext>
auto format(shared_ptr<T> const & ptr, FormatContext & ctx) const
{
if (ptr)
return base::format((void *)ptr.get(), ctx);
else
return std::format_to(ctx.out(), "(null)");
}
};
template <typename T, typename Char>
struct formatter<optional<T>, Char>
: formatter<T>
{
using base = formatter<T, Char>;
template <typename FormatContext>
auto format(optional<T> const & opt, FormatContext & ctx) const
{
if (opt)
return base::format(*opt, ctx);
else
return std::format_to(ctx.out(), "(none)");
}
};
template <typename ... Args, typename Char>
struct formatter<variant<Args...>, Char>
{
constexpr auto parse(std::format_parse_context & ctx) const
{
return ctx.begin();
}
template <typename FormatContext>
auto format(variant<Args...> const & v, FormatContext & ctx) const
{
return std::visit([&ctx](auto const & v){ return std::format_to(ctx.out(), "{}", v); }, v);
}
};
}