Skip to content

Commit

Permalink
feat: quantities can now be multiplied and divided by units
Browse files Browse the repository at this point in the history
  • Loading branch information
mpusz committed Sep 30, 2023
1 parent 476a68c commit b2423bf
Show file tree
Hide file tree
Showing 25 changed files with 79 additions and 118 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
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
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 a = isq::acceleration(measurement{9.8, 0.1} * (m / s2));
const auto a = isq::acceleration(measurement{9.8, 0.1} * m / s2);
const auto t = measurement{1.2, 0.1} * s;

const QuantityOf<isq::velocity> auto v = a * t;
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
16 changes: 15 additions & 1 deletion src/core/include/mp-units/reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,21 @@ template<typename Rep, Reference R>
return make_quantity<R{}>(std::forward<Rep>(lhs));
}

void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete;
template<typename Q, Reference R>
requires Quantity<std::remove_cvref_t<Q>>
[[nodiscard]] constexpr quantity<std::remove_cvref_t<Q>::reference * R{}, typename std::remove_cvref_t<Q>::rep>
operator*(Q&& q, R)
{
return make_quantity<std::remove_cvref_t<Q>::reference * R{}>(std::forward<Q>(q).numerical_value_);
}

template<typename Q, Reference R>
requires Quantity<std::remove_cvref_t<Q>>
[[nodiscard]] constexpr quantity<std::remove_cvref_t<Q>::reference / R{}, typename std::remove_cvref_t<Q>::rep>
operator/(Q&& q, R)
{
return make_quantity<std::remove_cvref_t<Q>::reference / R{}>(std::forward<Q>(q).numerical_value_);
}

[[nodiscard]] consteval AssociatedUnit auto common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2,
AssociatedUnit auto... rest)
Expand Down
Loading

0 comments on commit b2423bf

Please sign in to comment.