From e9272ac1088d72001ce38d0585c1280add85452b Mon Sep 17 00:00:00 2001 From: Ramzi Sabra Date: Tue, 24 Mar 2020 06:59:25 +0200 Subject: [PATCH] added ASCII-only format output support --- src/include/units/bits/deduced_symbol_text.h | 3 +- src/include/units/format.h | 27 +++++- src/include/units/physical/natural/units.h | 4 +- src/include/units/physical/si/resistance.h | 2 +- src/include/units/prefix.h | 4 +- src/include/units/symbol_text.h | 99 ++++++++++++++++++++ src/include/units/unit.h | 5 +- test/unit_test/runtime/fmt_test.cpp | 18 +++- 8 files changed, 150 insertions(+), 12 deletions(-) create mode 100644 src/include/units/symbol_text.h diff --git a/src/include/units/bits/deduced_symbol_text.h b/src/include/units/bits/deduced_symbol_text.h index bf35c126..5d3e2cd9 100644 --- a/src/include/units/bits/deduced_symbol_text.h +++ b/src/include/units/bits/deduced_symbol_text.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace units::detail { @@ -49,7 +50,7 @@ constexpr auto operator_text() } } -template +template constexpr auto exp_text() { // get calculation operator + symbol diff --git a/src/include/units/format.h b/src/include/units/format.h index 9d3ef4de..67997238 100644 --- a/src/include/units/format.h +++ b/src/include/units/format.h @@ -131,6 +131,13 @@ namespace units { if(ptr == end) throw fmt::format_error("invalid format"); c = *ptr++; + switch(c) + { + case 'A': + handler.on_quantity_unit_ascii_only(); + c = *ptr++; + break; + } switch(c) { // units-type case '%': @@ -212,13 +219,14 @@ namespace units { global_format_specs const & global_specs; rep_format_specs const & rep_specs; unit_format_specs const & unit_specs; + bool ascii_only; explicit units_formatter( OutputIt o, quantity q, global_format_specs const & gspecs, rep_format_specs const & rspecs, unit_format_specs const & uspecs ): - out(o), val(q.count()), global_specs(gspecs), rep_specs(rspecs), unit_specs(uspecs) + out(o), val(q.count()), global_specs(gspecs), rep_specs(rspecs), unit_specs(uspecs), ascii_only(false) { } @@ -235,12 +243,19 @@ namespace units { void on_quantity_unit([[maybe_unused]] const CharT) { - if (unit_specs.modifier != '\0') { + if (unit_specs.modifier != '\0' && unit_specs.modifier != 'A') { throw fmt::format_error( fmt::format("Unit modifier '{}' is not implemented", unit_specs.modifier) ); // TODO } - format_to(out, "{}", unit_text().c_str()); + auto txt = unit_text(); + auto txt_c_str = ascii_only ? txt.ascii().c_str() : txt.standard().c_str(); + format_to(out, "{}", txt_c_str); + } + + void on_quantity_unit_ascii_only() + { + ascii_only = true; } }; @@ -260,6 +275,7 @@ private: units::detail::unit_format_specs unit_specs; bool quantity_value = false; bool quantity_unit = false; + bool quantity_unit_ascii_only = false; arg_ref_type width_ref; arg_ref_type precision_ref; fmt::basic_string_view format_str; @@ -342,6 +358,11 @@ private: } f.quantity_unit = true; } + + constexpr void on_quantity_unit_ascii_only() + { + f.quantity_unit_ascii_only = true; + } }; struct parse_range { diff --git a/src/include/units/physical/natural/units.h b/src/include/units/physical/natural/units.h index b1454756..1277c9eb 100644 --- a/src/include/units/physical/natural/units.h +++ b/src/include/units/physical/natural/units.h @@ -30,8 +30,8 @@ namespace units::natural { struct unitless : named_unit {}; struct electronvolt : named_unit {}; struct gigaelectronvolt : prefixed_unit {}; -struct inverted_gigaelectronvolt : named_unit {}; -struct square_gigaelectronvolt : named_unit {}; +struct inverted_gigaelectronvolt : named_unit {}; +struct square_gigaelectronvolt : named_unit {}; // NOTE: eV as a base unit with no relation to joule prevents us from going back // from natural units to SI. Do we need such a support or should we treat diff --git a/src/include/units/physical/si/resistance.h b/src/include/units/physical/si/resistance.h index 28cb7930..6a1c0a3e 100644 --- a/src/include/units/physical/si/resistance.h +++ b/src/include/units/physical/si/resistance.h @@ -30,7 +30,7 @@ namespace units::si { -struct ohm : named_unit {}; +struct ohm : named_unit {}; struct milliohm : prefixed_unit {}; struct kiloohm : prefixed_unit {}; struct megaohm : prefixed_unit {}; diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index 46951bfd..528f20ab 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -23,8 +23,8 @@ #pragma once #include -#include #include +#include namespace units { @@ -68,7 +68,7 @@ struct prefix_base : downcast_base> { * @tparam Symbol a text representation of the prefix * @tparam R factor to be used to scale a unit */ -template +template requires (!std::same_as) struct prefix : downcast_child> { static constexpr auto symbol = Symbol; diff --git a/src/include/units/symbol_text.h b/src/include/units/symbol_text.h new file mode 100644 index 00000000..c952cfba --- /dev/null +++ b/src/include/units/symbol_text.h @@ -0,0 +1,99 @@ +#pragma once + +#include + +namespace units { + +template +struct basic_symbol_text { + basic_fixed_string standard_; + basic_fixed_string ascii_; + + constexpr basic_symbol_text(StandardCharT s) noexcept: standard_(s), ascii_(s) {} + constexpr basic_symbol_text(StandardCharT s, ASCIICharT a) noexcept: standard_(s), ascii_(a) {} + constexpr basic_symbol_text(const StandardCharT (&s)[N + 1]) noexcept: standard_(s), ascii_(s) {} + constexpr basic_symbol_text(const basic_fixed_string& s) noexcept: standard_(s), ascii_(s) {} + constexpr basic_symbol_text(const StandardCharT (&s)[N + 1], const ASCIICharT (&a)[M + 1]) noexcept: standard_(s), ascii_(a) {} + constexpr basic_symbol_text(const basic_fixed_string& s, const basic_fixed_string& a) noexcept: standard_(s), ascii_(a) {} + + [[nodiscard]] constexpr auto& standard() { return standard_; } + [[nodiscard]] constexpr const auto& standard() const { return standard_; } + [[nodiscard]] constexpr auto& ascii() { return ascii_; } + [[nodiscard]] constexpr const auto& ascii() const { return ascii_; } + + [[nodiscard]] constexpr std::size_t size() const noexcept { return standard_.size(); } + [[nodiscard]] constexpr const StandardCharT* c_str() const noexcept { return standard_.c_str(); } + + template + [[nodiscard]] constexpr friend basic_symbol_text operator+( + const basic_symbol_text& lhs, const basic_symbol_text& rhs) noexcept + { + return basic_symbol_text( + lhs.standard_ + rhs.standard_, lhs.ascii_ + rhs.ascii_); + } + + template + [[nodiscard]] constexpr friend basic_symbol_text operator+( + const basic_symbol_text& lhs, const basic_fixed_string& rhs) noexcept + { + return basic_symbol_text( + lhs.standard_ + rhs, lhs.ascii_ + rhs); + } + + template + [[nodiscard]] constexpr friend basic_symbol_text operator+( + const basic_fixed_string& lhs, const basic_symbol_text& rhs) noexcept + { + return basic_symbol_text( + lhs + rhs.standard_, lhs + rhs.ascii_); + } + + template + [[nodiscard]] constexpr friend bool operator<(const basic_symbol_text& lhs, + const basic_symbol_text& rhs) noexcept + { + return (lhs.standard_ < rhs.standard_); + } + + [[nodiscard]] constexpr friend bool operator==(const basic_symbol_text& lhs, + const StandardCharT (&rhs)[N + 1]) noexcept + { + return (lhs.standard_ == basic_fixed_string(rhs)); + } + + template + [[nodiscard]] constexpr friend bool operator==(const basic_symbol_text& lhs, + const StandardCharT2 (&rhs)[N2 + 1]) noexcept + { + return false; + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, + const basic_symbol_text& symbol) + { + return os << symbol.standard_.c_str(); + } +}; + +template +basic_symbol_text(StandardCharT) -> basic_symbol_text; + +template +basic_symbol_text(StandardCharT, ASCIICharT) -> basic_symbol_text; + +template +basic_symbol_text(const StandardCharT (&)[N]) -> basic_symbol_text; + +template +basic_symbol_text(const basic_fixed_string& s) -> basic_symbol_text; + +template +basic_symbol_text(const StandardCharT (&)[N], const ASCIICharT (&)[M]) -> basic_symbol_text; + +template +basic_symbol_text(const basic_fixed_string& s, + const basic_fixed_string& a) +-> basic_symbol_text; + +} // namespace units diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 2654eafd..746c47b4 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -31,6 +31,7 @@ #include #include #include +#include namespace units { @@ -94,7 +95,7 @@ struct unknown_coherent_unit : unit {}; * @tparam Symbol a short text representation of the unit * @tparam PT no_prefix or a type of prefix family */ -template +template struct named_unit : downcast_child, Child>> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; @@ -115,7 +116,7 @@ struct named_unit : downcast_child, Child>> { * @tparam R a scale to apply to U * @tparam U a reference unit to scale */ -template +template struct named_scaled_unit : downcast_child, typename U::reference>> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index d8353531..3bedfde9 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -29,6 +29,7 @@ #include "units/physical/si/velocity.h" #include "units/physical/si/volume.h" #include "units/physical/si/surface_tension.h" +#include "units/physical/si/resistance.h" #include "units/format.h" #include "units/math.h" #include @@ -706,7 +707,22 @@ TEST_CASE("format string with only %q should print quantity unit symbol only", " CHECK(fmt::format("{:%q}", 123q_km_per_h) == "km/h"); } -TEST_CASE("%q an %Q can be put anywhere in a format string", "[text][fmt]") +TEST_CASE("format string with only %q for unit with ASCII quantity unit symbol should print Unicode quantity unit symbol only", "[text][fmt]") +{ + CHECK(fmt::format("{:%Q%q}", 123q_kR) == "123kΩ"); +} + +TEST_CASE("format string with %Aq for unit with ASCII quantity unit symbol should print ASCII quantity unit symbol only", "[text][fmt]") +{ + CHECK(fmt::format("{:%Q%Aq}", 123q_kR) == "123kohm"); +} + +TEST_CASE("format string with %Aq for unit with no ASCII quantity unit symbol should print Unicode quantity unit symbol only", "[text][fmt]") +{ + CHECK(fmt::format("{:%Aq}", 123q_km_per_h) == "km/h"); +} + +TEST_CASE("%q and %Q can be put anywhere in a format string", "[text][fmt]") { SECTION("no space") {