Examples
Compile-time unit inference
See xo-unit/examples/ex1
for code below.
Units propagate through familiar arithmetic expressions:
1 #include "xo/unit/quantity.hpp"
2 #include "xo/unit/quantity_iostream.hpp"
3 #include <iostream>
4
5 int
6 main () {
7 namespace q = xo::qty::qty;
8 namespace su = xo::qty::su;
9 using namespace std;
10
11 constexpr auto t = q::minutes(2);
12 constexpr auto d = q::kilometers(2.5);
13
14 constexpr auto t2 = t*t;
15 constexpr auto a = d / (t*t);
16
17 cerr << "t: " << t << ", d: " << d
18 << ", t^2: " << t2
19 << ", d.t^-2: " << a
20 << endl;
21 }
with output:
t: 2min, d: 2.5km, t^2: 4min^2, d.t^-2: 0.625km.min^-2
We can use static asserts to prove that units are being computed at compile-time
1 static_assert(std::same_as<decltype(t)::repr_type, int>);
2 static_assert(sizeof(t) == sizeof(double));
3 static_assert(t.scale() == 2);
4 static_assert(t.abbrev() == flatstring("min"));
5
6 static_assert(std::same_as<decltype(d)::repr_type, double>);
7 static_assert(sizeof(d) == sizeof(double));
8 static_assert(d.scale() == 2.5);
9 static_assert(d.abbrev() == flatstring("km"));
10
11 static_assert(std::same_as<decltype(t2)::repr_type, int>);
12 static_assert(sizeof(t2) == sizeof(double));
13 static_assert(t2.scale() == 4);
14 static_assert(t2.abbrev() == flatstring("min^2"));
15
16 static_assert(std::same_as<decltype(a)::repr_type, double>);
17 static_assert(sizeof(a) == sizeof(double));
18 static_assert(a.scale() == 0.625);
19 static_assert(a.abbrev() == flatstring("km.min^-2"));
Remarks:
xo::qty::quantity
performs unit consistency checking and propagation at compile time. Runtime space/time overhead is zero.Units are sticky: since we expressed
t
in minutes,(t*t)
andd/(t*t)
also use minutes.Unit ordering is sticky. Distance appears on the left of time in printed value of
d/(t*t)
because it was on the left-hand side ofoperator/
xo-unit
copies representation from the argument to factory functionsq::minutes
,q::kilometers
etc.Binary operators take representation from the ‘most precise’ argument, as prescribed by
std::common_type_t
.Unit abbreviations (such as
kg.min^-2
above) are computed at compile time. Seexo::flatstring
for constexpr string implementation.See
xo::xquantity
for parallel implementation that defers unit checking until runtime.
Explicit scale conversion
Can convert between compatible units explictly, using:
xo::qty::with_units
(template function)quantity.rescale_ext
(template method)xo::qty::with_units_from
(template function)
See xo-unit/examples/ex2
for code below.
1 #include "xo/unit/quantity.hpp"
2 #include "xo/unit/quantity_iostream.hpp"
3 #include <iostream>
4
5 int
6 main () {
7 namespace q = xo::qty::qty;
8 namespace u = xo::qty::u;
9 using xo::qty::with_units_from;
10 using xo::qty::with_units;
11 using xo::qty::quantity;
12 using xo::flatstring;
13 using namespace std;
14
15 constexpr auto t = q::minutes(2);
16 constexpr auto d = q::kilometers(2.5);
17
18 constexpr auto t2 = t*t;
19 constexpr auto a = d / (t*t);
20
21 cerr << "t: " << t << ", d: " << d
22 << ", t^2: " << t2
23 << ", d.t^-2: " << a
24 << endl;
25
26 constexpr auto a2 = with_units<u::meter / (u::second * u::second)>(a);
27
28 static_assert(a2.abbrev() == flatstring("m.s^-2"));
29
30 cerr << "a2: " << a2 << endl;
31
32 constexpr auto a3 = a.rescale_ext<u::meter / (u::second * u::second)>();
33
34 static_assert(a3.abbrev() == flatstring("m.s^-2"));
35
36 cerr << "a3: " << a3 << endl;
37
38 constexpr auto au = q::meter / (q::second * q::second);
39 constexpr auto a4 = with_units_from(a, au);
40
41 static_assert(a4.abbrev() == flatstring("m.s^-2"));
42
43 cerr << "a4: " << a4 << endl;
44 }
with output:
a2: 0.173611m.s^-2
a3: 0.173611m.s^-2
a4: 0.173611m.s^-2
Implicit Scale conversion
Another way to convert units is to assign to a variable with desired units – this works because the units are encoded as part of the assigned variable’s type.
See xo-unit/example/ex3
for code below
1 int
2 main () {
3 namespace q = xo::qty::qty;
4 namespace u = xo::qty::u;
5 using xo::qty::quantity;
6
7 constexpr quantity<u::second> t = q::minutes(2);
8 constexpr quantity<u::meter> d = q::kilometers(2.5);
9
10 constexpr auto t2 = t*t;
11 constexpr auto a = d / (t*t);
12
13 std::cerr << "t: " << t << ", d: " << d
14 << ", d.t^-2: " << a
15 << std::endl;
16 }
with output:
t: 120s, d: 2500m, d.t^-2: 0.17e611m.s^-2
Remarks:
Assignment to
t
converted to representationdouble
. We could have instead usedquantity<u::second, int>
to propagate right-hand-side representation
Scale conversion and arithmetic
When representing a particular quantity, xo-unit uses at most one scale for each basis dimension associated with the unit for that quantity. When an arithmetic operator encounters basis units involving two different scales, the operator will adopt the scale provided by the left-hand argument:
See xo-unit/example/ex4
for code below
1 #include "xo/unit/quantity.hpp"
2 #include <iostream>
3
4 int main() {
5 namespace q = xo::qty::qty;
6
7 auto t1 = qty::milliseconds(1);
8 auto t2 = qty::minutes(1);
9 auto p = t1 * t2;
10
11 std::cerr << "t1: " << t1 << ", t2: " << t2 << ", p: " << p << std::endl;
12 }
with output:
t1: 1ms, t2: 1min, t1*t2: 60000ms^2
Dimensionless quantities unwrap implicitly
Conversely, compiler rejects attempt to implictly unwrap a dimensioned quantity.
See xo-unit/examples/ex4
for code below.
1 #include "xo/unit/quantity.hpp"
2 #include "xo/unit/quantity_iostream.hpp"
3 #include <iostream>
4
5 int
6 main () {
7 namespace q = xo::qty::qty;
8
9 auto t1 = q::milliseconds(1);
10 auto t2 = q::minutes(1);
11
12 auto r1 = t1 / with_repr<double>(t2);
13
14 static_assert(r1.is_dimensionless());
15 static_assert(!t2.is_dimensionless());
16
17 static_assert(std::same_as<static_cast<double>(r1), double>);
18
19 // r1_value: assignment compiles, since r1 dimensionless
20 double r1_value = r1;
21
22 // r2_value: bad assignment won't compile, 'cannot convert' error
23 //double r2_value = t2;
24
25 std::cerr << "t1: " << t1 << ", t2: " << t2 << ", t1/t2: " << r1_value << std::endl;
26 }
with output:
t1: 1ms, t2: 1min, t1/t2: 1.66667e-05
Fractional dimension
Fractional dimensions have limited support. Prior to c++26 we can only support fractional dimensions with denominator 2, such as powers -3/2, -1/2, +1/2, +3/2 etc.
c++26 will enable support for support fractional dimensions involving other ratios,
by offering constexpr ::pow()
See xo-unit/examples/ex6
for code below
1 #include "xo/unit/quantity.hpp"
2 #include "xo/unit/quantity_iostream.hpp"
3 #include <iostream>
4
5 int
6 main () {
7 namespace u = xo::unit::units;
8 namespace q = xo::unit::qty;
9 using namespace std;
10
11 /* 20% volatility over 250 days (approx number of trading days in one year) */
12 auto q1 = q::volatility_250d(0.2);
13 /* 10% volatility over 30 days */
14 auto q2 = q::volatility_30d(0.1);
15
16 auto sum = q1 + q2;
17 auto prod = q1 * q2;
18
19 static_assert(sum.abbrev() == flatstring("yr360^(-1/2)"));
20 static_assert(prod.abbrev() == flatstring("yr360^-1"));
21
22 std::cerr << "q1: " << q1 << std::endl;
23 std::cerr << "q2: " << q2 << std::endl;
24 std::cerr << "q1+q2: " << sum << std::endl;
25 std::cerr << "q1*q2: " << prod << std::endl;
26 }
with output:
q1: 0.2yr360^(-1/2)
q2: 0.1mo^(-1/2)
q1+q2: 0.54641yr360^(-1/2)
q1*q2: 0.069282yr360^-1
Dynamic dimension
If the dimension (or units) associated with a quantity are not known at compile-time,
use xo::qty::xquantity
instead of xo::qty::quantity
.
See xo-unit/example/ex8
for code below
1 #include "xo/unit/xquantity.hpp"
2 #include "xo/unit/xquantity_iostream.hpp"
3 #include <iostream>
4
5 int
6 main () {
7 using namespace xo::qty;
8 namespace u = xo::qty::u;
9
10 xquantity qty1(7, u::foot);
11 xquantity qty2(6.0, u::inch);
12 xquantity qty3 = qty1 + qty2;
13
14 std::cerr << "qty1: " << qty1 << std::endl;
15 std::cerr << "qty2: " << qty2 << std::endl;
16 std::cerr << "qty3: " << qty3 << std::endl;
17
18 /* rescale to mm */
19 xquantity res = qty3.rescale(xo::qty::nu::millimeter);
20
21 /* 2286mm */
22 std::cerr << "res: " << res << std::endl;
23 }
Here u::foot
and u::inch
are literals,
but they could have been read from console input or another runtime-only context.