Derived unit factory helpers refactored

This commit is contained in:
Mateusz Pusz
2019-11-08 16:51:45 +00:00
parent ad895b4651
commit 0125bd7762
23 changed files with 116 additions and 92 deletions

View File

@@ -118,9 +118,10 @@ NOTE: This library as of now compiles correctly only with gcc-9.1 and newer.
- `units` removed from a `std::experimental` namespace
- 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 unit symbols definitions to `base_dimension` and derived units
- Added support for `operator<<` on `quantity`
- `fmt` support added
- Derived unit factory helpers refactored
- Refactored the way prefixed units are defined
- 0.3.1 Sep 18, 2019

View File

@@ -246,62 +246,74 @@ concept Unit =
detail::is_unit<downcast_base_t<T>>; // exposition only
```
The library provides a bunch of helpers to create the derived unit:
- `named_coherent_derived_unit<Child, Dim, Symbol, PrefixType>`
- `coherent_derived_unit<Child, Dim>`
- `named_scaled_derived_unit<Child, Dim, Symbol, R, PrefixType = no_prefix>`
- `named_deduced_derived_unit<Child, Dim, Symbol, PrefixType, U, Us...>`
- `deduced_derived_unit<Child, Dim, U, Us..>`
- `prefixed_derived_unit<Child, P, U>`
Coherent derived units (units with `ratio<1>`) are created with a `named_coherent_derived_unit`
or `coherent_derived_unit` class templates:
```cpp
template<typename Child, basic_fixed_string Symbol, Dimension D, typename PrefixType = no_prefix>
struct named_coherent_derived_unit : downcast_child<Child, unit<D, ratio<1>>> {
template<typename Child, Dimension Dim, basic_fixed_string Symbol, PrefixType PT>
struct named_coherent_derived_unit : downcast_child<Child, unit<Dim, ratio<1>>> {
static constexpr auto symbol = Symbol;
using prefix_type = PrefixType;
using prefix_type = PT;
};
template<typename Child, Dimension D, typename PrefixType = no_prefix>
struct coherent_derived_unit : downcast_child<Child, unit<D, ratio<1>>> {
template<typename Child, Dimension Dim>
struct coherent_derived_unit : downcast_child<Child, unit<Dim, ratio<1>>> {
static constexpr auto symbol = /* ... */;
using prefix_type = PrefixType;
using prefix_type = no_prefix;
};
```
The above exposes public `prefix_type` member type and `symbol` used to print unit symbol
names. `prefix_type` is a tag type used to identify the type of prefixes to be used (i.e. SI,
data).
data) and should satisfy the following concept:
```cpp
template<typename T>
concept PrefixType = std::derived_from<T, prefix_type>;
```
For example to define the coherent unit of `length`:
```cpp
struct metre : named_coherent_derived_unit<metre, "m", length, si_prefix> {};
struct metre : named_coherent_derived_unit<metre, length, "m", si_prefix> {};
```
Again, similarly to `derived_dimension`, the first class template parameter is a CRTP idiom used
to provide downcasting facility (described below).
To create the rest of derived units the following class templates can be used:
To create scaled unit the following template should be used:
```cpp
template<typename Child, basic_fixed_string Symbol, Dimension D, Ratio R>
struct named_derived_unit : downcast_child<Child, unit<D, R>> {
template<typename Child, Dimension Dim, basic_fixed_string Symbol, Ratio R, PrefixType PT = no_prefix>
struct named_scaled_derived_unit : downcast_child<Child, unit<Dim, R>> {
static constexpr auto symbol = Symbol;
using prefix_type = PT;
};
```
User has to provide a symbol name (in case of a named unit), dimension, and a ratio relative
to a coherent derived unit. For example to define `minute`:
For example to define `minute`:
```cpp
struct minute : named_derived_unit<minute, "min", time, ratio<60>> {};
struct minute : named_scaled_derived_unit<minute, time, "min", ratio<60>> {};
```
The `mp-units` library provides also a few helper class templates to simplify the above process.
The `mp-units` library provides also a helper class templates to simplify the above process.
For example to create a prefixed unit the following may be used:
```cpp
template<typename Child, Prefix P, Unit U>
requires requires { U::symbol; }
requires (!std::same_as<typename U::prefix_type, no_prefix>)
struct prefixed_derived_unit : downcast_child<Child, unit<typename U::dimension,
ratio_multiply<typename P::ratio,
typename U::ratio>>> {
ratio_multiply<typename P::ratio,
typename U::ratio>>> {
static constexpr auto symbol = P::symbol + U::symbol;
using prefix_type = P::prefix_type;
};
@@ -310,8 +322,8 @@ struct prefixed_derived_unit : downcast_child<Child, unit<typename U::dimension,
where `Prefix` is a concept requiring the instantiation of the following class template:
```cpp
template<typename Child, typename PrefixType, Ratio R, basic_fixed_string Symbol>
struct prefix : downcast_child<Child, detail::prefix_base<PrefixType, R>> {
template<typename Child, PrefixType PT, Ratio R, basic_fixed_string Symbol>
struct prefix : downcast_child<Child, detail::prefix_base<PT, R>> {
static constexpr auto symbol = Symbol;
};
```
@@ -323,25 +335,28 @@ or its symbol and just has to do the following:
struct kilometre : prefixed_derived_unit<kilometre, kilo, metre> {};
```
SI prefixes are predefined in the library and the user may easily predefined his/her own with:
SI prefixes are predefined in the library and the user may easily provide his/her own with:
```cpp
struct data_prefix;
struct data_prefix : units::prefix_type {};
struct kibi : units::prefix<kibi, data_prefix, units::ratio<1'024>, "Ki"> {};
struct kibi : units::prefix<kibi, data_prefix, units::ratio< 1'024>, "Ki"> {};
struct mebi : units::prefix<mebi, data_prefix, units::ratio<1'048'576>, "Mi"> {};
```
For the cases where determining the exact ratio is not trivial another helper can be used:
```cpp
template<typename Child, basic_fixed_string Symbol, Dimension D, Unit U, Unit... Us>
struct named_deduced_derived_unit : downcast_child<Child, detail::make_derived_unit<D, U, Us...>> {
template<typename Child, Dimension Dim, basic_fixed_string Symbol, PrefixType PT, Unit U, Unit... Us>
struct named_deduced_derived_unit : downcast_child<Child, /* magic to get the correct type */> {
static constexpr auto symbol = Symbol;
using prefix_type = PT;
};
template<typename Child, Dimension D, Unit U, Unit... Us>
struct deduced_derived_unit : downcast_child<Child, detail::make_derived_unit<D, U, Us...>> {
static constexpr auto symbol = /* ... */;
template<typename Child, Dimension Dim, Unit U, Unit... Us>
struct deduced_derived_unit : downcast_child<Child, /* magic to get the correct type */> {
static constexpr auto symbol = /* even more magic to get the correct unit symbol */;
using prefix_type = no_prefix;
};
```
@@ -350,7 +365,7 @@ dimension and in case the symbol name is not explicitly provided it with create
on the provided ingredients:
```cpp
struct mile_per_hour : deduced_derived_unit<mile_per_hour, velocity, mile, hour> {};
struct kilometre_per_hour : deduced_derived_unit<kilometre_per_hour, velocity, kilometre, hour> {};
```
@@ -590,7 +605,7 @@ aliases.
template<typename BaseType>
struct downcast_base {
using base_type = BaseType;
friend auto downcast_guide(downcast_base);
friend auto downcast_guide(downcast_base); // declaration only (no implementation)
};
```

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T>
concept Capacitance = QuantityOf<T, capacitance>;
struct farad : named_coherent_derived_unit<farad, "F", capacitance, si_prefix> {};
struct farad : named_coherent_derived_unit<farad, capacitance, "F", si_prefix> {};
inline namespace literals {

View File

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

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T>
concept ElectricCharge = QuantityOf<T, electric_charge>;
struct coulomb : named_coherent_derived_unit<coulomb, "C", electric_charge, si_prefix> {};
struct coulomb : named_coherent_derived_unit<coulomb, electric_charge, "C", si_prefix> {};
inline namespace literals {

View File

@@ -34,7 +34,7 @@ namespace units {
template<typename T>
concept Energy = QuantityOf<T, energy>;
struct joule : named_coherent_derived_unit<joule, "J", energy, si_prefix> {};
struct joule : named_coherent_derived_unit<joule, energy, "J", si_prefix> {};
struct millijoule : prefixed_derived_unit<millijoule, milli, joule> {};
struct kilojoule : prefixed_derived_unit<kilojoule, kilo, joule> {};
struct megajoule : prefixed_derived_unit<megajoule, mega, joule> {};

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T>
concept Force = QuantityOf<T, force>;
struct newton : named_coherent_derived_unit<newton, "N", force, si_prefix> {};
struct newton : named_coherent_derived_unit<newton, force, "N", si_prefix> {};
inline namespace literals {

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T>
concept Frequency = QuantityOf<T, frequency>;
struct hertz : named_coherent_derived_unit<hertz, "Hz", frequency, si_prefix> {};
struct hertz : named_coherent_derived_unit<hertz, frequency, "Hz", si_prefix> {};
struct millihertz : prefixed_derived_unit<millihertz, milli, hertz> {};
struct kilohertz : prefixed_derived_unit<kilohertz, kilo, hertz> {};
struct megahertz : prefixed_derived_unit<megahertz, mega, hertz> {};

View File

@@ -34,7 +34,7 @@ namespace units {
concept Length = QuantityOf<T, length>;
// SI units
struct metre : named_coherent_derived_unit<metre, "m", length, si_prefix> {};
struct metre : named_coherent_derived_unit<metre, length, "m", si_prefix> {};
struct millimetre : prefixed_derived_unit<millimetre, milli, metre> {};
struct centimetre : prefixed_derived_unit<centimetre, centi, metre> {};
struct kilometre : prefixed_derived_unit<kilometre, kilo, metre> {};
@@ -60,10 +60,10 @@ namespace units {
} // namespace literals
// US customary units
struct yard : named_derived_unit<yard, "yd", length, ratio<9'144, 10'000>> {};
struct foot : named_derived_unit<foot, "ft", length, ratio_multiply<ratio<1, 3>, yard::ratio>> {};
struct inch : named_derived_unit<inch, "in", length, ratio_multiply<ratio<1, 12>, foot::ratio>> {};
struct mile : named_derived_unit<mile, "mi", length, ratio_multiply<ratio<1'760>, yard::ratio>> {};
struct yard : named_scaled_derived_unit<yard, length, "yd", ratio<9'144, 10'000>> {};
struct foot : named_scaled_derived_unit<foot, length, "ft", ratio_multiply<ratio<1, 3>, yard::ratio>> {};
struct inch : named_scaled_derived_unit<inch, length, "in", ratio_multiply<ratio<1, 12>, foot::ratio>> {};
struct mile : named_scaled_derived_unit<mile, length, "mi", ratio_multiply<ratio<1'760>, yard::ratio>> {};
inline namespace literals {

View File

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

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T>
concept Mass = QuantityOf<T, mass>;
struct gram : named_derived_unit<gram, "g", mass, ratio<1, 1000>> {};
struct gram : named_scaled_derived_unit<gram, mass, "g", ratio<1, 1000>, si_prefix> {};
struct kilogram : prefixed_derived_unit<kilogram, kilo, gram> {};
inline namespace literals {

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T>
concept Power = QuantityOf<T, power>;
struct watt : named_coherent_derived_unit<watt, "W", power, si_prefix> {};
struct watt : named_coherent_derived_unit<watt, power, "W", si_prefix> {};
struct milliwatt : prefixed_derived_unit<milliwatt, milli, watt> {};
struct kilowatt : prefixed_derived_unit<kilowatt, kilo, watt> {};
struct megawatt : prefixed_derived_unit<megawatt, mega, watt> {};

View File

@@ -33,7 +33,7 @@ namespace units {
template<typename T>
concept Pressure = QuantityOf<T, pressure>;
struct pascal : named_coherent_derived_unit<pascal, "Pa", pressure, si_prefix> {};
struct pascal : named_coherent_derived_unit<pascal, pressure, "Pa", si_prefix> {};
inline namespace literals {

View File

@@ -28,7 +28,7 @@
namespace units {
// prefix tags
struct si_prefix;
struct si_prefix : prefix_type {};
// SI prefixes

View File

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

View File

@@ -32,7 +32,7 @@ namespace units {
template<typename T>
concept ThermodynamicTemperature = QuantityOf<T, temperature>;
struct kelvin : named_coherent_derived_unit<kelvin, "K", temperature> {};
struct kelvin : named_coherent_derived_unit<kelvin, temperature, "K", no_prefix> {};
inline namespace literals {

View File

@@ -33,12 +33,12 @@ namespace units {
template<typename T>
concept Time = QuantityOf<T, time>;
struct second : named_coherent_derived_unit<second, "s", time, si_prefix> {};
struct second : named_coherent_derived_unit<second, time, "s", si_prefix> {};
struct nanosecond : prefixed_derived_unit<nanosecond, nano, second> {};
struct microsecond : prefixed_derived_unit<microsecond, micro, second> {};
struct millisecond : prefixed_derived_unit<millisecond, milli, second> {};
struct minute : named_derived_unit<minute, "min", time, ratio<60>> {};
struct hour : named_derived_unit<hour, "h", time, ratio<3600>> {};
struct minute : named_scaled_derived_unit<minute, time, "min", ratio<60>> {};
struct hour : named_scaled_derived_unit<hour, time, "h", ratio<3600>> {};
inline namespace literals {

View File

@@ -35,7 +35,7 @@ namespace units {
template<typename T>
concept Voltage = QuantityOf<T, voltage>;
struct volt : named_coherent_derived_unit<volt, "V", voltage, si_prefix> {};
struct volt : named_coherent_derived_unit<volt, voltage, "V", si_prefix> {};
inline namespace literals {

View File

@@ -28,18 +28,23 @@
namespace units {
struct prefix_type {};
template<typename T>
concept PrefixType = std::derived_from<T, prefix_type>;
namespace detail {
template<typename PrefixType, Ratio R>
struct prefix_base : downcast_base<prefix_base<PrefixType, R>> {
using prefix_type = PrefixType;
template<PrefixType PT, Ratio R>
struct prefix_base : downcast_base<prefix_base<PT, R>> {
using prefix_type = PT;
using ratio = R;
};
}
template<typename Child, typename PrefixType, Ratio R, basic_fixed_string Symbol>
struct prefix : downcast_child<Child, detail::prefix_base<PrefixType, R>> {
template<typename Child, PrefixType PT, Ratio R, basic_fixed_string Symbol>
struct prefix : downcast_child<Child, detail::prefix_base<PT, R>> {
static constexpr auto symbol = Symbol;
};
@@ -60,6 +65,6 @@ namespace units {
// concept Prefix = detail::is_prefix<T>;
concept Prefix = true;
struct no_prefix;
struct no_prefix : prefix_type {};
} // namespace units

View File

@@ -273,38 +273,41 @@ namespace units {
// derived_unit
template<typename Child, basic_fixed_string Symbol, Dimension D, typename PrefixType = no_prefix>
struct named_coherent_derived_unit : downcast_child<Child, unit<D, ratio<1>>> {
template<typename Child, Dimension Dim, basic_fixed_string Symbol, PrefixType PT>
struct named_coherent_derived_unit : downcast_child<Child, unit<Dim, ratio<1>>> {
static constexpr auto symbol = Symbol;
using prefix_type = PrefixType;
using prefix_type = PT;
};
template<typename Child, Dimension D, typename PrefixType = no_prefix>
struct coherent_derived_unit : downcast_child<Child, unit<D, ratio<1>>> {
static constexpr auto symbol = detail::symbol_text(D());
using prefix_type = PrefixType;
template<typename Child, Dimension Dim>
struct coherent_derived_unit : downcast_child<Child, unit<Dim, ratio<1>>> {
static constexpr auto symbol = detail::symbol_text(Dim());
using prefix_type = no_prefix;
};
template<typename Child, basic_fixed_string Symbol, Dimension D, Ratio R>
struct named_derived_unit : downcast_child<Child, unit<D, R>> {
template<typename Child, Dimension Dim, basic_fixed_string Symbol, Ratio R, PrefixType PT = no_prefix>
struct named_scaled_derived_unit : downcast_child<Child, unit<Dim, R>> {
static constexpr auto symbol = Symbol;
using prefix_type = PT;
};
template<typename Child, Dimension Dim, basic_fixed_string Symbol, PrefixType PT, Unit U, Unit... Us>
struct named_deduced_derived_unit : downcast_child<Child, detail::make_derived_unit<Dim, U, Us...>> {
static constexpr auto symbol = Symbol;
using prefix_type = PT;
};
template<typename Child, Dimension Dim, Unit U, Unit... Us>
struct deduced_derived_unit : downcast_child<Child, detail::make_derived_unit<Dim, U, Us...>> {
static constexpr auto symbol = basic_fixed_string("bbb");
using prefix_type = no_prefix;
};
template<typename Child, Prefix P, Unit U>
requires requires { U::symbol; }
requires (!std::same_as<typename U::prefix_type, no_prefix>)
struct prefixed_derived_unit : downcast_child<Child, unit<typename U::dimension, ratio_multiply<typename P::ratio, typename U::ratio>>> {
static constexpr auto symbol = P::symbol + U::symbol;
using prefix_type = P::prefix_type;
};
template<typename Child, basic_fixed_string Symbol, Dimension D, Unit U, Unit... Us>
struct named_deduced_derived_unit : downcast_child<Child, detail::make_derived_unit<D, U, Us...>> {
static constexpr auto symbol = Symbol;
};
template<typename Child, Dimension D, Unit U, Unit... Us>
struct deduced_derived_unit : downcast_child<Child, detail::make_derived_unit<D, U, Us...>> {
static constexpr auto symbol = basic_fixed_string("bbb");
};
} // namespace units

View File

@@ -33,14 +33,14 @@ namespace data {
template<typename T>
concept DigitalInformation = units::QuantityOf<T, digital_information>;
struct data_prefix;
struct data_prefix : units::prefix_type {};
struct kibi : units::prefix<kibi, data_prefix, units::ratio< 1'024>, "Ki"> {};
struct mebi : units::prefix<mebi, data_prefix, units::ratio<1'048'576>, "Mi"> {};
struct bit : units::named_coherent_derived_unit<bit, "b", digital_information, data_prefix> {};
struct bit : units::named_coherent_derived_unit<bit, digital_information, "b", data_prefix> {};
struct kilobit : units::prefixed_derived_unit<kilobit, kibi, bit> {};
struct byte : units::named_derived_unit<byte, "B", digital_information, units::ratio<8>> {};
struct byte : units::named_scaled_derived_unit<byte, digital_information, "B", units::ratio<8>, data_prefix> {};
struct kilobyte : units::prefixed_derived_unit<kilobyte, kibi, byte> {};
inline namespace literals {

View File

@@ -31,11 +31,11 @@ namespace cgs {
using units::gram;
using units::second;
struct centimetre_per_second : units::deduced_derived_unit<centimetre_per_second, units::velocity, centimetre, second> {};
struct gal : units::named_deduced_derived_unit<gal, "Gal", units::acceleration, centimetre, second> {};
struct dyne : units::named_deduced_derived_unit<dyne, "dyn", units::force, centimetre, gram, second> {};
struct erg : units::named_deduced_derived_unit<erg, "erg", units::energy, centimetre, gram, second> {};
struct ergps : units::named_deduced_derived_unit<ergps, "erg/s", units::power, centimetre, gram, second> {}; // TODO make it work for erg and non-named
struct barye : units::named_deduced_derived_unit<barye, "Ba", units::pressure, centimetre, gram, second> {};
struct gal : units::named_deduced_derived_unit<gal, units::acceleration, "Gal", units::no_prefix, centimetre, second> {};
struct dyne : units::named_deduced_derived_unit<dyne, units::force, "dyn", units::no_prefix, centimetre, gram, second> {};
struct erg : units::named_deduced_derived_unit<erg, units::energy, "erg", units::no_prefix, centimetre, gram, second> {};
struct ergps : units::named_deduced_derived_unit<ergps, units::power, "erg/s", units::no_prefix, centimetre, gram, second> {}; // TODO make it work for erg and non-named
struct barye : units::named_deduced_derived_unit<barye, units::pressure, "Ba", units::no_prefix, centimetre, gram, second> {};
inline namespace literals {

View File

@@ -35,14 +35,14 @@ namespace {
template<typename T>
concept DigitalInformation = units::QuantityOf<T, digital_information>;
struct data_prefix {};
struct data_prefix : units::prefix_type {};
struct kibi : units::prefix<kibi, data_prefix, units::ratio< 1'024>, "Ki"> {};
struct bit : units::named_coherent_derived_unit<bit, "b", digital_information, data_prefix> {};
struct bit : units::named_coherent_derived_unit<bit, digital_information, "b", data_prefix> {};
struct kilobit : units::prefixed_derived_unit<kilobit, kibi, bit> {};
struct byte : units::named_derived_unit<byte, "B", digital_information, units::ratio<8>> {};
struct byte : units::named_scaled_derived_unit<byte, digital_information, "B", units::ratio<8>, data_prefix> {};
struct kilobyte : units::prefixed_derived_unit<kilobyte, kibi, byte> {};
inline namespace literals {