Skip to content

Commit

Permalink
Merge pull request #496 from mpusz/unit_compose_ext
Browse files Browse the repository at this point in the history
feat: quantities can now be multiplied and divided by units
  • Loading branch information
mpusz authored Oct 6, 2023
2 parents fa5419b + e23f038 commit 84df87d
Show file tree
Hide file tree
Showing 50 changed files with 351 additions and 351 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,9 @@ static_assert(1 * h == 3600 * s);
static_assert(1 * km + 1 * m == 1001 * m);

// derived quantities
inline constexpr auto kmph = km / h;
static_assert(1 * km / (1 * s) == 1000 * (m / s));
static_assert(2 * kmph * (2 * h) == 4 * km);
static_assert(2 * km / (2 * kmph) == 1 * h);
static_assert(1 * km / (1 * s) == 1000 * m / s);
static_assert(2 * km / h * (2 * h) == 4 * km);
static_assert(2 * km / (2 * km / h) == 1 * h);

static_assert(2 * m * (3 * m) == 6 * m2);

Expand Down Expand Up @@ -103,7 +102,7 @@ int main()
using namespace mp_units::si::unit_symbols;
using namespace mp_units::international::unit_symbols;
constexpr quantity v1 = 110 * (km / h);
constexpr quantity v1 = 110 * km / h;
constexpr quantity v2 = 70 * mph;
constexpr quantity v3 = avg_speed(220. * isq::distance[km], 2 * h);
constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * h);
Expand Down
9 changes: 4 additions & 5 deletions docs/getting_started/code_example.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ static_assert(1 * h == 3600 * s);
static_assert(1 * km + 1 * m == 1001 * m);

// derived quantities
inline constexpr auto kmph = km / h;
static_assert(1 * km / (1 * s) == 1000 * (m / s));
static_assert(2 * kmph * (2 * h) == 4 * km);
static_assert(2 * km / (2 * kmph) == 1 * h);
static_assert(1 * km / (1 * s) == 1000 * m / s);
static_assert(2 * km / h * (2 * h) == 4 * km);
static_assert(2 * km / (2 * km / h) == 1 * h);

static_assert(2 * m * (3 * m) == 6 * m2);

Expand Down Expand Up @@ -59,7 +58,7 @@ int main()
using namespace mp_units::si::unit_symbols;
using namespace mp_units::international::unit_symbols;
constexpr quantity v1 = 110 * (km / h);
constexpr quantity v1 = 110 * km / h;
constexpr quantity v2 = 70 * mph;
constexpr quantity v3 = avg_speed(220. * isq::distance[km], 2 * h);
constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * h);
Expand Down
41 changes: 0 additions & 41 deletions docs/getting_started/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,47 +92,6 @@ In the **mp-units** library, both a number and a unit have to always be explicit
form a quantity.
## Why `60 * km / h` does not compile?
The library design does not allow multiplying or dividing a quantity (the result of `60 * km`)
by another unit. This significantly limits the number of possible errors and surprises in the
quantity equations.
Consider the following expression:
```cpp
auto q = 60 * km / 2 * h;
```

Looks like `30 km/h`, right? But it is not. If the above code was allowed, it would result
in `30 km⋅h`. In case you want to divide `60 * km` by `2 * h` a parenthesis is needed:

```cpp
auto q = 60 * km / (2 * h);
```

Another surprising issue could result from the following code:

```cpp
template<typename T>
auto make_length(T v) { return v * si::metre; }

auto v = 42;
auto q = make_length(v);
```
The above might look like a good idea, but consider what would happen in the user provided
an already existing quantity:
```cpp
auto v = 42 * m;
auto q = make_length(v);
```

Fortunately, with the current design, such issues are detected at compile-time as
multiplying or dividing a quantity by a unit is not be supported.


## Why a dimensionless quantity is not just a fundamental arithmetic type?
In the initial design of this library, the resulting type of division of two quantities was their
Expand Down
7 changes: 1 addition & 6 deletions docs/getting_started/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,9 @@ quantity q = make_quantity<si::metre>(42);
Sometimes it might be awkward to type some derived units:

```cpp
quantity speed = 60 * (km / h);
quantity speed = 60 * km / h;
```

!!! note

