Added formattable utility.

This commit is contained in:
Patrick 2025-07-17 10:56:52 +02:00
parent d3659f11f7
commit 933a36d992

View File

@ -0,0 +1,122 @@
#pragma once
#ifndef MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED
#define MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED 1
#include <format>
#include "../container/boxed_object.hpp"
namespace mijin
{
//
// public concepts
//
template<typename T>
concept parse_result_type = requires
{
typename T::parse_result_t;
};
template<typename T, typename TParseContext>
concept parseable_by_type = parse_result_type &&
requires(const T& object, TParseContext& parseContext)
{
{ T::parseFormat(parseContext) } -> std::convertible_to<std::pair<typename TParseContext::iterator, typename T::parse_result_t>>;
};
template<typename T, typename TFmtContext>
concept simple_formattable_to_type = requires(const T& object, TFmtContext& formatContext)
{
{ object.format(formatContext) } -> std::convertible_to<typename TFmtContext::iterator>;
};
template<typename T, typename TFmtContext, typename TParseContext>
concept complex_formattable_to_type = parseable_by_type<T, TParseContext> &&
requires(const T& object, TParseContext& parseContext, TFmtContext& formatContext, T::parse_result_t& parseResult)
{
{ object.format(formatContext, parseResult) } -> std::convertible_to<typename TFmtContext::iterator>;
};
template<typename T, typename TFmtContext, typename TParseContext>
concept formattable_to_type = simple_formattable_to_type<T, TFmtContext> || complex_formattable_to_type<T, TFmtContext, TParseContext>;
template<typename T>
concept cformattable_type = formattable_to_type<T, std::format_context, std::format_parse_context>;
template<typename T>
concept wformattable_type = formattable_to_type<T, std::wformat_context, std::wformat_parse_context>;
template<typename T>
concept formattable_type = cformattable_type<T> && wformattable_type<T>;
template<typename T>
concept any_formattable_type = cformattable_type<T> || wformattable_type<T>;
//
// internal types
//
namespace impl
{
template<typename T>
struct ParseResult
{
};
template<parse_result_type T>
struct ParseResult<T>
{
BoxedObject<typename T::parse_result_t> value;
};
}
} // namespace mijin
template<mijin::any_formattable_type T>
struct std::formatter<T>
{
[[no_unique_address]] [[maybe_unused]] mijin::impl::ParseResult<T> parseResult;
template<typename TContext>
constexpr TContext::iterator parse(TContext& ctx)
{
if constexpr (mijin::parse_result_type<T>)
{
static_assert(mijin::parseable_by_type<T, TContext>, "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<typename TContext>
TContext::iterator format(const T& object, TContext& ctx) const
{
if constexpr (mijin::parse_result_type<T>)
{
auto it = object.format(ctx, std::move(*parseResult.value));
parseResult.destroy();
return it;
}
else
{
static_assert(mijin::simple_formattable_to_type<T, TContext>, "Type does not support formatting to this context.");
return object.format(ctx);
}
}
};
#endif // MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED