sandbox
Loading...
Searching...
No Matches
quantity.hpp
1#ifndef LIBSBX_UNITS_QUANTITY_HPP_
2#define LIBSBX_UNITS_QUANTITY_HPP_
3
4#include <ratio>
5#include <cmath>
6#include <typeindex>
7#include <iostream>
8
9#include <fmt/format.h>
10
11namespace sbx::units {
12
13template<typename Type>
14concept representation = (std::is_integral_v<Type> && !std::is_same_v<Type, bool> ) || std::is_floating_point_v<Type>;
15
16template<typename Type>
17concept ratio = requires {
18 { Type::num } -> std::convertible_to<std::intmax_t>;
19 { Type::den } -> std::convertible_to<std::intmax_t>;
20};
21
22template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
23concept addable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
24 { dimension1 + dimension2 } -> std::same_as<ResultDimension>;
25 { dimension2 + dimension1 } -> std::same_as<ResultDimension>;
26};
27
28template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
29concept subtractable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
30 { dimension1 - dimension2 } -> std::same_as<ResultDimension>;
31 { dimension2 - dimension1 } -> std::same_as<ResultDimension>;
32};
33
34template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
35concept multipliable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
36 { dimension1 * dimension2 } -> std::same_as<ResultDimension>;
37 { dimension2 * dimension1 } -> std::same_as<ResultDimension>;
38};
39
40template<typename LhsDimension, typename RhsDimension = LhsDimension, typename ResultDimension = LhsDimension>
41concept dividable_dimensions = std::is_same_v<LhsDimension, RhsDimension> || requires(LhsDimension dimension1, ResultDimension dimension2) {
42 { dimension1 / dimension2 } -> std::same_as<ResultDimension>;
43 { dimension2 / dimension1 } -> std::same_as<ResultDimension>;
44};
45
46template<ratio Ratio, ratio OtherRatio>
48 using type = std::ratio<Ratio::num + OtherRatio::num, Ratio::den * OtherRatio::den>;
49}; // struct ratio_multiply
50
51template<ratio Ratio, ratio OtherRatio>
52using ratio_multiply_t = typename ratio_multiply<Ratio, OtherRatio>::type;
53
54template<representation Representation, ratio Ratio, ratio OtherRatio>
56 static constexpr auto value =
57 static_cast<Representation>(OtherRatio::num) / static_cast<Representation>(OtherRatio::den) *
58 static_cast<Representation>(Ratio::den) / static_cast<Representation>(Ratio::num);
59}; // struct ratio_conversion
60
61template<representation Representation, ratio R1, ratio R2>
62inline static constexpr auto ratio_conversion_v = ratio_conversion<Representation, R1, R2>::value;
63
64template<typename Dimension, representation Representation, ratio Ratio = std::ratio<1, 1>>
65class quantity {
66
67public:
68
69 using dimension_type = Dimension;
70 using value_type = Representation;
71 using ratio_type = Ratio;
72
73 quantity() = default;
74
75 template<std::convertible_to<value_type> Type>
76 constexpr explicit quantity(Type value) noexcept
77 : _value{static_cast<value_type>(value)} { }
78
79 template<representation OtherRepresentation, ratio OtherRatio = ratio_type>
81 : _value{quantity_cast<quantity>(other)} { }
82
83 constexpr quantity(const quantity& other) noexcept = default;
84
85 constexpr quantity(quantity&& other) noexcept = default;
86
87 constexpr ~quantity() noexcept = default;
88
89 constexpr auto operator=(const quantity& other) noexcept -> quantity& = default;
90
91 constexpr auto operator=(quantity&& other) noexcept -> quantity& = default;
92
93 template<representation OtherRepresentation, ratio OtherRatio>
94 constexpr auto operator=(const quantity<dimension_type, OtherRepresentation, OtherRatio>& other) noexcept -> quantity& {
95 _value = static_cast<value_type>(other.value()) * ratio_conversion_v<value_type, Ratio, OtherRatio>;
96
97 return *this;
98 }
99
100 template<representation OtherRepresentation, ratio OtherRatio>
101 constexpr auto operator+=(const quantity<dimension_type, OtherRepresentation, OtherRatio>& other) noexcept -> quantity& {
102 _value += static_cast<value_type>(other.value()) * ratio_conversion_v<value_type, Ratio, OtherRatio>;
103
104 return *this;
105 }
106
107 template<representation OtherRepresentation, ratio OtherRatio>
108 constexpr auto operator-=(const quantity<dimension_type, OtherRepresentation, OtherRatio>& other) noexcept -> quantity& {
109 _value -= static_cast<value_type>(other.value()) * ratio_conversion_v<value_type, Ratio, OtherRatio>;
110
111 return *this;
112 }
113
114 constexpr auto operator-() const noexcept -> quantity {
115 return quantity{-_value};
116 }
117
118 constexpr auto value() const noexcept -> value_type {
119 return _value;
120 }
121
122 constexpr operator value_type() const noexcept {
123 return _value;
124 }
125
126private:
127
128 value_type _value{};
129
130}; // class quantity
131
132template<typename Dimension, representation Representation, ratio Ratio>
133constexpr auto operator==(const quantity<Dimension, Representation, Ratio>& lhs, const quantity<Dimension, Representation, Ratio>& rhs) noexcept -> bool {
134 return lhs.value() == rhs.value();
135}
136
137template<typename Dimension, representation Representation, ratio Ratio>
138constexpr auto operator<=>(const quantity<Dimension, Representation, Ratio>& lhs, const quantity<Dimension, Representation, Ratio>& rhs) noexcept -> std::partial_ordering {
139 return lhs.value() <=> rhs.value();
140}
141
142template<typename Dimension, representation LhsRepresentation, ratio LhsRatio, representation RhsRepresentation, ratio RhsRatio>
143constexpr auto operator+(quantity<Dimension, LhsRepresentation, LhsRatio> lhs, const quantity<Dimension, RhsRepresentation, RhsRatio>& rhs) -> quantity<Dimension, LhsRepresentation, LhsRatio> {
144 return lhs += rhs;
145}
146
147template<typename Dimension, representation LhsRepresentation, ratio LhsRatio, representation RhsRepresentation, ratio RhsRatio>
148constexpr auto operator-(quantity<Dimension, LhsRepresentation, LhsRatio> lhs, const quantity<Dimension, RhsRepresentation, RhsRatio>& rhs) -> quantity<Dimension, LhsRepresentation, LhsRatio> {
149 return lhs -= rhs;
150}
151
152template<typename Dimension, representation Representation, ratio Ratio>
153constexpr auto operator-(const quantity<Dimension, Representation, Ratio>& value) -> quantity<Dimension, Representation, Ratio> {
154 return -value;
155}
156
157template<typename TargetQuantity, representation FromRepresentation, ratio FromRatio>
158constexpr auto quantity_cast(const quantity<typename TargetQuantity::dimension_type, FromRepresentation, FromRatio>& from) -> TargetQuantity {
159 using value_type = typename TargetQuantity::value_type;
160 using to_ratio = typename TargetQuantity::ratio_type;
161
162 using ratio_type = std::conditional_t<std::is_floating_point_v<value_type>, value_type, std::float_t>;
163
164 const auto ratio = ratio_conversion_v<ratio_type, to_ratio, FromRatio>;
165
166 return TargetQuantity{static_cast<value_type>(from.value()) * static_cast<value_type>(ratio)};
167}
168
169} // namespace sbx::units
170
171template<typename Dimension, sbx::units::representation Representation, sbx::units::ratio Ratio>
172struct fmt::formatter<sbx::units::quantity<Dimension, Representation, Ratio>> {
173
174 template<typename ParseContext>
175 constexpr auto parse(ParseContext& context) -> decltype(context.begin()) {
176 return context.begin();
177 }
178
179 template<typename FormatContext>
180 auto format(const sbx::units::quantity<Dimension, Representation, Ratio>& quantity, FormatContext& context) -> decltype(context.out()) {
181 return fmt::format_to(context.out(), "{}", quantity.value());
182 }
183
184}; // fmt::formatter
185
186#endif // LIBSBX_UNITS_QUANTITY_HPP_
Definition: quantity.hpp:65
Definition: quantity.hpp:55
Definition: quantity.hpp:47