Please note that `60 * km / h` will not compile. To read more about the rationale for such
a design please check our [FAQ](faq.md#why-dont-we-use-udls-to-create-a-quantity).

In case such a unit is used a lot in the project, a user can easily provide a nicely named
wrapper for it with:

Expand Down
6 changes: 3 additions & 3 deletions docs/users_guide/framework_basics/character_of_a_quantity.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ either:
The following does not work:

```cpp
Quantity auto q1 = la_vector{1, 2, 3} * (m / s);
Quantity auto q2 = isq::velocity(la_vector{1, 2, 3} * (m / s));
quantity<isq::velocity[m/s]> q3{la_vector{1, 2, 3} * (m / s)};
Quantity auto q1 = la_vector{1, 2, 3} * m / s;
Quantity auto q2 = isq::velocity(la_vector{1, 2, 3} * m / s);
quantity<isq::velocity[m/s]> q3{la_vector{1, 2, 3} * m / s};
```

In all the cases above, the SI unit `m / s` has an associated scalar quantity of `isq::length / isq::time`.
Expand Down
16 changes: 9 additions & 7 deletions docs/users_guide/framework_basics/interface_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,22 @@ identities used in the library:
| `QuantitySpec` | `dimensionless` |
| `Unit` | `one` |

In the equations, a user can refer to an identity object either explicitly:
In the equations, a user can explicitly refer to an identity object:

```cpp
constexpr auto my_unit = one / second;
```

or implicitly:
!!! note

```cpp
constexpr auto my_unit = 1 / second;
```
Another way to achieve the same result is to call an `inverse()` function:

```cpp
constexpr auto my_unit = inverse(second);
```

Both cases with result in the same expression template being generated and put into the wrapper
class template.
Both cases will result in the same expression template being generated and put into the wrapper
class template.


### Supported operations and their results
Expand Down
4 changes: 2 additions & 2 deletions docs/users_guide/framework_basics/quantity_arithmetics.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ However, suppose we multiply or divide quantities of the same or different types
number by a quantity. In that case, we most probably will end up in a quantity of yet another type:

```cpp
static_assert(120 * km / (2 * h) == 60 * (km / h));
static_assert(120 * km / (2 * h) == 60 * km / h);
static_assert(isq::width(2 * m) * isq::length(2 * m) == isq::area(4 * m2));
static_assert(50 / isq::time(1 * s) == isq::frequency(50 * Hz));
```
Expand Down Expand Up @@ -283,7 +283,7 @@ every time when we want to ensure that we deal with a non-zero or positive value
We could implement such checks in the following way:
```cpp
if (q1 / q2 != 0 * (m / s))
if (q1 / q2 != 0 * m / s)
// ...
```

Expand Down
4 changes: 2 additions & 2 deletions docs/users_guide/framework_basics/systems_of_units.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ However, it also explicitly states:
The library allows constraining such units in the following way:

```cpp
inline constexpr struct hertz : named_unit<"Hz", 1 / second, kind_of<isq::frequency>> {} hertz;
inline constexpr struct becquerel : named_unit<"Bq", 1 / second, kind_of<isq::activity>> {} becquerel;
inline constexpr struct hertz : named_unit<"Hz", one / second, kind_of<isq::frequency>> {} hertz;
inline constexpr struct becquerel : named_unit<"Bq", one / second, kind_of<isq::activity>> {} becquerel;
```
With the above, `hertz` can only be used for frequencies while becquerel should only be used for
Expand Down
16 changes: 8 additions & 8 deletions docs/users_guide/framework_basics/text_output.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,12 @@ in the denominator), or never in which case a parenthesis will be added to enclo
units.
```cpp
std::println("{:%Q %q}", 1 * (m / s)); // 1 m/s
std::println("{:%Q %q}", 1 * (kg / m / s2)); // 1 kg m⁻¹ s⁻²
std::println("{:%Q %aq}", 1 * (m / s)); // 1 m/s
std::println("{:%Q %aq}", 1 * (kg / m / s2)); // 1 kg/(m s²)
std::println("{:%Q %nq}", 1 * (m / s)); // 1 m s⁻¹
std::println("{:%Q %nq}", 1 * (kg / m / s2)); // 1 kg m⁻¹ s⁻²
std::println("{:%Q %q}", 1 * m / s); // 1 m/s
std::println("{:%Q %q}", 1 * kg / m / s2); // 1 kg m⁻¹ s⁻²
std::println("{:%Q %aq}", 1 * m / s); // 1 m/s
std::println("{:%Q %aq}", 1 * kg / m / s2); // 1 kg/(m s²)
std::println("{:%Q %nq}", 1 * m / s); // 1 m s⁻¹
std::println("{:%Q %nq}", 1 * kg / m / s2); // 1 kg m⁻¹ s⁻²
```

Also, there are a few options to separate the units being multiplied:
Expand All @@ -319,6 +319,6 @@ to just use the `·` symbol as a separator.
The `units-unit-symbol-separator` token allows us to obtain the following outputs:

