dimval 0.2.0
Modern C++23 header-only library of dimensional values (units, measures, ranges)
Loading...
Searching...
No Matches
unit.hpp
Go to the documentation of this file.
1#pragma once
2
9
10#include <dimval/core.hpp>
11#include <dimval/interface.hpp>
13#include <dimval/traits.hpp>
14
15#include <commons/color.hpp>
16#include <commons/icon.hpp>
17
18#include <expected>
19#include <memory>
20#include <optional>
21#include <string>
22#include <string_view>
23#include <type_traits>
24
25namespace dimval {
26
35template <UnitLike U, NumericValue T = double>
36struct UnitValue : public IUnitValue {
37 using unit_t = U;
38 using value_t = T;
39
40 T v{};
41
42 constexpr UnitValue() = default;
46 constexpr UnitValue(T value) noexcept : v(value) {} // NOLINT(google-explicit-constructor)
47
50 [[nodiscard]] static std::shared_ptr<UnitValue> of(T value) {
51 return std::make_shared<UnitValue>(value);
52 }
53
55 [[nodiscard]] static std::unique_ptr<UnitValue> unique(T value) {
56 return std::make_unique<UnitValue>(value);
57 }
58
62 [[nodiscard]] static std::expected<UnitValue, ParseError> parse(const std::string_view input) {
63 auto split = detail::split_number_and_tail(input);
64 if (!split)
65 return std::unexpected{split.error()};
66 auto num = detail::parse_number<T>(split->number, input);
67 if (!num)
68 return std::unexpected{num.error()};
69 constexpr auto d = U::descriptor();
70 const std::string_view tail = split->tail;
71 if (tail.empty() && d.symbol.empty())
72 return UnitValue{*num};
73 if (tail == d.id || tail == d.symbol || tail == d.short_name)
74 return UnitValue{*num};
75 return std::unexpected{ParseError{ParseErrorCode::UnitMismatch,
76 std::string{input},
77 split->number_end,
78 std::string{"expected '"} + std::string{d.symbol} +
79 "', got '" + std::string{tail} + "'"}};
80 }
81
82 // Compile-time metadata accessors -----------------------------------------
83 // Read straight off the unit tag, no descriptor round-trip. Callable as
84 // `MeterValue::id()` (zero-cost, constexpr) or `v.id()` on a concrete
85 // instance — both reach the same static.
86 [[nodiscard]] static constexpr std::string_view id() noexcept {
87 return U::id;
88 }
89 [[nodiscard]] static constexpr std::string_view symbol() noexcept {
90 return U::symbol;
91 }
92 [[nodiscard]] static constexpr std::string_view short_name() noexcept {
93 return U::short_name;
94 }
95 [[nodiscard]] static constexpr std::string_view kind() noexcept {
96 return U::kind;
97 }
98 [[nodiscard]] static constexpr std::string_view long_name() noexcept {
99 return U::long_name;
100 }
101 [[nodiscard]] static constexpr std::optional<comms::Icon> icon() noexcept {
102 return U::icon;
103 }
104 [[nodiscard]] static constexpr std::optional<comms::Color> color() noexcept {
105 return U::color;
106 }
107
108 // IUnitValue overrides ----------------------------------------------------
109 // The descriptor is the only metadata accessor that must be virtual (so
110 // polymorphic readers can introspect a `unique_ptr<IUnitValue>`). Per-field
111 // metadata is reachable via `descriptor().id`, `descriptor().symbol`, ....
112 [[nodiscard]] UnitDescriptor descriptor() const noexcept override {
113 return U::descriptor();
114 }
115 [[nodiscard]] double numeric_as_double() const noexcept override {
116 return static_cast<double>(v);
117 }
118
119 // Out-of-line definitions live in <dimval/format.hpp>.
120 [[nodiscard]] std::string to_string() const override;
121 [[nodiscard]] std::string to_formatted_string() const override;
122
123 [[nodiscard]] std::unique_ptr<IUnitValue> clone() const override {
124 return std::make_unique<UnitValue>(*this);
125 }
126
127 // Comparison: empty IUnitValue base + value member ------------------------
128 [[nodiscard]] friend constexpr bool operator==(const UnitValue& a,
129 const UnitValue& b) noexcept {
130 return a.v == b.v;
131 }
132 [[nodiscard]] friend constexpr auto operator<=>(const UnitValue& a,
133 const UnitValue& b) noexcept {
134 return a.v <=> b.v;
135 }
136
137 constexpr UnitValue& operator+=(const UnitValue& rhs) noexcept {
138 v += rhs.v;
139 return *this;
140 }
141 constexpr UnitValue& operator-=(const UnitValue& rhs) noexcept {
142 v -= rhs.v;
143 return *this;
144 }
145 constexpr UnitValue& operator*=(T s) noexcept {
146 v *= s;
147 return *this;
148 }
149 constexpr UnitValue& operator/=(T s) noexcept {
150 v /= s;
151 return *this;
152 }
153};
154
155template <UnitLike U, NumericValue T>
156[[nodiscard]] constexpr UnitValue<U, T> operator+(const UnitValue<U, T>& a,
157 const UnitValue<U, T>& b) noexcept {
158 return UnitValue<U, T>{static_cast<T>(a.v + b.v)};
159}
160
161template <UnitLike U, NumericValue T>
162[[nodiscard]] constexpr UnitValue<U, T> operator-(const UnitValue<U, T>& a,
163 const UnitValue<U, T>& b) noexcept {
164 return UnitValue<U, T>{static_cast<T>(a.v - b.v)};
165}
166
167template <UnitLike U, NumericValue T>
168[[nodiscard]] constexpr UnitValue<U, T> operator-(const UnitValue<U, T>& a) noexcept {
169 return UnitValue<U, T>{static_cast<T>(-a.v)};
170}
171
172template <UnitLike U, NumericValue T>
173[[nodiscard]] constexpr UnitValue<U, T> operator+(const UnitValue<U, T>& a) noexcept {
174 return a;
175}
176
177template <UnitLike U, NumericValue T>
178[[nodiscard]] constexpr UnitValue<U, T> operator*(const UnitValue<U, T>& a, T s) noexcept {
179 return UnitValue<U, T>{static_cast<T>(a.v * s)};
180}
181
182template <UnitLike U, NumericValue T>
183[[nodiscard]] constexpr UnitValue<U, T> operator*(T s, const UnitValue<U, T>& a) noexcept {
184 return UnitValue<U, T>{static_cast<T>(a.v * s)};
185}
186
187template <UnitLike U, NumericValue T>
188[[nodiscard]] constexpr UnitValue<U, T> operator/(const UnitValue<U, T>& a, T s) noexcept {
189 return UnitValue<U, T>{static_cast<T>(a.v / s)};
190}
191
193template <UnitLike U, NumericValue T>
194[[nodiscard]] constexpr T operator/(const UnitValue<U, T>& a, const UnitValue<U, T>& b) noexcept {
195 return static_cast<T>(a.v / b.v);
196}
197
200template <UnitLike To, UnitLike From, NumericValue T>
201[[nodiscard]] constexpr UnitValue<To, T> convert(const UnitValue<From, T>& v) noexcept {
202 static_assert(units_compatible<From, To>(),
203 "dimval::convert: source and destination units have different kinds");
204 constexpr auto from_d = From::descriptor();
205 constexpr auto to_d = To::descriptor();
206 if constexpr (std::is_same_v<From, To>) {
207 return v;
208 } else {
209 // canonical = factor * v + offset; v_to = (canonical - offset_to) / factor_to.
210 const auto canonical = static_cast<double>(v.v) * from_d.factor + from_d.offset;
211 const auto out = (canonical - to_d.offset) / to_d.factor;
212 return UnitValue<To, T>{static_cast<T>(out)};
213 }
214}
215
217template <UnitLike U, NumericValue T = double>
218[[nodiscard]] constexpr UnitValue<U, T> unit_value(T v) noexcept {
219 return UnitValue<U, T>{v};
220}
221
222} // namespace dimval
Polymorphic interface implemented by every UnitValue<U,T>.
Definition interface.hpp:27
Common concepts, error types, and small utilities used across dimval.
Abstract interfaces for runtime polymorphic handling of dimensional values.
Low-level parsing primitives shared by parse.hpp and the inline UnitValue::parse / MeasureValue::pars...
Detailed error report from a parse_*_value call.
Definition core.hpp:30
Runtime metadata for a unit.
Definition descriptor.hpp:29
A value paired at the type level with a unit tag.
Definition unit.hpp:36
static std::expected< UnitValue, ParseError > parse(const std::string_view input)
Parse "42.5 m" → UnitValue<U,T>.
Definition unit.hpp:62
double numeric_as_double() const noexcept override
Numeric value as a double (lossy for >double types).
Definition unit.hpp:115
static std::shared_ptr< UnitValue > of(T value)
Heap-allocated shared-owned factory: auto p = MeterValue::of(1.78); For a stack value,...
Definition unit.hpp:50
std::unique_ptr< IUnitValue > clone() const override
Deep copy as the interface type — the moral equivalent of a virtual copy ctor.
Definition unit.hpp:123
std::string to_string() const override
Render <value> <symbol> (or whatever the descriptor's formatter dictates).
Definition format.hpp:340
static std::unique_ptr< UnitValue > unique(T value)
Heap-allocated unique-owned factory: auto p = MeterValue::unique(1.78);
Definition unit.hpp:55
std::string to_formatted_string() const override
Render with the descriptor's default precision applied.
Definition format.hpp:345
UnitDescriptor descriptor() const noexcept override
Full descriptor — read individual fields (descriptor().id, ...) from here.
Definition unit.hpp:112
constexpr UnitValue(T value) noexcept
Implicit conversion from the numeric type — enables MeterValue distance = 1.78; style construction.
Definition unit.hpp:46
Concepts and helper accessors that drive the static side of dimval.
constexpr UnitValue< To, T > convert(const UnitValue< From, T > &v) noexcept
Convert a UnitValue between units of the same kind.
Definition unit.hpp:201
constexpr UnitValue< U, T > unit_value(T v) noexcept
Sugar: produce a UnitValue from a scalar literal.
Definition unit.hpp:218