#pragma once #ifndef MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED #define MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED 1 #include #include "../container/boxed_object.hpp" namespace mijin { // // public concepts // template concept parse_result_type = requires { typename T::parse_result_t; }; template concept parseable_by_type = parse_result_type && requires(const T& object, TParseContext& parseContext) { { T::parseFormat(parseContext) } -> std::convertible_to>; }; template concept simple_formattable_to_type = requires(const T& object, TFmtContext& formatContext) { { object.format(formatContext) } -> std::convertible_to; }; template concept complex_formattable_to_type = parseable_by_type && requires(const T& object, TParseContext& parseContext, TFmtContext& formatContext, T::parse_result_t& parseResult) { { object.format(formatContext, parseResult) } -> std::convertible_to; }; template concept formattable_to_type = simple_formattable_to_type || complex_formattable_to_type; template concept cformattable_type = formattable_to_type; template concept wformattable_type = formattable_to_type; template concept formattable_type = cformattable_type && wformattable_type; template concept any_formattable_type = cformattable_type || wformattable_type; // // internal types // namespace impl { template struct ParseResult { }; template struct ParseResult { BoxedObject value; }; } } // namespace mijin template struct std::formatter { [[no_unique_address]] [[maybe_unused]] mijin::impl::ParseResult parseResult; template constexpr TContext::iterator parse(TContext& ctx) { if constexpr (mijin::parse_result_type) { static_assert(mijin::parseable_by_type, "Type does not support parsing by this context."); auto [it, result] = T::parseFormat(ctx); parseResult.value.construct(std::move(result)); return it; } else { auto it = ctx.begin(); auto end = ctx.end(); if (it != end && *it != '}') { throw std::format_error("invalid format"); } return it; } } template TContext::iterator format(const T& object, TContext& ctx) const { if constexpr (mijin::parse_result_type) { auto it = object.format(ctx, std::move(*parseResult.value)); parseResult.destroy(); return it; } else { static_assert(mijin::simple_formattable_to_type, "Type does not support formatting to this context."); return object.format(ctx); } } }; #endif // MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED