feat: std::format support added

Resolves #503
This commit is contained in:
Mateusz Pusz
2024-01-23 21:19:45 +01:00
parent 030c53a404
commit 1817687ca2
3 changed files with 40 additions and 13 deletions

View File

@@ -116,8 +116,8 @@ MP_UNITS_DIAGNOSTIC_POP
#define MP_UNITS_STD_FMT fmt #define MP_UNITS_STD_FMT fmt
#define MP_UNITS_FMT_LOCALE(loc) (loc).template get<std::locale>() #define MP_UNITS_FMT_LOCALE(loc) (loc).template get<std::locale>()
#define MP_UNITS_FMT_TO_ARG_ID(arg) static_cast<int>(arg) #define MP_UNITS_FMT_TO_ARG_ID(arg) (arg)
#define MP_UNITS_FMT_FROM_ARG_ID(arg) static_cast<size_t>(arg) #define MP_UNITS_FMT_FROM_ARG_ID(arg) (arg)
// This re-uses code from fmt; // This re-uses code from fmt;
#if FMT_EXCEPTIONS #if FMT_EXCEPTIONS
@@ -143,8 +143,8 @@ MP_UNITS_DIAGNOSTIC_POP
#define MP_UNITS_STD_FMT std #define MP_UNITS_STD_FMT std
#define MP_UNITS_FMT_LOCALE(loc) loc #define MP_UNITS_FMT_LOCALE(loc) loc
#define MP_UNITS_FMT_TO_ARG_ID(arg) arg #define MP_UNITS_FMT_TO_ARG_ID(arg) static_cast<std::size_t>(arg)
#define MP_UNITS_FMT_FROM_ARG_ID(arg) arg #define MP_UNITS_FMT_FROM_ARG_ID(arg) static_cast<int>(arg)
#define MP_UNITS_THROW(arg) throw arg #define MP_UNITS_THROW(arg) throw arg
#endif #endif

View File

