fix: expr_divide fixed for 1 / derived_type

This commit is contained in:
Mateusz Pusz
2022-11-07 14:57:39 -10:00
parent 9a11c33764
commit a97eea95e9
3 changed files with 81 additions and 105 deletions

View File

@@ -38,21 +38,13 @@ if(${projectPrefix}AS_SYSTEM_HEADERS)
endif()
add_subdirectory(core)
# add_subdirectory(core-fmt)
# add_subdirectory(core-io)
add_subdirectory(core-fmt)
add_subdirectory(core-io)
add_subdirectory(systems)
# project-wide wrapper
add_library(mp-units INTERFACE)
target_link_libraries(
mp-units
INTERFACE
mp-units::core
# mp-units::core-io
# mp-units::core-fmt
mp-units::systems
)
target_link_libraries(mp-units INTERFACE mp-units::core mp-units::core-io mp-units::core-fmt mp-units::systems)
add_library(mp-units::mp-units ALIAS mp-units)
install(TARGETS mp-units EXPORT mp-unitsTargets)

View File

@@ -22,16 +22,13 @@
#pragma once
#include <units/bits/algorithm.h>
#include <units/bits/fmt.h>
#include <units/customization_points.h>
#include <units/quantity.h>
#include <algorithm>
#include <units/unit.h>
#include <cstdint>
// IWYU pragma: begin_exports
#include <units/bits/unit_text.h>
// IWYU pragma: end_exports
// Grammar
//
// units-format-spec ::= [fill-and-align] [width] [units-specs]
@@ -42,37 +39,12 @@
// conversion-spec ::= '%' units-type
// units-type ::= [units-rep-modifier] 'Q'
// [units-unit-modifier] 'q'
// one of "nt%"
// units-rep-modifier ::= [sign] [#] [precision] [L] [units-rep-type]
// units-rep-type ::= one of "aAbBdeEfFgGoxX"
// units-unit-modifier ::= 'A'
// Guide for editing
//
// If you want to add a new `units-type` terminal character (e.g. 'Q', 'q'):
// - If needed, write a new `specs` class (e.g. `global_format_specs`)
// - Add the new symbol in the `units_types` variable in the `parse_units_format` function
// - Add a new case in the `if` following the format_error in `parse_units_format` function;
// this should invoke `handler.on_[...]`
// - Edit `STD_FMT::formatter`:
// - Add a new field for the flag/specs
// - Add to the `STD_FMT::formatter::spec_handler` a `on_[...]` function that set the flag/specs if needed
// - Edit `quantity_formatter`:
// - Add a new field for the flag/specs
// - write a `on_[...]` function that writes to the `out` iterator the correct output
//
// If you want to add a new `units-rep-type`:
// - Add the new symbol in the `valid_rep_types` variable (which is in the
// STD_FMT::formatter::spec_handler::on_type member function)
// NB: currently this function forward the modifier to the value that must be formatted;
// if the symbol has no meaning for STD_FMT::formatter<Rep>, this behavior should be disabled manually
// (as is done for '\0')
// - Implement the effect of the new flag in `format_units_quantity_value`
//
// If you want to add a new `units-unit-modifier`:
// - Add the new symbol in the `valid_modifiers` variable (which is in the
// STD_FMT::formatter::spec_handler::on_unit_modifier member function)
// - Implement the effect of the new flag in the `quantity_formatter::on_quantity_unit` member function
// units-unit-modifier ::= [units-text-encoding, units-unit-symbol-denominator, units-unit-symbol-separator]
// units-text-encoding ::= one of "UA"
// units-unit-symbol-solidus ::= one of "oan"
// units-unit-symbol-separator ::= one of "sd"
namespace units::detail {
@@ -96,9 +68,7 @@ struct quantity_rep_format_specs {
};
// Holds specs about the unit (%[specs]q)
struct quantity_unit_format_specs {
bool ascii_only = false;
};
struct quantity_unit_format_specs : unit_symbol_formatting {};
template<typename CharT>
struct quantity_format_specs {
@@ -159,32 +129,15 @@ constexpr It parse_units_format(It begin, S end, Handler&& handler)
if (ptr == end) throw STD_FMT::format_error("invalid format");
c = *ptr++;
switch (c) {
// units-type
case '%':
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
handler.on_text(tab, tab + 1);
break;
}
default:
constexpr auto units_types = std::string_view{"Qq"};
const auto new_end = std::find_first_of(begin, end, units_types.begin(), units_types.end());
if (new_end == end) throw STD_FMT::format_error("invalid format");
if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else {
handler.on_quantity_unit(*begin); // Edit `on_quantity_unit` to add an unit modifier
}
ptr = new_end + 1;
constexpr auto units_types = std::string_view{"Qq"};
const auto new_end = find_first_of(begin, end, units_types.begin(), units_types.end());
if (new_end == end) throw STD_FMT::format_error("invalid format");
if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else {
handler.on_quantity_unit(begin, new_end); // Edit `on_quantity_unit` to add an unit modifier
}
ptr = new_end + 1;
begin = ptr;
}
if (begin != ptr) handler.on_text(begin, ptr);
@@ -264,14 +217,14 @@ OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs<C
return STD_FMT::format_to(out, "}}");
}
template<typename Dimension, typename Unit, typename Rep, typename Locale, typename CharT, typename OutputIt>
template<auto Reference, typename Rep, typename Locale, typename CharT, typename OutputIt>
struct quantity_formatter {
OutputIt out;
Rep val;
const quantity_format_specs<CharT>& specs;
Locale loc;
explicit quantity_formatter(OutputIt o, quantity<Dimension, Unit, Rep> q, const quantity_format_specs<CharT>& fspecs,
explicit quantity_formatter(OutputIt o, quantity<Reference, Rep> q, const quantity_format_specs<CharT>& fspecs,
Locale lc) :
out(o), val(std::move(q).number()), specs(fspecs), loc(std::move(lc))
{
@@ -289,23 +242,29 @@ struct quantity_formatter {
out = format_units_quantity_value<CharT>(out, val, specs.rep, loc);
}
void on_quantity_unit([[maybe_unused]] CharT)
template<std::input_iterator It, std::sentinel_for<It> S>
void on_quantity_unit(It, S)
{
auto txt = unit_text<Dimension, Unit>();
if (specs.unit.ascii_only) {
STD_FMT::format_to(out, "{}", txt.ascii().c_str());
} else {
STD_FMT::format_to(out, "{}", txt.standard().c_str());
}
out = unit_symbol_to<CharT>(out, Reference.unit, specs.unit);
}
};
template<std::input_iterator It, std::sentinel_for<It> S>
[[nodiscard]] constexpr It at_most_one_of(It begin, S end, std::string_view modifiers)
{
auto it = find_first_of(begin, end, modifiers.begin(), modifiers.end());
if (it != end && find_first_of(it + 1, end, modifiers.begin(), modifiers.end()) != end)
throw STD_FMT::format_error("only one of '" + std::string(modifiers) +
"' unit modifiers may be used in the format spec");
return it;
}
} // namespace units::detail
template<typename Dimension, typename Unit, typename Rep, typename CharT>
struct STD_FMT::formatter<units::quantity<Dimension, Unit, Rep>, CharT> {
template<auto Reference, typename Rep, typename CharT>
struct STD_FMT::formatter<units::quantity<Reference, Rep>, CharT> {
private:
using quantity = units::quantity<Dimension, Unit, Rep>;
using quantity = units::quantity<Reference, Rep>;
using iterator = TYPENAME STD_FMT::basic_format_parse_context<CharT>::iterator;
bool quantity_value = false;
@@ -335,16 +294,6 @@ private:
}
}
constexpr void on_unit_modifier(char mod)
{
constexpr auto valid_modifiers = std::string_view{"A"};
if (valid_modifiers.find(mod) != std::string_view::npos) {
f.specs.unit.ascii_only = true;
} else {
throw STD_FMT::format_error("invalid unit modifier specified");
}
}
template<typename T>
constexpr void on_dynamic_width(T t)
{
@@ -369,9 +318,43 @@ private:
f.quantity_value = true;
}
constexpr void on_quantity_unit(CharT mod)
template<std::input_iterator It, std::sentinel_for<It> S>
constexpr void on_quantity_unit(It begin, S end)
{
if (mod != 'q') on_unit_modifier(mod);
if (begin == end) return;
constexpr auto valid_modifiers = std::string_view{"UAoansd"};
for (auto it = begin; it != end; ++it) {
if (valid_modifiers.find(*it) == std::string_view::npos)
throw STD_FMT::format_error("invalid unit modifier specified");
}
if (auto it = units::detail::at_most_one_of(begin, end, "UA"); it != end) {
if (*it == 'U')
f.specs.unit.encoding = units::text_encoding::unicode;
else
f.specs.unit.encoding = units::text_encoding::ascii;
}
if (auto it = units::detail::at_most_one_of(begin, end, "oan"); it != end) {
if (*it == 'o')
f.specs.unit.solidus = units::unit_symbol_solidus::one_denominator;
else if (*it == 'a')
f.specs.unit.solidus = units::unit_symbol_solidus::always;
else
f.specs.unit.solidus = units::unit_symbol_solidus::never;
}
if (auto it = units::detail::at_most_one_of(begin, end, "sd"); it != end) {
if (*it == 's')
f.specs.unit.separator = units::unit_symbol_separator::space;
else {
if (f.specs.unit.encoding == units::text_encoding::ascii)
throw STD_FMT::format_error("dot unit separator allowed only for Unicode encoding");
f.specs.unit.separator = units::unit_symbol_separator::dot;
}
}
f.quantity_unit = true;
}
};
@@ -413,10 +396,9 @@ private:
if (begin == end || *begin == '}') {
// default format should print value followed by the unit separated with 1 space
out = units::detail::format_units_quantity_value<CharT>(out, q.number(), specs.rep, ctx.locale());
constexpr auto symbol = units::detail::unit_text<Dimension, Unit>();
if constexpr (symbol.standard().size() > 0) {
if constexpr (!std::derived_from<decltype(Reference.unit), units::derived_unit<>>) {
*out++ = CharT(' ');
STD_FMT::format_to(out, "{}", symbol.standard().c_str());
out = unit_symbol_to<CharT>(out, Reference.unit);
}
} else {
// user provided format
@@ -436,7 +418,7 @@ public:
}
template<typename FormatContext>
auto format(const quantity& q, FormatContext& ctx)
[[nodiscard]] auto format(const quantity& q, FormatContext& ctx)
{
// process dynamic width and precision
if (specs.global.dynamic_width_index >= 0)

View File

@@ -403,14 +403,16 @@ template<template<typename...> typename To, typename OneType, template<typename,
*/
template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred, typename Lhs,
typename Rhs>
[[nodiscard]] consteval auto expr_divide(Lhs, Rhs)
[[nodiscard]] consteval auto expr_divide(Lhs lhs, Rhs rhs)
{
if constexpr (is_same_v<Lhs, Rhs>) {
return OneType{};
} else if constexpr (is_same_v<Rhs, OneType>) {
return Lhs{};
return lhs;
} else if constexpr (is_same_v<Lhs, OneType>) {
return expr_divide<To, OneType, Pred>(To<>{}, rhs);
} else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) {
// two derived dimensions
// two derived entities
return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_den_, Pred>,
type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_num_, Pred>, OneType,
Pred, To>();
@@ -421,7 +423,7 @@ template<template<typename...> typename To, typename OneType, template<typename,
return get_optimized_expression<type_list_merge_sorted<typename Rhs::_den_, type_list<Lhs>, Pred>,
typename Rhs::_num_, OneType, Pred, To>();
} else {
// two base dimensions
// two named entities
return To<Lhs, per<Rhs>>{};
}
}