```cpp
std::println("{:%Q %q}", 1 * (kg * m2 / s2)); // 1 kg m²/s²
std::println("{:%Q %dq}", 1 * (kg * m2 / s2)); // 1 kg⋅m²/s²
std::println("{:%Q %q}", 1 * kg * m2 / s2); // 1 kg m²/s²
std::println("{:%Q %dq}", 1 * kg * m2 / s2); // 1 kg⋅m²/s²
```
8 changes: 4 additions & 4 deletions example/foot_pound_second.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ int main()
auto bismark = Ship{.length{251. * m},
.draft{9.3 * m},
.beam{36 * m},
.speed{56 * (km / h)},
.speed{56 * km / h},
.mass{50'300 * t},
.mainGuns{380 * mm},
.shellMass{800 * kg},
.shellSpeed{820. * (m / s)},
.shellSpeed{820. * m / s},
.power{110.45 * kW}};

// USS Iowa, using units from the foot-pound-second system
Expand All @@ -97,7 +97,7 @@ int main()
.mass{57'540 * imperial::long_ton},
.mainGuns{16 * in},
.shellMass{2700 * lb},
.shellSpeed{2690. * (ft / s)},
.shellSpeed{2690. * ft / s},
.power{212'000 * hp}};

// HMS King George V, using units from the foot-pound-second system
Expand All @@ -108,7 +108,7 @@ int main()
.mass{42'245 * imperial::long_ton},
.mainGuns{14 * in},
.shellMass{1590 * lb},
.shellSpeed{2483. * (ft / s)},
.shellSpeed{2483. * ft / s},
.power{110'000 * hp}};

print_details("KMS Bismark, defined in appropriate units from the SI system", bismark);
Expand Down
16 changes: 8 additions & 8 deletions example/glide_computer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@ auto get_gliders()
using namespace mp_units::si::unit_symbols;
MP_UNITS_DIAGNOSTIC_PUSH
MP_UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES
static const std::array gliders = {glider{"SZD-30 Pirat", {83 * (km / h), -0.7389 * (m / s)}},
glider{"SZD-51 Junior", {80 * (km / h), -0.6349 * (m / s)}},
glider{"SZD-48 Jantar Std 3", {110 * (km / h), -0.77355 * (m / s)}},
glider{"SZD-56 Diana", {110 * (km / h), -0.63657 * (m / s)}}};
static const std::array gliders = {glider{"SZD-30 Pirat", {83 * km / h, -0.7389 * m / s}},
glider{"SZD-51 Junior", {80 * km / h, -0.6349 * m / s}},
glider{"SZD-48 Jantar Std 3", {110 * km / h, -0.77355 * m / s}},
glider{"SZD-56 Diana", {110 * km / h, -0.63657 * m / s}}};
MP_UNITS_DIAGNOSTIC_POP
return gliders;
}

auto get_weather_conditions()
{
using namespace mp_units::si::unit_symbols;
static const std::array weather_conditions = {std::pair{"Good", weather{1900 * m, 4.3 * (m / s)}},
std::pair{"Medium", weather{1550 * m, 2.8 * (m / s)}},
std::pair{"Bad", weather{850 * m, 1.8 * (m / s)}}};
static const std::array weather_conditions = {std::pair{"Good", weather{1900 * m, 4.3 * m / s}},
std::pair{"Medium", weather{1550 * m, 2.8 * m / s}},
std::pair{"Bad", weather{850 * m, 1.8 * m / s}}};
return weather_conditions;
}

Expand Down Expand Up @@ -164,7 +164,7 @@ void example()
const auto waypoints = get_waypoints();
const auto weather_conditions = get_weather_conditions();
const task t = {waypoints[0], waypoints[1], waypoints[0]};
const aircraft_tow tow = {400 * m, 1.6 * (m / s)};
const aircraft_tow tow = {400 * m, 1.6 * m / s};
// TODO use C++20 date library when available
// set `start_time` to 11:00 am today
const timestamp start_time(std::chrono::system_clock::now());
Expand Down
2 changes: 1 addition & 1 deletion example/hello_units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ int main()
using namespace mp_units::si::unit_symbols;
using namespace mp_units::international::unit_symbols;

constexpr quantity v1 = 110 * (km / h);
constexpr quantity v1 = 110 * km / h;
constexpr quantity v2 = 70 * mph;
constexpr quantity v3 = avg_speed(220. * km, 2 * h);
constexpr quantity v4 = avg_speed(isq::distance(140. * mi), 2 * isq::duration[h]);
Expand Down
2 changes: 1 addition & 1 deletion example/kalman_filter/kalman_filter-example_2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ int main()
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;

const auto interval = isq::duration(5 * s);
const state initial = {30 * km, 40 * (m / s)};
const state initial = {30 * km, 40 * m / s};
const quantity<isq::position_vector[m], int> measurements[] = {30'110 * m, 30'265 * m, 30'740 * m, 30'750 * m,
31'135 * m, 31'015 * m, 31'180 * m, 31'610 * m,
31'960 * m, 31'865 * m};
Expand Down
2 changes: 1 addition & 1 deletion example/kalman_filter/kalman_filter-example_3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ int main()
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;

const auto interval = isq::duration(5 * s);
const state initial = {30 * km, 50 * (m / s)};
const state initial = {30 * km, 50 * m / s};
const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
36'010 * m, 37'265 * m};
Expand Down
2 changes: 1 addition & 1 deletion example/kalman_filter/kalman_filter-example_4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ int main()
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>,
quantity<isq::acceleration[m / s2]>>;
const auto interval = isq::duration(5. * s);
const state initial = {30 * km, 50 * (m / s), 0 * (m / s2)};
const state initial = {30 * km, 50 * m / s, 0 * m / s2};

const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
Expand Down
2 changes: 1 addition & 1 deletion example/measurement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ void example()
using namespace mp_units;
using namespace mp_units::si::unit_symbols;

const auto acceleration = isq::acceleration(measurement{9.8, 0.1} * (m / s2));
const auto acceleration = isq::acceleration(measurement{9.8, 0.1} * m / s2);
const auto time = measurement{1.2, 0.1} * s;

const QuantityOf<isq::velocity> auto velocity = acceleration * time;
Expand Down
3 changes: 2 additions & 1 deletion example/si_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ inline constexpr bool mp_units::is_vector<T> = true;

int main()
{
using namespace mp_units;
using namespace mp_units::si;
using namespace mp_units::si::unit_symbols;

Expand All @@ -52,7 +53,7 @@ int main()
std::cout << MP_UNITS_STD_FMT::format("- Boltzmann constant: {} = {:%.6eQ %q}\n",
1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant).in(J / K));
std::cout << MP_UNITS_STD_FMT::format("- Avogadro constant: {} = {:%.8eQ %q}\n",
1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant).in(1 / mol));
1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant).in(one / mol));
std::cout << MP_UNITS_STD_FMT::format("- luminous efficacy: {} = {}\n",
1. * si2019::luminous_efficacy, (1. * si2019::luminous_efficacy).in(lm / W));
}
4 changes: 2 additions & 2 deletions example/spectroscopy_units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ template<QuantityOf<isq::energy> T1, QuantityOf<isq::wavenumber> T2, QuantityOf<
void print_line_si(const std::tuple<T1, T2, T3, T4, T5>& t)
{
MP_UNITS_STD_FMT::println("| {:<15} | {:<15} | {:<15} | {:<15} | {:<15} |", std::get<0>(t).in(eV),
std::get<1>(t).in(1 / cm), std::get<2>(t).in(THz), std::get<3>(t).in(K),
std::get<1>(t).in(one / cm), std::get<2>(t).in(THz), std::get<3>(t).in(K),
std::get<4>(t).in(um));
}

