From 3e30c779a61cd8cd75752d1d55f0997b1facb5d6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 17 Oct 2019 10:16:44 +0200 Subject: [PATCH] coherent_derived_unit and custom prefixes support added --- README.md | 2 + doc/DESIGN.md | 80 +++++++++++++------ src/include/units/dimensions/acceleration.h | 2 +- src/include/units/dimensions/area.h | 10 +-- src/include/units/dimensions/capacitance.h | 2 +- src/include/units/dimensions/current.h | 2 +- .../units/dimensions/electric_charge.h | 2 +- src/include/units/dimensions/energy.h | 4 +- src/include/units/dimensions/force.h | 2 +- src/include/units/dimensions/frequency.h | 2 +- src/include/units/dimensions/length.h | 2 +- .../units/dimensions/luminous_intensity.h | 2 +- src/include/units/dimensions/mass.h | 2 +- src/include/units/dimensions/power.h | 2 +- src/include/units/dimensions/pressure.h | 2 +- src/include/units/dimensions/substance.h | 2 +- src/include/units/dimensions/temperature.h | 2 +- src/include/units/dimensions/time.h | 2 +- src/include/units/dimensions/velocity.h | 2 +- src/include/units/dimensions/voltage.h | 2 +- src/include/units/dimensions/volume.h | 2 +- src/include/units/format.h | 27 ++----- src/include/units/prefix.h | 68 ++++++++++++++++ src/include/units/quantity.h | 14 ++-- src/include/units/unit.h | 33 +++----- test/unit_test/runtime/CMakeLists.txt | 1 + .../runtime/digital_information_test.cpp | 77 ++++++++++++++++++ test/unit_test/runtime/text_test.cpp | 16 ++-- test/unit_test/static/custom_unit_test.cpp | 10 +-- 29 files changed, 261 insertions(+), 115 deletions(-) create mode 100644 src/include/units/prefix.h create mode 100644 test/unit_test/runtime/digital_information_test.cpp diff --git a/README.md b/README.md index 5ff436fe..f91810fd 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ NOTE: This library as of now compiles correctly only with gcc-9.1 and newer. - Downcasting facility refactored so the user does not have to write the boilerplate code anymore - From now on base dimensions should inherit from `base_dimension` class template - Added unit symbols definitions to `base_dimension` and `derived_unit` + - Added `coherent_derived_unit` helper + - Added support for `operator<<` on `quantity` - 0.3.1 Sep 18, 2019 - cmcstl2 dependency changed to range-v3 0.9.1 diff --git a/doc/DESIGN.md b/doc/DESIGN.md index b1e85ad1..86fd0cfc 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -1,5 +1,6 @@ # `mp-units` - A Units Library for C++ + ## Summary `Units` is a compile-time enabled Modern C++ library that provides compile-time dimensional @@ -71,6 +72,7 @@ constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto } ``` + ## Basic Concepts Below UML diagram shows the most important entities in the library design and how they relate to @@ -256,35 +258,35 @@ struct unit : downcast_base> { }; ``` -All units are created with a `derived_unit` helper: +Coherent derived units (units with `ratio<1>`) are created with a `coherent_derived_unit` helper: + +```cpp +template +struct coherent_derived_unit; +``` + +The above type exposes public `symbol` and `prefix` member types used to print unit symbol names. + +For example to define the base unit of `length`: + +```cpp +struct metre : coherent_derived_unit {}; +``` + +Again, similarly to `derived_dimension`, the first class template parameter is a CRTP idiom used +to provide downcasting facility (described below). + +All other units are created with a `derived_unit` helper: ```cpp template struct derived_unit; ``` -For example to define the base unit of `length`: +The above type exposes public `symbol` member type used to print unit symbol names. -```cpp -struct metre : derived_unit {}; -``` - -Again, similarly to `derived_dimension`, the first class template parameter is a CRTP idiom used -to provide downcasting facility (described below). - -`derived_unit` has a few partial useful specializations: -- helper to create a base unit of a specified dimension - -```cpp -template -struct derived_unit; -``` - -```cpp -struct metre : derived_unit {}; -``` - -- helper to create other units for a specified dimension +`derived_unit` has a few useful partial specializations: +- helper to create non-coherent units for a specified dimension ```cpp template @@ -295,7 +297,7 @@ struct derived_unit; struct yard : derived_unit> {}; ``` -- helper to create a unit with a SI prefix +- helper to create a named unit with a SI prefix ```cpp template @@ -428,6 +430,22 @@ template [[nodiscard]] constexpr quantity quantity_cast(const quantity& q); ``` +#### `operator<<` + +The library tries its best to print a correct unit of the quantity. This is why it performs a series +of checks: +1. If the user predefined a unit with a `coherent_derived_unit` or `derived_unit` class templates, + the symbol provided by the user will be used (i.e. `60 W`). +2. If a quantity has an unknown unit for a dimension predefined by the user with `derived_dimension`, + the symbol of a coherent unit of this dimension will be used. Additionally: + - if `Prefix` template parameter of a `coherent_derived_unit` is different than `no_prefix` then + the prefix symbol (i.e. `8 cJ`) defined by the specialization of `units::prefix_symbol` will be + aded wherever possible (`Ratio` matches the prefix ratio), + - otherwise, non-standard ratio (i.e. `2 [60]Hz`) will be printed. +3. If a quantity has an unknown dimension, the symbols of base dimensions will be used to construct + a unit symbol (i.e. `2 m/kg^2`). In this case no prefix symbols are added. + + ## Strong types instead of aliases, and type downcasting facility Most of the important design decisions in the library are dictated by the requirement of providing @@ -658,14 +676,24 @@ In order to extend the library with custom dimensions the user has to: concept DigitalInformation = units::QuantityOf; ``` -4. Define units and register them to a downcasting facility: +4. If non-SI prefixes should be applied to the unit symbol, define a new prefix tag and provide + `prefix_symbol` specializations to provide their text representation: ```cpp - struct bit : units::derived_unit {}; + struct data_prefix; + + template<> inline constexpr std::string_view units::prefix_symbol> = "Ki"; + template<> inline constexpr std::string_view units::prefix_symbol> = "Mi"; + ``` + +5. Define units and register them to a downcasting facility: + + ```cpp + struct bit : units::coherent_derived_unit {}; struct byte : units::derived_unit> {}; ``` -5. Provide user-defined literals for the most important units: +6. Provide user-defined literals for the most important units: ```cpp inline namespace literals { diff --git a/src/include/units/dimensions/acceleration.h b/src/include/units/dimensions/acceleration.h index de3c4a79..29074bb7 100644 --- a/src/include/units/dimensions/acceleration.h +++ b/src/include/units/dimensions/acceleration.h @@ -31,7 +31,7 @@ namespace units { template concept Acceleration = QuantityOf; - struct metre_per_second_sq : derived_unit {}; + struct metre_per_second_sq : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/area.h b/src/include/units/dimensions/area.h index 976073f8..59121ed6 100644 --- a/src/include/units/dimensions/area.h +++ b/src/include/units/dimensions/area.h @@ -31,14 +31,18 @@ namespace units { template concept Area = QuantityOf; + struct square_metre : coherent_derived_unit {}; struct square_millimetre : derived_unit {}; struct square_centimetre : derived_unit {}; - struct square_metre : derived_unit {}; struct square_kilometre : derived_unit {}; struct square_foot : derived_unit {}; inline namespace literals { + // sq_m + constexpr auto operator""sq_m(unsigned long long l) { return quantity(l); } + constexpr auto operator""sq_m(long double l) { return quantity(l); } + // sq_mm constexpr auto operator""sq_mm(unsigned long long l) { return quantity(l); } constexpr auto operator""sq_mm(long double l) { return quantity(l); } @@ -47,10 +51,6 @@ namespace units { constexpr auto operator""sq_cm(unsigned long long l) { return quantity(l); } constexpr auto operator""sq_cm(long double l) { return quantity(l); } - // sq_m - constexpr auto operator""sq_m(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_m(long double l) { return quantity(l); } - // sq_km constexpr auto operator""sq_km(unsigned long long l) { return quantity(l); } constexpr auto operator""sq_km(long double l) { return quantity(l); } diff --git a/src/include/units/dimensions/capacitance.h b/src/include/units/dimensions/capacitance.h index b5b8705e..79a3432e 100644 --- a/src/include/units/dimensions/capacitance.h +++ b/src/include/units/dimensions/capacitance.h @@ -33,7 +33,7 @@ namespace units { template concept Capacitance = QuantityOf; - struct farad : derived_unit {}; + struct farad : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/current.h b/src/include/units/dimensions/current.h index cb8c6c82..5fb5fda7 100644 --- a/src/include/units/dimensions/current.h +++ b/src/include/units/dimensions/current.h @@ -32,7 +32,7 @@ namespace units { template concept Current = QuantityOf; - struct ampere : derived_unit {}; + struct ampere : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/electric_charge.h b/src/include/units/dimensions/electric_charge.h index 173871c8..4181e936 100644 --- a/src/include/units/dimensions/electric_charge.h +++ b/src/include/units/dimensions/electric_charge.h @@ -33,7 +33,7 @@ namespace units { template concept ElectricCharge = QuantityOf; - struct coulomb : derived_unit {}; + struct coulomb : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/energy.h b/src/include/units/dimensions/energy.h index 68245abf..8468fb27 100644 --- a/src/include/units/dimensions/energy.h +++ b/src/include/units/dimensions/energy.h @@ -27,13 +27,13 @@ #include namespace units { - + struct energy : derived_dimension, exp> {}; template concept Energy = QuantityOf; - struct joule : derived_unit {}; + struct joule : coherent_derived_unit {}; struct millijoule : derived_unit> {}; struct kilojoule : derived_unit> {}; struct megajoule : derived_unit> {}; diff --git a/src/include/units/dimensions/force.h b/src/include/units/dimensions/force.h index eaefeac4..52fcb3ab 100644 --- a/src/include/units/dimensions/force.h +++ b/src/include/units/dimensions/force.h @@ -33,7 +33,7 @@ namespace units { template concept Force = QuantityOf; - struct newton : derived_unit {}; + struct newton : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/frequency.h b/src/include/units/dimensions/frequency.h index 60aeab12..38c9b971 100644 --- a/src/include/units/dimensions/frequency.h +++ b/src/include/units/dimensions/frequency.h @@ -32,7 +32,7 @@ namespace units { template concept Frequency = QuantityOf; - struct hertz : derived_unit {}; + struct hertz : coherent_derived_unit {}; struct millihertz : derived_unit> {}; struct kilohertz : derived_unit> {}; struct megahertz : derived_unit> {}; diff --git a/src/include/units/dimensions/length.h b/src/include/units/dimensions/length.h index d43d1841..7aebd258 100644 --- a/src/include/units/dimensions/length.h +++ b/src/include/units/dimensions/length.h @@ -33,7 +33,7 @@ namespace units { concept Length = QuantityOf; // SI units - struct metre : derived_unit {}; + struct metre : coherent_derived_unit {}; struct millimetre : derived_unit> {}; struct centimetre : derived_unit> {}; struct kilometre : derived_unit> {}; diff --git a/src/include/units/dimensions/luminous_intensity.h b/src/include/units/dimensions/luminous_intensity.h index c344bdef..14df26ac 100644 --- a/src/include/units/dimensions/luminous_intensity.h +++ b/src/include/units/dimensions/luminous_intensity.h @@ -32,7 +32,7 @@ namespace units { template concept LuminousIntensity = QuantityOf; - struct candela : derived_unit {}; + struct candela : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/mass.h b/src/include/units/dimensions/mass.h index 3a4c0047..36c34826 100644 --- a/src/include/units/dimensions/mass.h +++ b/src/include/units/dimensions/mass.h @@ -32,8 +32,8 @@ namespace units { template concept Mass = QuantityOf; + struct kilogram : coherent_derived_unit {}; struct gram : derived_unit> {}; - struct kilogram : derived_unit> {}; inline namespace literals { diff --git a/src/include/units/dimensions/power.h b/src/include/units/dimensions/power.h index 0f09ec3f..a415a740 100644 --- a/src/include/units/dimensions/power.h +++ b/src/include/units/dimensions/power.h @@ -32,7 +32,7 @@ namespace units { template concept Power = QuantityOf; - struct watt : derived_unit {}; + struct watt : coherent_derived_unit {}; struct milliwatt : derived_unit> {}; struct kilowatt : derived_unit> {}; struct megawatt : derived_unit> {}; diff --git a/src/include/units/dimensions/pressure.h b/src/include/units/dimensions/pressure.h index 5b560de3..11fdd086 100644 --- a/src/include/units/dimensions/pressure.h +++ b/src/include/units/dimensions/pressure.h @@ -33,7 +33,7 @@ namespace units { template concept Pressure = QuantityOf; - struct pascal : derived_unit {}; + struct pascal : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/substance.h b/src/include/units/dimensions/substance.h index a0a0eebb..47a4d077 100644 --- a/src/include/units/dimensions/substance.h +++ b/src/include/units/dimensions/substance.h @@ -32,7 +32,7 @@ namespace units { template concept Substance = QuantityOf; - struct mole : derived_unit {}; + struct mole : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/temperature.h b/src/include/units/dimensions/temperature.h index a48dfbe5..e59b2e83 100644 --- a/src/include/units/dimensions/temperature.h +++ b/src/include/units/dimensions/temperature.h @@ -32,7 +32,7 @@ namespace units { template concept ThermodynamicTemperature = QuantityOf; - struct kelvin : derived_unit {}; + struct kelvin : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/time.h b/src/include/units/dimensions/time.h index 950e120b..46fd2e4c 100644 --- a/src/include/units/dimensions/time.h +++ b/src/include/units/dimensions/time.h @@ -32,7 +32,7 @@ namespace units { template concept Time = QuantityOf; - struct second : derived_unit {}; + struct second : coherent_derived_unit {}; struct nanosecond : derived_unit> {}; struct microsecond : derived_unit> {}; struct millisecond : derived_unit> {}; diff --git a/src/include/units/dimensions/velocity.h b/src/include/units/dimensions/velocity.h index 2b8d46f3..e3458686 100644 --- a/src/include/units/dimensions/velocity.h +++ b/src/include/units/dimensions/velocity.h @@ -32,7 +32,7 @@ namespace units { template concept Velocity = QuantityOf; - struct metre_per_second : derived_unit {}; + struct metre_per_second : coherent_derived_unit {}; struct kilometre_per_hour : derived_unit {}; struct mile_per_hour : derived_unit {}; diff --git a/src/include/units/dimensions/voltage.h b/src/include/units/dimensions/voltage.h index b06e217a..47886166 100644 --- a/src/include/units/dimensions/voltage.h +++ b/src/include/units/dimensions/voltage.h @@ -35,7 +35,7 @@ namespace units { template concept Voltage = QuantityOf; - struct volt : derived_unit {}; + struct volt : coherent_derived_unit {}; inline namespace literals { diff --git a/src/include/units/dimensions/volume.h b/src/include/units/dimensions/volume.h index 247a8e34..a0007a39 100644 --- a/src/include/units/dimensions/volume.h +++ b/src/include/units/dimensions/volume.h @@ -31,9 +31,9 @@ namespace units { template concept Volume = QuantityOf; + struct cubic_metre : coherent_derived_unit {}; struct cubic_millimetre : derived_unit {}; struct cubic_centimetre : derived_unit {}; - struct cubic_metre : derived_unit {}; struct cubic_kilometre : derived_unit {}; struct cubic_foot : derived_unit {}; diff --git a/src/include/units/format.h b/src/include/units/format.h index 1dbfc831..e6d62661 100644 --- a/src/include/units/format.h +++ b/src/include/units/format.h @@ -26,6 +26,9 @@ namespace units { + template + inline constexpr std::string_view prefix_symbol = ""; + namespace detail { template @@ -41,31 +44,11 @@ namespace units { } } - template - inline constexpr std::string_view ratio_txt = ""; - - template<> inline constexpr std::string_view ratio_txt> = "a"; - template<> inline constexpr std::string_view ratio_txt> = "f"; - template<> inline constexpr std::string_view ratio_txt> = "p"; - template<> inline constexpr std::string_view ratio_txt> = "n"; - template<> inline constexpr std::string_view ratio_txt> = "\u00b5\u0073"; - template<> inline constexpr std::string_view ratio_txt> = "m"; - template<> inline constexpr std::string_view ratio_txt> = "c"; - template<> inline constexpr std::string_view ratio_txt> = "d"; - template<> inline constexpr std::string_view ratio_txt> = "da"; - template<> inline constexpr std::string_view ratio_txt> = "h"; - template<> inline constexpr std::string_view ratio_txt> = "k"; - template<> inline constexpr std::string_view ratio_txt> = "M"; - template<> inline constexpr std::string_view ratio_txt> = "G"; - template<> inline constexpr std::string_view ratio_txt> = "T"; - template<> inline constexpr std::string_view ratio_txt> = "P"; - template<> inline constexpr std::string_view ratio_txt> = "E"; - - template + template void print_prefix_or_ratio(std::basic_ostream& os) { if constexpr(Ratio::num != 1 || Ratio::den != 1) { - constexpr auto prefix = ratio_txt; + constexpr auto prefix = prefix_symbol; if constexpr(prefix != "") { // print as a prefixed unit diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h new file mode 100644 index 00000000..a7d488e1 --- /dev/null +++ b/src/include/units/prefix.h @@ -0,0 +1,68 @@ +// 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 + +namespace units { + + // prefix tags + struct si_prefix; + + // SI prefixes + + template using atto = unit>>; + template using femto = unit>>; + template using pico = unit>>; + template using nano = unit>>; + template using micro = unit>>; + template using milli = unit>>; + template using centi = unit>>; + template using deca = unit>>; + template using hecto = unit>>; + template using kilo = unit>>; + template using mega = unit>>; + template using giga = unit>>; + template using tera = unit>>; + template using peta = unit>>; + template using exa = unit>>; + + template<> inline constexpr std::string_view prefix_symbol> = "a"; + template<> inline constexpr std::string_view prefix_symbol> = "f"; + template<> inline constexpr std::string_view prefix_symbol> = "p"; + template<> inline constexpr std::string_view prefix_symbol> = "n"; + template<> inline constexpr std::string_view prefix_symbol> = "\u00b5\u0073"; // ยต + template<> inline constexpr std::string_view prefix_symbol> = "m"; + template<> inline constexpr std::string_view prefix_symbol> = "c"; + template<> inline constexpr std::string_view prefix_symbol> = "d"; + template<> inline constexpr std::string_view prefix_symbol> = "da"; + template<> inline constexpr std::string_view prefix_symbol> = "h"; + template<> inline constexpr std::string_view prefix_symbol> = "k"; + template<> inline constexpr std::string_view prefix_symbol> = "M"; + template<> inline constexpr std::string_view prefix_symbol> = "G"; + template<> inline constexpr std::string_view prefix_symbol> = "T"; + template<> inline constexpr std::string_view prefix_symbol> = "P"; + template<> inline constexpr std::string_view prefix_symbol> = "E"; + +} diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index edffe3d1..ff2a86a9 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -23,8 +23,7 @@ #pragma once #include -#include -#include +#include #include #include @@ -290,13 +289,10 @@ namespace units { using ratio = quantity::unit::ratio; using dim = quantity::unit::dimension; if constexpr(!detail::is_dimension) { - // print as a prefix or ratio of a coherent unit - detail::print_prefix_or_ratio(os); - - if constexpr(!detail::is_dimension) { - // print coherent unit symbol defined by the user - os << downcast_target>::symbol::c_str(); - } + // print as a prefix or ratio of a coherent unit symbol defined by the user + using coherent_unit = downcast_target>>; + detail::print_prefix_or_ratio(os); + os << coherent_unit::symbol::c_str(); } else { // print as a ratio of a coherent unit diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 4fe06378..596bc439 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -28,7 +28,7 @@ namespace units { - template> + template requires (R::num * R::den > 0) struct unit : downcast_base> { using dimension = D; @@ -141,14 +141,17 @@ namespace units { // static constexpr auto symbol = Symbol; // }; + struct no_prefix; + + template + struct coherent_derived_unit : downcast_helper>> { + using symbol = Symbol; + using prefix = Prefix; + }; + template struct derived_unit; - template - struct derived_unit : downcast_helper>> { - using symbol = Symbol; - }; - template struct derived_unit : downcast_helper> { using symbol = Symbol; @@ -164,22 +167,4 @@ namespace units { using symbol = Symbol; }; - // SI prefixes - - template using atto = unit>>; - template using femto = unit>>; - template using pico = unit>>; - template using nano = unit>>; - template using micro = unit>>; - template using milli = unit>>; - template using centi = unit>>; - template using deca = unit>>; - template using hecto = unit>>; - template using kilo = unit>>; - template using mega = unit>>; - template using giga = unit>>; - template using tera = unit>>; - template using peta = unit>>; - template using exa = unit>>; - } // namespace units diff --git a/test/unit_test/runtime/CMakeLists.txt b/test/unit_test/runtime/CMakeLists.txt index cfce6f87..b8652202 100644 --- a/test/unit_test/runtime/CMakeLists.txt +++ b/test/unit_test/runtime/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(unit_tests_runtime catch_main.cpp + digital_information_test.cpp math_test.cpp text_test.cpp ) diff --git a/test/unit_test/runtime/digital_information_test.cpp b/test/unit_test/runtime/digital_information_test.cpp new file mode 100644 index 00000000..82315c68 --- /dev/null +++ b/test/unit_test/runtime/digital_information_test.cpp @@ -0,0 +1,77 @@ +// 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. + +#include +#include +#include + +namespace data { + + struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; + + struct digital_information : units::derived_dimension> {}; + + template + concept DigitalInformation = units::QuantityOf; + + using namespace units::hacks; + + struct data_prefix; + + struct bit : units::coherent_derived_unit {}; + struct kilobit : units::derived_unit> {}; + struct byte : units::derived_unit> {}; + + inline namespace literals { + + constexpr auto operator""_b(unsigned long long l) { return units::quantity(l); } + constexpr auto operator""_Kib(unsigned long long l) { return units::quantity(l); } + constexpr auto operator""_B(unsigned long long l) { return units::quantity(l); } + + } + +} + +template<> inline constexpr std::string_view units::prefix_symbol> = "Ki"; +template<> inline constexpr std::string_view units::prefix_symbol> = "Mi"; + +using namespace data; + +TEST_CASE("operator<< on a custom quantity", "[text][ostream]") +{ + std::stringstream stream; + + SECTION("quantity with a predefined unit and prefix") + { + SECTION("named unit") + { + stream << 64_B; + REQUIRE(stream.str() == "64 B"); + } + + SECTION("other unit matching prefix") + { + stream << 8_Kib * 8_Kib / 2_b; + REQUIRE(stream.str() == "32 Mib"); + } + } +} diff --git a/test/unit_test/runtime/text_test.cpp b/test/unit_test/runtime/text_test.cpp index 3ecfb4e5..c3c791ef 100644 --- a/test/unit_test/runtime/text_test.cpp +++ b/test/unit_test/runtime/text_test.cpp @@ -39,25 +39,31 @@ TEST_CASE("operator<< on a quantity", "[text][ostream]") { SECTION("integral representation") { - stream << 16sq_m; - REQUIRE(stream.str() == "16 m^2"); + stream << 60W; + REQUIRE(stream.str() == "60 W"); } SECTION("floating-point representation") { - stream << 72.5kmph; - REQUIRE(stream.str() == "72.5 km/h"); + stream << 72.5kJ; + REQUIRE(stream.str() == "72.5 kJ"); } } SECTION("quantity with a predefined dimension but unknown unit") { - SECTION("unit::ratio as an SI prefix") + SECTION("unit::ratio as an SI prefix for a dimension with a special symbol") { stream << 4.N * 2cm; REQUIRE(stream.str() == "8 cJ"); } + SECTION("unit::ratio for a dimension without a special symbol") + { + stream << 2.cm * 2m * 2m; + REQUIRE(stream.str() == "8 [1/100]m^3"); + } + SECTION("unit::ratio::num != 1 && unit::ratio::den == 1") { stream << 4 * 2min / (2s * 2s); diff --git a/test/unit_test/static/custom_unit_test.cpp b/test/unit_test/static/custom_unit_test.cpp index 7b2c2948..66660e82 100644 --- a/test/unit_test/static/custom_unit_test.cpp +++ b/test/unit_test/static/custom_unit_test.cpp @@ -37,7 +37,9 @@ namespace { using namespace units::hacks; - struct bit : units::derived_unit {}; + struct data_prefix {}; + + struct bit : units::coherent_derived_unit {}; struct byte : units::derived_unit> {}; inline namespace literals { @@ -63,13 +65,11 @@ namespace { // power spectral density struct power_spectral_density : derived_dimension, units::exp> {}; - struct sq_volt_per_hertz : derived_unit {}; + struct sq_volt_per_hertz : coherent_derived_unit {}; // amplitude spectral density struct amplitude_spectral_density : derived_dimension, units::exp> {}; - // TODO: add support for derived_unit - //struct volt_per_sq_hertz : derived_unit {}; - struct volt_per_sqrt_hertz : derived_unit {}; + struct volt_per_sqrt_hertz : coherent_derived_unit {}; } namespace {