diff --git a/src/include/units/bits/deduced_symbol_text.h b/src/include/units/bits/deduced_symbol_text.h index 5d3e2cd9..91333b2d 100644 --- a/src/include/units/bits/deduced_symbol_text.h +++ b/src/include/units/bits/deduced_symbol_text.h @@ -45,7 +45,7 @@ constexpr auto operator_text() return basic_fixed_string("/"); } else { - return basic_fixed_string("⋅"); + return basic_symbol_text("⋅", "."); } } } diff --git a/src/include/units/bits/external/text_tools.h b/src/include/units/bits/external/text_tools.h index aabf37a5..6d9b321f 100644 --- a/src/include/units/bits/external/text_tools.h +++ b/src/include/units/bits/external/text_tools.h @@ -23,6 +23,7 @@ #pragma once #include +#include namespace units::detail { @@ -41,26 +42,34 @@ template<> inline constexpr basic_fixed_string superscript_number<7> = "\u2077"; template<> inline constexpr basic_fixed_string superscript_number<8> = "\u2078"; template<> inline constexpr basic_fixed_string superscript_number<9> = "\u2079"; -inline constexpr basic_fixed_string superscript_minus = "\u207b"; +inline constexpr basic_symbol_text superscript_minus("\u207b", "-"); + +inline constexpr basic_symbol_text superscript_prefix("", "^"); + +template +constexpr auto superscript_helper() +{ + if constexpr(Value < 0) + return superscript_minus + superscript_helper<-Value>(); + else if constexpr(Value < 10) + return basic_symbol_text(superscript_number, basic_fixed_string(static_cast('0' + Value))); + else + return superscript_helper() + superscript_helper(); +} template constexpr auto superscript() { - if constexpr(Value < 0) - return superscript_minus + superscript<-Value>(); - else if constexpr(Value < 10) - return superscript_number; - else - return superscript() + superscript(); + return superscript_prefix + superscript_helper(); } template constexpr auto regular() { if constexpr (Value < 0) - return basic_fixed_string("-") + superscript<-Value>(); + return basic_fixed_string("-") + superscript_helper<-Value>(); else if constexpr (Value < 10) - return basic_fixed_string(static_cast('0' + Value)); + return basic_symbol_text(static_cast('0' + Value)); else return regular() + regular(); } diff --git a/src/include/units/bits/to_string.h b/src/include/units/bits/to_string.h index 89fae646..0c9f4988 100644 --- a/src/include/units/bits/to_string.h +++ b/src/include/units/bits/to_string.h @@ -30,11 +30,13 @@ namespace units::detail { +inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10"); + template constexpr auto ratio_text() { if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp != 0) { - return basic_fixed_string("\u00D7 10") + superscript() + basic_fixed_string(" "); + return base_multiplier + superscript() + basic_fixed_string(" "); } else if constexpr(Ratio::num != 1 || Ratio::den != 1 || Ratio::exp != 0) { auto txt = basic_fixed_string("[") + regular(); @@ -43,7 +45,7 @@ constexpr auto ratio_text() return txt + basic_fixed_string("] "); } else { - return txt + basic_fixed_string(" \u00D7 10") + superscript() + + return txt + " " + base_multiplier + superscript() + basic_fixed_string("] "); } } @@ -54,7 +56,7 @@ constexpr auto ratio_text() } else { return txt + basic_fixed_string("/") + regular() + - basic_fixed_string(" \u00D7 10") + superscript() + + " " + base_multiplier + superscript() + basic_fixed_string("] "); } } diff --git a/src/include/units/symbol_text.h b/src/include/units/symbol_text.h index ac5086e1..2eed9c1b 100644 --- a/src/include/units/symbol_text.h +++ b/src/include/units/symbol_text.h @@ -10,12 +10,21 @@ 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, char 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 StandardCharT (&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) {} + constexpr void validate_ascii_char(char c) noexcept { assert((c & 0x80) == 0); } + + template + constexpr void validate_ascii_string(const char (&s)[P + 1]) noexcept + { + for (size_t i = 0; i < P; ++i) + validate_ascii_char(s[i]); + } + + constexpr basic_symbol_text(StandardCharT s) noexcept: standard_(s), ascii_(s) { validate_ascii_char(s); } + constexpr basic_symbol_text(StandardCharT s, char a) noexcept: standard_(s), ascii_(a) { validate_ascii_char(a); } + constexpr basic_symbol_text(const StandardCharT (&s)[N + 1]) noexcept: standard_(s), ascii_(s) { validate_ascii_string(s); } + constexpr basic_symbol_text(const basic_fixed_string& s) noexcept: standard_(s), ascii_(s) { validate_ascii_string(s.data_); } + constexpr basic_symbol_text(const StandardCharT (&s)[N + 1], const char (&a)[M + 1]) noexcept: standard_(s), ascii_(a) { validate_ascii_string(a); } + constexpr basic_symbol_text(const basic_fixed_string& s, const basic_fixed_string& a) noexcept: standard_(s), ascii_(a) { validate_ascii_string(a.data_); } [[nodiscard]] constexpr auto& standard() { return standard_; } [[nodiscard]] constexpr const auto& standard() const { return standard_; } diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 615be835..a9a03a8f 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -56,6 +56,8 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") CHECK(fmt::format("{}", 1q_ns) == "1 ns"); CHECK(fmt::format("{}", 1q_us) == "1 µs"); CHECK(fmt::format("{}", 1q_ms) == "1 ms"); + + CHECK(fmt::format("{:%Q %Aq}", 1q_us) == "1 us"); } SECTION("length") @@ -98,11 +100,18 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") CHECK(fmt::format("{}", 1q_cm2) == "1 cm²"); CHECK(fmt::format("{}", 1q_km2) == "1 km²"); CHECK(fmt::format("{}", 1q_ft2) == "1 ft²"); + + CHECK(fmt::format("{:%Q %Aq}", 1q_m2) == "1 m^2"); + CHECK(fmt::format("{:%Q %Aq}", 1q_mm2) == "1 mm^2"); + CHECK(fmt::format("{:%Q %Aq}", 1q_cm2) == "1 cm^2"); + CHECK(fmt::format("{:%Q %Aq}", 1q_km2) == "1 km^2"); + CHECK(fmt::format("{:%Q %Aq}", 1q_ft2) == "1 ft^2"); } SECTION("density") { CHECK(fmt::format("{}", 1q_kg_per_m3) == "1 kg/m³"); + CHECK(fmt::format("{:%Q %Aq}", 1q_kg_per_m3) == "1 kg/m^3"); } SECTION("resistance") @@ -111,6 +120,11 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") CHECK(fmt::format("{}", 1q_kR) == "1 kΩ"); CHECK(fmt::format("{}", 1q_mR) == "1 mΩ"); CHECK(fmt::format("{}", 1q_MR) == "1 MΩ"); + + CHECK(fmt::format("{:%Q %Aq}", 1q_R) == "1 ohm"); + CHECK(fmt::format("{:%Q %Aq}", 1q_kR) == "1 kohm"); + CHECK(fmt::format("{:%Q %Aq}", 1q_mR) == "1 mohm"); + CHECK(fmt::format("{:%Q %Aq}", 1q_MR) == "1 Mohm"); } SECTION("voltage") @@ -120,6 +134,8 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") CHECK(fmt::format("{}", 1q_uV) == "1 µV"); CHECK(fmt::format("{}", 1q_nV) == "1 nV"); CHECK(fmt::format("{}", 1q_pV) == "1 pV"); + + CHECK(fmt::format("{:%Q %Aq}", 1q_uV) == "1 uV"); } SECTION("volume") @@ -129,6 +145,12 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") CHECK(fmt::format("{}", 1q_cm3) == "1 cm³"); CHECK(fmt::format("{}", 1q_km3) == "1 km³"); CHECK(fmt::format("{}", 1q_ft3) == "1 ft³"); + + CHECK(fmt::format("{:%Q %Aq}", 1q_m3) == "1 m^3"); + CHECK(fmt::format("{:%Q %Aq}", 1q_mm3) == "1 mm^3"); + CHECK(fmt::format("{:%Q %Aq}", 1q_cm3) == "1 cm^3"); + CHECK(fmt::format("{:%Q %Aq}", 1q_km3) == "1 km^3"); + CHECK(fmt::format("{:%Q %Aq}", 1q_ft3) == "1 ft^3"); } SECTION("frequency") @@ -150,11 +172,13 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") SECTION("acceleration") { CHECK(fmt::format("{}", 1q_m_per_s2) == "1 m/s²"); + CHECK(fmt::format("{:%Q %Aq}", 1q_m_per_s2) == "1 m/s^2"); } SECTION("momentum") { CHECK(fmt::format("{}", 1q_kg_m_per_s) == "1 kg⋅m/s"); + CHECK(fmt::format("{:%Q %Aq}", 1q_kg_m_per_s) == "1 kg.m/s"); } SECTION("energy") @@ -187,10 +211,14 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") { CHECK(fmt::format("{}", 1q_mi * 1q_mi * 1q_mi) == "1 [15900351812136/3814697265625 × 10⁹] m³"); CHECK(fmt::format("{}", 1q_au * 1q_au) == "1 [2237952291797391849 × 10⁴] m²"); + + CHECK(fmt::format("{:%Q %Aq}", 1q_mi * 1q_mi * 1q_mi) == "1 [15900351812136/3814697265625 x 10^9] m^3"); + CHECK(fmt::format("{:%Q %Aq}", 1q_au * 1q_au) == "1 [2237952291797391849 x 10^4] m^2"); } SECTION("unknown scaled unit with reference different than the dimension's coherent unit") { CHECK(fmt::format("{}", mass, gram>>(1)) == "1 [2/3 × 10⁻³] kg"); + CHECK(fmt::format("{:%Q %Aq}", mass, gram>>(1)) == "1 [2/3 x 10^-3] kg"); } }