Expand All @@ -71,7 +71,7 @@ int main()
const auto t1 = std::make_tuple(q1, isq::wavenumber(q1 / (h * c)), isq::frequency(q1 / h),
isq::thermodynamic_temperature(q1 / kb), isq::wavelength(h * c / q1));

const auto q2 = 1. * isq::wavenumber[1 / cm];
const auto q2 = 1. * isq::wavenumber[one / cm];
const auto t2 = std::make_tuple(isq::energy(q2 * h * c), q2, isq::frequency(q2 * c),
isq::thermodynamic_temperature(q2 * h * c / kb), isq::wavelength(1 / q2));

Expand Down
4 changes: 2 additions & 2 deletions example/storage_tank.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ QUANTITY_SPEC(horizontal_length, isq::length);
QUANTITY_SPEC(horizontal_area, isq::area, horizontal_length* isq::width);

inline constexpr auto g = 1 * si::standard_gravity;
inline constexpr auto air_density = isq::mass_density(1.225 * (kg / m3));
inline constexpr auto air_density = isq::mass_density(1.225 * kg / m3);

class StorageTank {
quantity<horizontal_area[m2]> base_;
Expand Down Expand Up @@ -114,7 +114,7 @@ int main()
{
const quantity height = isq::height(200 * mm);
auto tank = RectangularStorageTank(horizontal_length(1'000 * mm), isq::width(500 * mm), height);
tank.set_contents_density(1'000 * isq::mass_density[kg / m3]);
tank.set_contents_density(1'000 * kg / m3);

const auto duration = std::chrono::seconds{200};
const quantity fill_time = value_cast<int>(quantity{duration}); // time since starting fill
Expand Down
Loading

0 comments on commit 84df87d

Please sign in to comment.