coherent_derived_unit and custom prefixes support added

This commit is contained in:
Mateusz Pusz
2019-10-17 10:16:44 +02:00
parent 9a9abca81d
commit 3e30c779a6
29 changed files with 261 additions and 115 deletions

View File

@@ -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 - 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 - From now on base dimensions should inherit from `base_dimension` class template
- Added unit symbols definitions to `base_dimension` and `derived_unit` - 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 - 0.3.1 Sep 18, 2019
- cmcstl2 dependency changed to range-v3 0.9.1 - cmcstl2 dependency changed to range-v3 0.9.1

View File

@@ -1,5 +1,6 @@
# `mp-units` - A Units Library for C++ # `mp-units` - A Units Library for C++
## Summary ## Summary
`Units` is a compile-time enabled Modern C++ library that provides compile-time dimensional `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 ## Basic Concepts
Below UML diagram shows the most important entities in the library design and how they relate to 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<unit<D, R>> {
}; };
``` ```
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<typename Child, fixed_string Symbol, Dimension D, typename Prefix = no_prefix>
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<metre, "m", length, si_prefix> {};
```
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 ```cpp
template<typename Child, fixed_string Symbol, typename...> template<typename Child, fixed_string Symbol, typename...>
struct derived_unit; 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 `derived_unit` has a few useful partial specializations:
struct metre : derived_unit<metre, "m", length> {}; - helper to create non-coherent units for a specified dimension
```
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<typename Child, fixed_string Symbol, Dimension D>
struct derived_unit<Child, Symbol, D>;
```
```cpp
struct metre : derived_unit<metre, "m", length> {};
```
- helper to create other units for a specified dimension
```cpp ```cpp
template<typename Child, fixed_string Symbol, Dimension D, Ratio R> template<typename Child, fixed_string Symbol, Dimension D, Ratio R>
@@ -295,7 +297,7 @@ struct derived_unit<Child, Symbol, D, R>;
struct yard : derived_unit<yard, "yd", length, ratio<9'144, 10'000>> {}; struct yard : derived_unit<yard, "yd", length, ratio<9'144, 10'000>> {};
``` ```
- helper to create a unit with a SI prefix - helper to create a named unit with a SI prefix
```cpp ```cpp
template<typename Child, fixed_string Symbol, Unit U> template<typename Child, fixed_string Symbol, Unit U>
@@ -428,6 +430,22 @@ template<Scalar ToRep, typename U, typename Rep>
[[nodiscard]] constexpr quantity<U, ToRep> quantity_cast(const quantity<U, Rep>& q); [[nodiscard]] constexpr quantity<U, ToRep> quantity_cast(const quantity<U, Rep>& 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 ## 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 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<T, digital_information>; concept DigitalInformation = units::QuantityOf<T, digital_information>;
``` ```
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 ```cpp
struct bit : units::derived_unit<bit, "b", digital_information> {}; struct data_prefix;
template<> inline constexpr std::string_view units::prefix_symbol<data_prefix, units::ratio< 1'024>> = "Ki";
template<> inline constexpr std::string_view units::prefix_symbol<data_prefix, units::ratio<1'048'576>> = "Mi";
```
5. Define units and register them to a downcasting facility:
```cpp
struct bit : units::coherent_derived_unit<bit, "b", digital_information, data_prefix> {};
struct byte : units::derived_unit<byte, "B", digital_information, units::ratio<8>> {}; struct byte : units::derived_unit<byte, "B", digital_information, units::ratio<8>> {};
``` ```
5. Provide user-defined literals for the most important units: 6. Provide user-defined literals for the most important units:
```cpp ```cpp
inline namespace literals { inline namespace literals {

View File

@@ -31,7 +31,7 @@ namespace units {
template<typename T> template<typename T>
concept Acceleration = QuantityOf<T, acceleration>; concept Acceleration = QuantityOf<T, acceleration>;
struct metre_per_second_sq : derived_unit<metre_per_second_sq, decltype("m/s^2"_fs), acceleration, metre, second> {}; struct metre_per_second_sq : coherent_derived_unit<metre_per_second_sq, decltype("m/s^2"_fs), acceleration> {};
inline namespace literals { inline namespace literals {

View File

@@ -31,14 +31,18 @@ namespace units {
template<typename T> template<typename T>
concept Area = QuantityOf<T, area>; concept Area = QuantityOf<T, area>;
struct square_metre : coherent_derived_unit<square_metre, decltype("m^2"_fs), area> {};
struct square_millimetre : derived_unit<square_millimetre, decltype("mm^2"_fs), area, millimetre> {}; struct square_millimetre : derived_unit<square_millimetre, decltype("mm^2"_fs), area, millimetre> {};
struct square_centimetre : derived_unit<square_centimetre, decltype("cm^2"_fs), area, centimetre> {}; struct square_centimetre : derived_unit<square_centimetre, decltype("cm^2"_fs), area, centimetre> {};
struct square_metre : derived_unit<square_metre, decltype("m^2"_fs), area, metre> {};
struct square_kilometre : derived_unit<square_kilometre, decltype("km^2"_fs), area, kilometre> {}; struct square_kilometre : derived_unit<square_kilometre, decltype("km^2"_fs), area, kilometre> {};
struct square_foot : derived_unit<square_foot, decltype("ft^2"_fs), area, foot> {}; struct square_foot : derived_unit<square_foot, decltype("ft^2"_fs), area, foot> {};
inline namespace literals { inline namespace literals {
// sq_m
constexpr auto operator""sq_m(unsigned long long l) { return quantity<square_metre, std::int64_t>(l); }
constexpr auto operator""sq_m(long double l) { return quantity<square_metre, long double>(l); }
// sq_mm // sq_mm
constexpr auto operator""sq_mm(unsigned long long l) { return quantity<square_millimetre, std::int64_t>(l); } constexpr auto operator""sq_mm(unsigned long long l) { return quantity<square_millimetre, std::int64_t>(l); }
constexpr auto operator""sq_mm(long double l) { return quantity<square_millimetre, long double>(l); } constexpr auto operator""sq_mm(long double l) { return quantity<square_millimetre, long double>(l); }
@@ -47,10 +51,6 @@ namespace units {
constexpr auto operator""sq_cm(unsigned long long l) { return quantity<square_centimetre, std::int64_t>(l); } constexpr auto operator""sq_cm(unsigned long long l) { return quantity<square_centimetre, std::int64_t>(l); }
constexpr auto operator""sq_cm(long double l) { return quantity<square_centimetre, long double>(l); } constexpr auto operator""sq_cm(long double l) { return quantity<square_centimetre, long double>(l); }
// sq_m
constexpr auto operator""sq_m(unsigned long long l) { return quantity<square_metre, std::int64_t>(l); }
constexpr auto operator""sq_m(long double l) { return quantity<square_metre, long double>(l); }
// sq_km // sq_km
constexpr auto operator""sq_km(unsigned long long l) { return quantity<square_kilometre, std::int64_t>(l); } constexpr auto operator""sq_km(unsigned long long l) { return quantity<square_kilometre, std::int64_t>(l); }
constexpr auto operator""sq_km(long double l) { return quantity<square_kilometre, long double>(l); } constexpr auto operator""sq_km(long double l) { return quantity<square_kilometre, long double>(l); }

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T> template<typename T>
concept Capacitance = QuantityOf<T, capacitance>; concept Capacitance = QuantityOf<T, capacitance>;
struct farad : derived_unit<farad, decltype("F"_fs), capacitance, kilogram, metre, second, ampere> {}; struct farad : coherent_derived_unit<farad, decltype("F"_fs), capacitance, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept Current = QuantityOf<T, current>; concept Current = QuantityOf<T, current>;
struct ampere : derived_unit<ampere, decltype("A"_fs), current> {}; struct ampere : coherent_derived_unit<ampere, decltype("A"_fs), current, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T> template<typename T>
concept ElectricCharge = QuantityOf<T, electric_charge>; concept ElectricCharge = QuantityOf<T, electric_charge>;
struct coulomb : derived_unit<coulomb, decltype("C"_fs), electric_charge, second, ampere> {}; struct coulomb : coherent_derived_unit<coulomb, decltype("C"_fs), electric_charge, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -27,13 +27,13 @@
#include <units/dimensions/pressure.h> #include <units/dimensions/pressure.h>
namespace units { namespace units {
struct energy : derived_dimension<energy, exp<force, 1>, exp<length, 1>> {}; struct energy : derived_dimension<energy, exp<force, 1>, exp<length, 1>> {};
template<typename T> template<typename T>
concept Energy = QuantityOf<T, energy>; concept Energy = QuantityOf<T, energy>;
struct joule : derived_unit<joule, decltype("J"_fs), energy, kilogram, metre, second> {}; struct joule : coherent_derived_unit<joule, decltype("J"_fs), energy, si_prefix> {};
struct millijoule : derived_unit<millijoule, decltype("mJ"_fs), milli<joule>> {}; struct millijoule : derived_unit<millijoule, decltype("mJ"_fs), milli<joule>> {};
struct kilojoule : derived_unit<kilojoule, decltype("kJ"_fs), kilo<joule>> {}; struct kilojoule : derived_unit<kilojoule, decltype("kJ"_fs), kilo<joule>> {};
struct megajoule : derived_unit<megajoule, decltype("MJ"_fs), mega<joule>> {}; struct megajoule : derived_unit<megajoule, decltype("MJ"_fs), mega<joule>> {};

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T> template<typename T>
concept Force = QuantityOf<T, force>; concept Force = QuantityOf<T, force>;
struct newton : derived_unit<newton, decltype("N"_fs), force, kilogram, metre, second> {}; struct newton : coherent_derived_unit<newton, decltype("N"_fs), force, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept Frequency = QuantityOf<T, frequency>; concept Frequency = QuantityOf<T, frequency>;
struct hertz : derived_unit<hertz, decltype("Hz"_fs), frequency, second> {}; struct hertz : coherent_derived_unit<hertz, decltype("Hz"_fs), frequency, si_prefix> {};
struct millihertz : derived_unit<millihertz, decltype("mHz"_fs), milli<hertz>> {}; struct millihertz : derived_unit<millihertz, decltype("mHz"_fs), milli<hertz>> {};
struct kilohertz : derived_unit<kilohertz, decltype("kHz"_fs), kilo<hertz>> {}; struct kilohertz : derived_unit<kilohertz, decltype("kHz"_fs), kilo<hertz>> {};
struct megahertz : derived_unit<megahertz, decltype("MHz"_fs), mega<hertz>> {}; struct megahertz : derived_unit<megahertz, decltype("MHz"_fs), mega<hertz>> {};

View File

@@ -33,7 +33,7 @@ namespace units {
concept Length = QuantityOf<T, length>; concept Length = QuantityOf<T, length>;
// SI units // SI units
struct metre : derived_unit<metre, decltype("m"_fs), length> {}; struct metre : coherent_derived_unit<metre, decltype("m"_fs), length, si_prefix> {};
struct millimetre : derived_unit<millimetre, decltype("mm"_fs), milli<metre>> {}; struct millimetre : derived_unit<millimetre, decltype("mm"_fs), milli<metre>> {};
struct centimetre : derived_unit<centimetre, decltype("cm"_fs), centi<metre>> {}; struct centimetre : derived_unit<centimetre, decltype("cm"_fs), centi<metre>> {};
struct kilometre : derived_unit<kilometre, decltype("km"_fs), kilo<metre>> {}; struct kilometre : derived_unit<kilometre, decltype("km"_fs), kilo<metre>> {};

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept LuminousIntensity = QuantityOf<T, luminous_intensity>; concept LuminousIntensity = QuantityOf<T, luminous_intensity>;
struct candela : derived_unit<candela, decltype("cd"_fs), luminous_intensity> {}; struct candela : coherent_derived_unit<candela, decltype("cd"_fs), luminous_intensity, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -32,8 +32,8 @@ namespace units {
template<typename T> template<typename T>
concept Mass = QuantityOf<T, mass>; concept Mass = QuantityOf<T, mass>;
struct kilogram : coherent_derived_unit<kilogram, decltype("kg"_fs), mass> {};
struct gram : derived_unit<gram, decltype("g"_fs), mass, ratio<1, 1000>> {}; struct gram : derived_unit<gram, decltype("g"_fs), mass, ratio<1, 1000>> {};
struct kilogram : derived_unit<kilogram, decltype("kg"_fs), kilo<gram>> {};
inline namespace literals { inline namespace literals {

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept Power = QuantityOf<T, power>; concept Power = QuantityOf<T, power>;
struct watt : derived_unit<watt, decltype("W"_fs), power, kilogram, metre, second> {}; struct watt : coherent_derived_unit<watt, decltype("W"_fs), power, si_prefix> {};
struct milliwatt : derived_unit<milliwatt, decltype("mW"_fs), milli<watt>> {}; struct milliwatt : derived_unit<milliwatt, decltype("mW"_fs), milli<watt>> {};
struct kilowatt : derived_unit<kilowatt, decltype("kW"_fs), kilo<watt>> {}; struct kilowatt : derived_unit<kilowatt, decltype("kW"_fs), kilo<watt>> {};
struct megawatt : derived_unit<megawatt, decltype("MW"_fs), mega<watt>> {}; struct megawatt : derived_unit<megawatt, decltype("MW"_fs), mega<watt>> {};

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T> template<typename T>
concept Pressure = QuantityOf<T, pressure>; concept Pressure = QuantityOf<T, pressure>;
struct pascal : derived_unit<pascal, decltype("Pa"_fs), pressure, kilogram, metre, second> {}; struct pascal : coherent_derived_unit<pascal, decltype("Pa"_fs), pressure, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept Substance = QuantityOf<T, substance>; concept Substance = QuantityOf<T, substance>;
struct mole : derived_unit<mole, decltype("mol"_fs), substance> {}; struct mole : coherent_derived_unit<mole, decltype("mol"_fs), substance, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept ThermodynamicTemperature = QuantityOf<T, temperature>; concept ThermodynamicTemperature = QuantityOf<T, temperature>;
struct kelvin : derived_unit<kelvin, decltype("K"_fs), temperature> {}; struct kelvin : coherent_derived_unit<kelvin, decltype("K"_fs), temperature> {};
inline namespace literals { inline namespace literals {

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept Time = QuantityOf<T, time>; concept Time = QuantityOf<T, time>;
struct second : derived_unit<second, decltype("s"_fs), time> {}; struct second : coherent_derived_unit<second, decltype("s"_fs), time, si_prefix> {};
struct nanosecond : derived_unit<nanosecond, decltype("ns"_fs), nano<second>> {}; struct nanosecond : derived_unit<nanosecond, decltype("ns"_fs), nano<second>> {};
struct microsecond : derived_unit<microsecond, decltype("us"_fs), micro<second>> {}; struct microsecond : derived_unit<microsecond, decltype("us"_fs), micro<second>> {};
struct millisecond : derived_unit<millisecond, decltype("ms"_fs), milli<second>> {}; struct millisecond : derived_unit<millisecond, decltype("ms"_fs), milli<second>> {};

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T> template<typename T>
concept Velocity = QuantityOf<T, velocity>; concept Velocity = QuantityOf<T, velocity>;
struct metre_per_second : derived_unit<metre_per_second, decltype("m/s"_fs), velocity, metre, second> {}; struct metre_per_second : coherent_derived_unit<metre_per_second, decltype("m/s"_fs), velocity> {};
struct kilometre_per_hour : derived_unit<kilometre_per_hour, decltype("km/h"_fs), velocity, kilometre, hour> {}; struct kilometre_per_hour : derived_unit<kilometre_per_hour, decltype("km/h"_fs), velocity, kilometre, hour> {};
struct mile_per_hour : derived_unit<mile_per_hour, decltype("mi/h"_fs), velocity, mile, hour> {}; struct mile_per_hour : derived_unit<mile_per_hour, decltype("mi/h"_fs), velocity, mile, hour> {};

View File

@@ -35,7 +35,7 @@ namespace units {
template<typename T> template<typename T>
concept Voltage = QuantityOf<T, voltage>; concept Voltage = QuantityOf<T, voltage>;
struct volt : derived_unit<volt, decltype("V"_fs), voltage, kilogram, metre, second, ampere> {}; struct volt : coherent_derived_unit<volt, decltype("V"_fs), voltage, si_prefix> {};
inline namespace literals { inline namespace literals {

View File

@@ -31,9 +31,9 @@ namespace units {
template<typename T> template<typename T>
concept Volume = QuantityOf<T, volume>; concept Volume = QuantityOf<T, volume>;
struct cubic_metre : coherent_derived_unit<cubic_metre, decltype("m^3"_fs), volume> {};
struct cubic_millimetre : derived_unit<cubic_millimetre, decltype("mm^3"_fs), volume, millimetre> {}; struct cubic_millimetre : derived_unit<cubic_millimetre, decltype("mm^3"_fs), volume, millimetre> {};
struct cubic_centimetre : derived_unit<cubic_centimetre, decltype("cm^3"_fs), volume, centimetre> {}; struct cubic_centimetre : derived_unit<cubic_centimetre, decltype("cm^3"_fs), volume, centimetre> {};
struct cubic_metre : derived_unit<cubic_metre, decltype("m^3"_fs), volume, metre> {};
struct cubic_kilometre : derived_unit<cubic_kilometre, decltype("km^3"_fs), volume, kilometre, metre> {}; struct cubic_kilometre : derived_unit<cubic_kilometre, decltype("km^3"_fs), volume, kilometre, metre> {};
struct cubic_foot : derived_unit<cubic_foot, decltype("ft^3"_fs), volume, foot> {}; struct cubic_foot : derived_unit<cubic_foot, decltype("ft^3"_fs), volume, foot> {};

View File

@@ -26,6 +26,9 @@
namespace units { namespace units {
template<typename Prefix, typename Ratio>
inline constexpr std::string_view prefix_symbol = "";
namespace detail { namespace detail {
template<typename Ratio, typename CharT, typename Traits> template<typename Ratio, typename CharT, typename Traits>
@@ -41,31 +44,11 @@ namespace units {
} }
} }
template<typename Ratio> template<typename Ratio, typename Prefix, typename CharT, typename Traits>
inline constexpr std::string_view ratio_txt = "";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::atto::den>> = "a";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::femto::den>> = "f";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::pico::den>> = "p";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::nano::den>> = "n";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::micro::den>> = "\u00b5\u0073";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::milli::den>> = "m";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::centi::den>> = "c";
template<> inline constexpr std::string_view ratio_txt<ratio<1, std::deci::den>> = "d";
template<> inline constexpr std::string_view ratio_txt<ratio<std::deca::num>> = "da";
template<> inline constexpr std::string_view ratio_txt<ratio<std::hecto::num>> = "h";
template<> inline constexpr std::string_view ratio_txt<ratio<std::kilo::num>> = "k";
template<> inline constexpr std::string_view ratio_txt<ratio<std::mega::num>> = "M";
template<> inline constexpr std::string_view ratio_txt<ratio<std::giga::num>> = "G";
template<> inline constexpr std::string_view ratio_txt<ratio<std::tera::num>> = "T";
template<> inline constexpr std::string_view ratio_txt<ratio<std::peta::num>> = "P";
template<> inline constexpr std::string_view ratio_txt<ratio<std::exa::num>> = "E";
template<typename Ratio, typename CharT, typename Traits>
void print_prefix_or_ratio(std::basic_ostream<CharT, Traits>& os) void print_prefix_or_ratio(std::basic_ostream<CharT, Traits>& os)
{ {
if constexpr(Ratio::num != 1 || Ratio::den != 1) { if constexpr(Ratio::num != 1 || Ratio::den != 1) {
constexpr auto prefix = ratio_txt<Ratio>; constexpr auto prefix = prefix_symbol<Prefix, Ratio>;
if constexpr(prefix != "") { if constexpr(prefix != "") {
// print as a prefixed unit // print as a prefixed unit

View File

@@ -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 <units/unit.h>
#include <units/format.h>
namespace units {
// prefix tags
struct si_prefix;
// SI prefixes
template<Unit U> using atto = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::atto::den>>>;
template<Unit U> using femto = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::femto::den>>>;
template<Unit U> using pico = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::pico::den>>>;
template<Unit U> using nano = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::nano::den>>>;
template<Unit U> using micro = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::micro::den>>>;
template<Unit U> using milli = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::milli::den>>>;
template<Unit U> using centi = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::centi::den>>>;
template<Unit U> using deca = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::deca::num>>>;
template<Unit U> using hecto = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::hecto::num>>>;
template<Unit U> using kilo = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::kilo::num>>>;
template<Unit U> using mega = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::mega::num>>>;
template<Unit U> using giga = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::giga::num>>>;
template<Unit U> using tera = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::tera::num>>>;
template<Unit U> using peta = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::peta::num>>>;
template<Unit U> using exa = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::exa::num>>>;
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::atto::den>> = "a";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::femto::den>> = "f";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::pico::den>> = "p";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::nano::den>> = "n";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::micro::den>> = "\u00b5\u0073"; // µ
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::milli::den>> = "m";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::centi::den>> = "c";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<1, std::deci::den>> = "d";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::deca::num>> = "da";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::hecto::num>> = "h";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::kilo::num>> = "k";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::mega::num>> = "M";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::giga::num>> = "G";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::tera::num>> = "T";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::peta::num>> = "P";
template<> inline constexpr std::string_view prefix_symbol<si_prefix, ratio<std::exa::num>> = "E";
}

View File

@@ -23,8 +23,7 @@
#pragma once #pragma once
#include <units/bits/concepts.h> #include <units/bits/concepts.h>
#include <units/unit.h> #include <units/prefix.h>
#include <units/format.h>
#include <limits> #include <limits>
#include <ostream> #include <ostream>
@@ -290,13 +289,10 @@ namespace units {
using ratio = quantity::unit::ratio; using ratio = quantity::unit::ratio;
using dim = quantity::unit::dimension; using dim = quantity::unit::dimension;
if constexpr(!detail::is_dimension<dim>) { if constexpr(!detail::is_dimension<dim>) {
// print as a prefix or ratio of a coherent unit // print as a prefix or ratio of a coherent unit symbol defined by the user
detail::print_prefix_or_ratio<ratio>(os); using coherent_unit = downcast_target<units::unit<dim, units::ratio<1>>>;
detail::print_prefix_or_ratio<ratio, typename coherent_unit::prefix>(os);
if constexpr(!detail::is_dimension<dim>) { os << coherent_unit::symbol::c_str();
// print coherent unit symbol defined by the user
os << downcast_target<units::unit<dim>>::symbol::c_str();
}
} }
else { else {
// print as a ratio of a coherent unit // print as a ratio of a coherent unit

View File

@@ -28,7 +28,7 @@
namespace units { namespace units {
template<Dimension D, Ratio R = ratio<1>> template<Dimension D, Ratio R>
requires (R::num * R::den > 0) requires (R::num * R::den > 0)
struct unit : downcast_base<unit<D, R>> { struct unit : downcast_base<unit<D, R>> {
using dimension = D; using dimension = D;
@@ -141,14 +141,17 @@ namespace units {
// static constexpr auto symbol = Symbol; // static constexpr auto symbol = Symbol;
// }; // };
struct no_prefix;
template<typename Child, typename Symbol, Dimension D, typename Prefix = no_prefix>
struct coherent_derived_unit : downcast_helper<Child, unit<D, ratio<1>>> {
using symbol = Symbol;
using prefix = Prefix;
};
template<typename Child, typename Symbol, typename...> template<typename Child, typename Symbol, typename...>
struct derived_unit; struct derived_unit;
template<typename Child, typename Symbol, Dimension D>
struct derived_unit<Child, Symbol, D> : downcast_helper<Child, unit<D, ratio<1>>> {
using symbol = Symbol;
};
template<typename Child, typename Symbol, Dimension D, Ratio R> template<typename Child, typename Symbol, Dimension D, Ratio R>
struct derived_unit<Child, Symbol, D, R> : downcast_helper<Child, unit<D, R>> { struct derived_unit<Child, Symbol, D, R> : downcast_helper<Child, unit<D, R>> {
using symbol = Symbol; using symbol = Symbol;
@@ -164,22 +167,4 @@ namespace units {
using symbol = Symbol; using symbol = Symbol;
}; };
// SI prefixes
template<Unit U> using atto = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::atto::den>>>;
template<Unit U> using femto = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::femto::den>>>;
template<Unit U> using pico = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::pico::den>>>;
template<Unit U> using nano = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::nano::den>>>;
template<Unit U> using micro = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::micro::den>>>;
template<Unit U> using milli = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::milli::den>>>;
template<Unit U> using centi = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<1, std::centi::den>>>;
template<Unit U> using deca = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::deca::num>>>;
template<Unit U> using hecto = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::hecto::num>>>;
template<Unit U> using kilo = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::kilo::num>>>;
template<Unit U> using mega = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::mega::num>>>;
template<Unit U> using giga = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::giga::num>>>;
template<Unit U> using tera = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::tera::num>>>;
template<Unit U> using peta = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::peta::num>>>;
template<Unit U> using exa = unit<typename U::dimension, ratio_multiply<typename U::ratio, ratio<std::exa::num>>>;
} // namespace units } // namespace units

View File

@@ -22,6 +22,7 @@
add_executable(unit_tests_runtime add_executable(unit_tests_runtime
catch_main.cpp catch_main.cpp
digital_information_test.cpp
math_test.cpp math_test.cpp
text_test.cpp text_test.cpp
) )

View File

@@ -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 <units/quantity.h>
#include <catch2/catch.hpp>
#include <sstream>
namespace data {
struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {};
struct digital_information : units::derived_dimension<digital_information, units::exp<base_dim_digital_information, 1>> {};
template<typename T>
concept DigitalInformation = units::QuantityOf<T, digital_information>;
using namespace units::hacks;
struct data_prefix;
struct bit : units::coherent_derived_unit<bit, decltype("b"_fs), digital_information, data_prefix> {};
struct kilobit : units::derived_unit<bit, decltype("Kib"_fs), digital_information, units::ratio<1'024>> {};
struct byte : units::derived_unit<byte, decltype("B"_fs), digital_information, units::ratio<8>> {};
inline namespace literals {
constexpr auto operator""_b(unsigned long long l) { return units::quantity<bit, std::int64_t>(l); }
constexpr auto operator""_Kib(unsigned long long l) { return units::quantity<kilobit, std::int64_t>(l); }
constexpr auto operator""_B(unsigned long long l) { return units::quantity<byte, std::int64_t>(l); }
}
}
template<> inline constexpr std::string_view units::prefix_symbol<data::data_prefix, units::ratio< 1'024>> = "Ki";
template<> inline constexpr std::string_view units::prefix_symbol<data::data_prefix, units::ratio<1'048'576>> = "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");
}
}
}

View File

@@ -39,25 +39,31 @@ TEST_CASE("operator<< on a quantity", "[text][ostream]")
{ {
SECTION("integral representation") SECTION("integral representation")
{ {
stream << 16sq_m; stream << 60W;
REQUIRE(stream.str() == "16 m^2"); REQUIRE(stream.str() == "60 W");
} }
SECTION("floating-point representation") SECTION("floating-point representation")
{ {
stream << 72.5kmph; stream << 72.5kJ;
REQUIRE(stream.str() == "72.5 km/h"); REQUIRE(stream.str() == "72.5 kJ");
} }
} }
SECTION("quantity with a predefined dimension but unknown unit") 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; stream << 4.N * 2cm;
REQUIRE(stream.str() == "8 cJ"); 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") SECTION("unit::ratio::num != 1 && unit::ratio::den == 1")
{ {
stream << 4 * 2min / (2s * 2s); stream << 4 * 2min / (2s * 2s);

View File

@@ -37,7 +37,9 @@ namespace {
using namespace units::hacks; using namespace units::hacks;
struct bit : units::derived_unit<bit, decltype("b"_fs), digital_information> {}; struct data_prefix {};
struct bit : units::coherent_derived_unit<bit, decltype("b"_fs), digital_information, data_prefix> {};
struct byte : units::derived_unit<byte, decltype("B"_fs), digital_information, units::ratio<8>> {}; struct byte : units::derived_unit<byte, decltype("B"_fs), digital_information, units::ratio<8>> {};
inline namespace literals { inline namespace literals {
@@ -63,13 +65,11 @@ namespace {
// power spectral density // power spectral density
struct power_spectral_density : derived_dimension<power_spectral_density, units::exp<voltage, 2>, units::exp<frequency, -1>> {}; struct power_spectral_density : derived_dimension<power_spectral_density, units::exp<voltage, 2>, units::exp<frequency, -1>> {};
struct sq_volt_per_hertz : derived_unit<sq_volt_per_hertz, decltype("V^2/Hz"_fs), power_spectral_density> {}; struct sq_volt_per_hertz : coherent_derived_unit<sq_volt_per_hertz, decltype("V^2/Hz"_fs), power_spectral_density> {};
// amplitude spectral density // amplitude spectral density
struct amplitude_spectral_density : derived_dimension<amplitude_spectral_density, units::exp<voltage, 1>, units::exp<frequency, -1, 2>> {}; struct amplitude_spectral_density : derived_dimension<amplitude_spectral_density, units::exp<voltage, 1>, units::exp<frequency, -1, 2>> {};
// TODO: add support for derived_unit struct volt_per_sqrt_hertz : coherent_derived_unit<volt_per_sqrt_hertz, decltype("V/Hz^(1/2)"_fs), amplitude_spectral_density> {};
//struct volt_per_sq_hertz : derived_unit<volt_per_sq_hertz, amplitude_spectral_density, volt, hertz> {};
struct volt_per_sqrt_hertz : derived_unit<volt_per_sqrt_hertz, decltype("V/Hz^(1/2)"_fs), amplitude_spectral_density> {};
} }
namespace { namespace {