@@ -40,23 +40,35 @@
namespace mp_units::detail { namespace mp_units::detail {
enum class fmt_align { none, left, right, center, numeric }; enum class fmt_align { none, left, right, center, numeric };
enum class fmt_arg_id_kind { none, index, name }; enum class fmt_arg_id_kind {
none,
#if MP_UNITS_USE_FMTLIB
name,
#endif
index
};
template<typename Char> template<typename Char>
struct fmt_arg_ref { struct fmt_arg_ref {
fmt_arg_id_kind kind = fmt_arg_id_kind::none; fmt_arg_id_kind kind = fmt_arg_id_kind::none;
union value { union value {
int index = 0; int index = 0;
#if MP_UNITS_USE_FMTLIB
std::basic_string_view<Char> name; std::basic_string_view<Char> name;
#endif
value() = default; constexpr value() {}
constexpr value(int idx) : index(idx) {} constexpr value(int idx) : index(idx) {}
#if MP_UNITS_USE_FMTLIB
constexpr value(std::basic_string_view<Char> n) : name(n) {} constexpr value(std::basic_string_view<Char> n) : name(n) {}
#endif
} val{}; } val{};
fmt_arg_ref() = default; fmt_arg_ref() = default;
constexpr explicit fmt_arg_ref(int index) : kind(fmt_arg_id_kind::index), val(index) {} constexpr explicit fmt_arg_ref(int index) : kind(fmt_arg_id_kind::index), val(index) {}
#if MP_UNITS_USE_FMTLIB
constexpr explicit fmt_arg_ref(std::basic_string_view<Char> name) : kind(fmt_arg_id_kind::name), val(name) {} constexpr explicit fmt_arg_ref(std::basic_string_view<Char> name) : kind(fmt_arg_id_kind::name), val(name) {}
#endif
[[nodiscard]] constexpr fmt_arg_ref& operator=(int idx) [[nodiscard]] constexpr fmt_arg_ref& operator=(int idx)
{ {
@@ -79,7 +91,7 @@ public:
{ {
auto size = str.size(); auto size = str.size();
if (size > max_size) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid fill")); if (size > max_size) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("invalid fill"));
for (size_t i = 0; i < size; ++i) data_[i] = str[i]; for (size_t i = 0; i < size && i < max_size; ++i) data_[i] = str[i];
size_ = static_cast<unsigned char>(size); size_ = static_cast<unsigned char>(size);
return *this; return *this;
} }
@@ -144,8 +156,8 @@ template<class Handler, typename FormatArg>
template<typename Context, typename ID> template<typename Context, typename ID>
[[nodiscard]] constexpr auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) [[nodiscard]] constexpr auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id))
{ {
auto arg = ctx.arg(id); auto arg = ctx.arg(MP_UNITS_FMT_TO_ARG_ID(id));
if (!arg) ctx.on_error("argument not found"); if (!arg) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("argument not found"));
return arg; return arg;
} }
@@ -158,9 +170,11 @@ constexpr void handle_dynamic_spec(int& value, fmt_arg_ref<typename Context::cha
case fmt_arg_id_kind::index: case fmt_arg_id_kind::index:
value = ::mp_units::detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index)); value = ::mp_units::detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index));
break; break;
#if MP_UNITS_USE_FMTLIB
case fmt_arg_id_kind::name: case fmt_arg_id_kind::name:
value = ::mp_units::detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name)); value = ::mp_units::detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name));
break; break;
#endif
} }
} }
@@ -219,7 +233,12 @@ template<typename Char, typename Handler>
do { do {
++it; ++it;
} while (it != end && (::mp_units::detail::is_name_start(*it) || ('0' <= *it && *it <= '9'))); } while (it != end && (::mp_units::detail::is_name_start(*it) || ('0' <= *it && *it <= '9')));
#if MP_UNITS_USE_FMTLIB
handler.on_name({begin, ::mp_units::detail::to_unsigned(it - begin)}); handler.on_name({begin, ::mp_units::detail::to_unsigned(it - begin)});
#else
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("named arguments are not supported in the C++ standard facilities"));
#endif
return it; return it;
} }
@@ -260,21 +279,27 @@ struct dynamic_spec_id_handler {
constexpr void on_auto() constexpr void on_auto()
{ {
int id = ctx.next_arg_id(); int id = MP_UNITS_FMT_FROM_ARG_ID(ctx.next_arg_id());
ref = fmt_arg_ref<Char>(id); ref = fmt_arg_ref<Char>(id);
#if MP_UNITS_USE_FMTLIB || __cpp_lib_format >= 202305L
ctx.check_dynamic_spec(id); ctx.check_dynamic_spec(id);
#endif
} }
constexpr void on_index(int id) constexpr void on_index(int id)
{ {
ref = fmt_arg_ref<Char>(id); ref = fmt_arg_ref<Char>(id);
ctx.check_arg_id(id); ctx.check_arg_id(MP_UNITS_FMT_TO_ARG_ID(id));
#if MP_UNITS_USE_FMTLIB || __cpp_lib_format >= 202305L
ctx.check_dynamic_spec(id); ctx.check_dynamic_spec(id);
#endif
} }
constexpr void on_name(std::basic_string_view<Char> id) #if MP_UNITS_USE_FMTLIB
constexpr void on_name([[maybe_unused]] std::basic_string_view<Char> id)
{ {
ref = fmt_arg_ref<Char>(id); ref = fmt_arg_ref<Char>(id);
ctx.check_arg_id(id); ctx.check_arg_id(id);
} }
#endif
}; };
template<typename Char> template<typename Char>

View File

@@ -293,6 +293,8 @@ class MP_UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, Char> {
return begin + *format_str_lengths_it++; return begin + *format_str_lengths_it++;
} }
}; };
template<typename OutputIt, typename... Args>
quantity_formatter(OutputIt, Args...) -> quantity_formatter<OutputIt>;
template<typename Handler> template<typename Handler>
constexpr const Char* parse_quantity_specs(const Char* begin, const Char* end, Handler&& handler) const constexpr const Char* parse_quantity_specs(const Char* begin, const Char* end, Handler&& handler) const
@@ -345,7 +347,7 @@ class MP_UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, Char> {
template<typename OutputIt, typename FormatContext> template<typename OutputIt, typename FormatContext>
OutputIt format_quantity(OutputIt out, const quantity_t& q, FormatContext& ctx) const OutputIt format_quantity(OutputIt out, const quantity_t& q, FormatContext& ctx) const
{ {
std::locale locale = ctx.locale().template get<std::locale>(); std::locale locale = MP_UNITS_FMT_LOCALE(ctx.locale());
if (modifiers_format_str_.empty()) { if (modifiers_format_str_.empty()) {
// default format should print value followed by the unit separated with 1 space // default format should print value followed by the unit separated with 1 space
out = MP_UNITS_STD_FMT::vformat_to(out, locale, "{}", out = MP_UNITS_STD_FMT::vformat_to(out, locale, "{}",