From 53720c82b85533327e4f95d8c1dfc2cb2e82f68d Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 18 Oct 2019 23:50:43 +0200 Subject: [PATCH] fmt support started --- src/include/units/bits/format_utils.h | 92 +++++++++++++++ src/include/units/format.h | 88 ++------------ src/include/units/quantity.h | 2 +- test/unit_test/runtime/text_test.cpp | 160 +++++++++++++++++++++++--- 4 files changed, 249 insertions(+), 93 deletions(-) create mode 100644 src/include/units/bits/format_utils.h diff --git a/src/include/units/bits/format_utils.h b/src/include/units/bits/format_utils.h new file mode 100644 index 00000000..a16f384f --- /dev/null +++ b/src/include/units/bits/format_utils.h @@ -0,0 +1,92 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace units { + + namespace detail { + + template + void print_ratio(std::basic_ostream& os) + { + if constexpr(Ratio::num != 1 || Ratio::den != 1) { + if constexpr(Ratio::den == 1) { + os << "[" << Ratio::num << "]"; + } + else { + os << "[" << Ratio::num << "/" << Ratio::den << "]"; + } + } + } + + template + void print_prefix_or_ratio(std::basic_ostream& os) + { + if constexpr(Ratio::num != 1 || Ratio::den != 1) { + if(!std::same_as) { + using prefix = downcast_target>; + + if constexpr(!std::same_as>) { + // print as a prefixed unit + os << prefix::symbol; + return; + } + } + // print as a ratio of the coherent unit + print_ratio(os); + } + } + + template + void print_dimensions(std::basic_ostream& os, dimension) + { + bool first = true; + auto ingr_printer = [&](E) { + if constexpr(E::num < 0) { + os << (first ? "1/" : "/"); + } + else { + os << (first ? "" : "⋅"); + } + os << E::dimension::symbol; + if constexpr(E::den != 1) { + os << "^(" << abs(E::num) << "/" << E::den << ")"; + } + else if constexpr(abs(E::num) != 1) { + // if constexpr(is_unicode) + // os << superscript; + // else + os << "^" << abs(E::num); + } + first = false; + }; + (ingr_printer(Es{}), ...); + } + + } + +} // namespace units diff --git a/src/include/units/format.h b/src/include/units/format.h index 6728eeb3..2433b5a7 100644 --- a/src/include/units/format.h +++ b/src/include/units/format.h @@ -22,83 +22,17 @@ #pragma once -#include -#include -#include +#include +#include -namespace units { - - namespace detail { - - template - void print_ratio(std::basic_ostream& os) - { - if constexpr(Ratio::num != 1 || Ratio::den != 1) { - if constexpr(Ratio::den == 1) { - os << "[" << Ratio::num << "]"; - } - else { - os << "[" << Ratio::num << "/" << Ratio::den << "]"; - } - } - } - - template - void print_prefix_or_ratio(std::basic_ostream& os) - { - if constexpr(Ratio::num != 1 || Ratio::den != 1) { - if(!std::same_as) { - using prefix = downcast_target>; - - if constexpr(!std::same_as>) { - // print as a prefixed unit - os << prefix::symbol; - return; - } - } - // print as a ratio of the coherent unit - print_ratio(os); - } - } - - template - void print_dimensions(std::basic_ostream& os, dimension) - { - bool first = true; - auto ingr_printer = [&](E) { - if constexpr(E::num < 0) { - os << (first ? "1/" : "/"); - } - else { - os << (first ? "" : "⋅"); - } - os << E::dimension::symbol; - if constexpr(E::den != 1) { - os << "^(" << abs(E::num) << "/" << E::den << ")"; - } - else if constexpr(abs(E::num) != 1) { - // if constexpr(is_unicode) - // os << superscript; - // else - os << "^" << abs(E::num); - } - first = false; - }; - (ingr_printer(Es{}), ...); - } +template +struct fmt::formatter, CharT> { + template + constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } + template + auto format(const units::quantity& q, FormatContext& ctx) + { + return format_to(ctx.out(), "{:.1f}", q.count()); } - -} // namespace units - -// template -// struct fmt::formatter, CharT> { -// template -// constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } - -// template -// auto format(const units::quantity& q, FormatContext& ctx) -// { -// return format_to(ctx.out(), "{:.1f}, {:.1f}", p.x, p.y); -// } -// } +}; diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index ff7b55ac..86e45630 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -23,8 +23,8 @@ #pragma once #include +#include #include -#include #include #include diff --git a/test/unit_test/runtime/text_test.cpp b/test/unit_test/runtime/text_test.cpp index 6ca8d70e..1ec9ecfa 100644 --- a/test/unit_test/runtime/text_test.cpp +++ b/test/unit_test/runtime/text_test.cpp @@ -25,6 +25,7 @@ #include "units/dimensions/power.h" #include "units/dimensions/velocity.h" #include "units/dimensions/volume.h" +#include "units/format.h" #include "units/math.h" #include #include @@ -159,23 +160,152 @@ TEST_CASE("operator<< on a quantity", "[text][ostream]") } } -// SCENARIO("default format '{}' produces the same output as operator<<", "[text][fmt]") -// { -// GIVEN("A quantity q") { -// auto q = 2m; -// REQUIRE(q.count() == 2); +TEST_CASE("fmt with default format {} on a quantity", "[text][fmt]") +{ + std::stringstream stream; -// WHEN("format(\"{}\", q) is called") { -// std::string fmtstr = format("{}", q); + SECTION("quantity with a predefined unit") + { + SECTION("integral representation") + { + constexpr auto q = 60W; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } -// THEN("the same output as operator<< is returned") { -// std::stringstream stream; -// stream << q; -// REQUIRE(fmtstr == stream.str()); -// } -// } -// } -// } + SECTION("floating-point representation") + { + constexpr auto q = 72.5kJ; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit with a prefix") + { + constexpr auto q = 125us; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + } + + SECTION("quantity with a predefined dimension but unknown unit") + { + SECTION("unit::ratio as an SI prefix for a dimension with a special symbol") + { + constexpr auto q = 4.N * 2cm; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio for a dimension without a special symbol") + { + constexpr auto q = 2.cm * 2m * 2m; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio::num != 1 && unit::ratio::den == 1") + { + constexpr auto q = 4 * 2min / (2s * 2s); + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio::num == 1 && unit::ratio::den != 1") + { + constexpr auto q = 20._J / 2min; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio::num != 1 && unit::ratio::den != 1") + { + constexpr auto q = 60.kJ / 2min; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + } + + SECTION("quantity with an unkown dimension") + { + SECTION("unit::ratio::num == 1 && unit::ratio::den == 1") + { + constexpr auto q = 2s * 2m * 2kg; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio as an SI prefix") + { + constexpr auto q = 4km * 2s; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio::num != 1 && unit::ratio::den == 1") + { + constexpr auto q = 4kg * 2min / (2s * 2s); + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio::num == 1 && unit::ratio::den != 1") + { + constexpr auto q = 20.kg / 2min; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("unit::ratio::num != 1 && unit::ratio::den != 1") + { + constexpr auto q = 60.min / 2km; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("exp::num == 1 && exp::den == 1") + { + constexpr auto q = 4m * 2s; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("exp::num == 2 && exp::den == 1 for positive exponent") + { + constexpr auto q = 4m * 2s * 2s; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("exp::num == 2 && exp::den == 1 for negative exponent (first dimension)") + { + constexpr auto q = 8.s / 2m / 2m; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("exp::num == 2 && exp::den == 1 for negative exponent (not first dimension)") + { + constexpr auto q = 8.m / 2kg / 2kg; + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("fractional positive exponent") + { + auto q = sqrt(9.m); + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + + SECTION("fractional negative exponent") + { + auto q = sqrt(9 / 1.m); + stream << q; + REQUIRE(fmt::format("{}", q) == stream.str()); + } + } +} // Restate operator<< definitions in terms of std::format to make I/O manipulators apply to whole objects // rather than their parts