-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlab_units.hpp
More file actions
121 lines (108 loc) · 4.52 KB
/
Copy pathlab_units.hpp
File metadata and controls
121 lines (108 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// SPDX-License-Identifier: Apache-2.0
#pragma once
/// @file
/// The demo's unit system: a plain enum, its `UnitTraits` metadata, and the
/// consteval algebra that lets `Quantity` deduce result units at compile
/// time. This is the application-side half of `morph/quantity.hpp` — morph
/// itself ships no units.
///
/// Naming convention: composition is explicit in the enumerator (`kg_per_m3`,
/// never `kgm3`). The `id` strings become schema/wire vocabulary: append new
/// enumerators, never renumber or rename existing ones.
#include <array>
#include <cstdint>
#include <morph/util/quantity.hpp>
#include <span>
namespace lab {
/// @brief Units the demo lab works in.
enum class Unit : std::uint16_t {
scalar = 0, ///< dimensionless ratio
percent, ///< dimensionless, scaled by 100 for display
kg, ///< mass
m3, ///< volume
kg_per_m3, ///< density
g, ///< mass entry alternative (1 g = 1/1000 kg)
t, ///< mass entry alternative (1 t = 1000 kg)
l, ///< volume entry alternative (1 L = 1/1000 m³)
};
} // namespace lab
/// @brief Static unit metadata: schema id, display text, default decimals.
template <>
struct morph::units::UnitTraits<lab::Unit> {
static constexpr morph::units::UnitMeta meta(lab::Unit unit) noexcept {
switch (unit) {
case lab::Unit::scalar:
return {"scalar", "", 3};
case lab::Unit::percent:
return {"percent", "%", 1};
case lab::Unit::kg:
return {"kg", "kg", 3};
case lab::Unit::m3:
return {"m3", "m³", 3};
case lab::Unit::kg_per_m3:
return {"kg_per_m3", "kg/m³", 1};
case lab::Unit::g:
return {"g", "g", 1};
case lab::Unit::t:
return {"t", "t", 4};
case lab::Unit::l:
return {"l", "L", 1};
default:
return {"?", "?", 3};
}
}
/// @brief Within-dimension conversion ratios (mass g/t ↔ kg, volume L ↔ m³).
/// These drive `convert`, chaining, and the display-unit selector.
static constexpr std::array<morph::units::UnitRelation<lab::Unit>, 3> relations{{
{lab::Unit::g, lab::Unit::kg,
morph::math::Rational{morph::math::Numerator{1}, morph::math::Denominator{1000},
morph::math::DecimalPlaces{3}}},
{lab::Unit::t, lab::Unit::kg,
morph::math::Rational{morph::math::Numerator{1000}, morph::math::Denominator{1},
morph::math::DecimalPlaces{3}}},
{lab::Unit::l, lab::Unit::m3,
morph::math::Rational{morph::math::Numerator{1}, morph::math::Denominator{1000},
morph::math::DecimalPlaces{3}}},
}};
};
namespace lab {
/// @brief Unit product table. A combination without an entry is a
/// compile-time error at the call site that attempted it: both
/// operators are `consteval`, so the `throw` below can only be
/// reached while evaluating a constant expression, which C++ forbids
/// -- it never executes as a runtime exception.
consteval Unit operator*(Unit lhs, Unit rhs) {
if (lhs == Unit::scalar) {
return rhs;
}
if (rhs == Unit::scalar) {
return lhs;
}
if ((lhs == Unit::kg_per_m3 && rhs == Unit::m3) || (lhs == Unit::m3 && rhs == Unit::kg_per_m3)) {
return Unit::kg;
}
throw "lab::Unit: unsupported unit product"; // compile error at the call site (consteval) -- see @brief above
}
/// @brief Unit quotient table. Same compile-time-only `throw` as `operator*`
/// above (this function is `consteval` too).
consteval Unit operator/(Unit lhs, Unit rhs) {
if (rhs == Unit::scalar) {
return lhs;
}
if (lhs == rhs) {
return Unit::scalar;
}
if (lhs == Unit::kg && rhs == Unit::m3) {
return Unit::kg_per_m3;
}
throw "lab::Unit: unsupported unit quotient"; // compile error at the call site (consteval) -- see @brief above
}
/// @brief Shorthand for quantities in this unit system. The second argument
/// overrides the field's declared precision (defaults from `UnitTraits`).
template <Unit U, std::uint32_t Decimals = morph::units::UnitTraits<Unit>::meta(U).defaultDecimals>
using Quantity = morph::units::Quantity<U, Decimals>;
using Mass = Quantity<Unit::kg>;
using Volume = Quantity<Unit::m3>;
using Density = Quantity<Unit::kg_per_m3>;
using Percent = Quantity<Unit::percent>;
} // namespace lab