ActiveLib
Loading...
Searching...
No Matches
MeasuredValue.h
1
6#ifndef ACTIVE_SETTING_MEASURED_VALUE
7#define ACTIVE_SETTING_MEASURED_VALUE
8
9#include "Active/Setting/Values/DoubleValue.h"
10#include "Active/Utility/BufferIn.h"
11
12#include <functional>
13
14namespace active::setting {
15
17
84 template<class T>
85 class MeasuredValue : public DoubleValue {
86 public:
87
88 // MARK: - Types
89
90 //Measurement unit type
91 using Type = typename T::Type;
93 using UnitRetrieval = std::function<T()>;
94
95 // MARK: - Constructors
96
105 explicit MeasuredValue(double val) : DoubleValue(val) {}
111 explicit MeasuredValue(const T& unit, double val = 0.0) : DoubleValue(val), m_unit{unit} {}
117 MeasuredValue(UnitRetrieval unitFinder, double val = 0.0) : DoubleValue(val), m_unitFinder{unitFinder} {}
118
119 // MARK: - Operators
120
121 using DoubleValue::operator=;
122
128 Value& operator=(const utility::String& val) override {
129 return assign(val, getUnit());
130 }
131
136 operator utility::String() const override {
137 return (*this)(getUnit());
138 }
143 virtual utility::String operator()(const T& unit) const {
144 //Pair a value with a unit type
145 using UnitValue = std::pair<double, Type>;
146 //Collect the string from a value (as an integer) before replacing it with the remaining fraction of a specified type
147 auto collect = [](UnitValue& value, const T& unit, Type type) {
148 UnitValue otherValue{0.0, type};
149 auto total = value.first;
150 value.first = math::roundDown(total, 1.0);
151 otherValue.first = unit.conversion(otherValue.second, unit.conversion(value.second, total - value.first, true));
152 utility::String result;
153 if (unit.isLeadingZero || !math::isZero(value.first, 1.0))
154 result = utility::String{value.first, 1.0} + unit.suffix(value.second);
155 value = otherValue;
156 return result;
157 };
158
159 bool isSuffix = unit.isUnitSuffix || unit.secondary;
160 UnitValue value{unit.conversion(unit.primary, data), unit.primary};
161 utility::String result;
162 //Split primary and second value when a second unit is specified
163 if (unit.secondary) {
164 result = collect(value, unit, *unit.secondary);
165 //Split secondary and tertiary value when a third unit is specified
166 if (unit.tertiary)
167 result += " " + collect(value, unit, *unit.tertiary);
168 }
169 //Floating point output
170 if (unit.isDecimal()) {
171 if (!result.empty())
172 result += " ";
173 result += utility::String{value.first, unit.eps()};
174 } else {
175 //Fractional output
176 auto wholePart = math::roundDown(value.first, 1.0);
177 if (!math::isZero(wholePart)) {
178 value.first -= wholePart;
179 if (!result.empty())
180 result += " ";
181 result += utility::String{wholePart, 1.0};
182 }
183 auto dividend = static_cast<uint64_t>(math::round(fabs(value.first) / unit.eps(), 1.0));
184 if (dividend != 0) {
185 auto divisor = static_cast<uint64_t>(unit.divisor());
186 while ((dividend % 2) == 0) {
187 dividend /= 2;
188 divisor /= 2;
189 }
190 if (!result.empty())
191 result += " ";
192 result += utility::String{dividend} + "/" + utility::String{divisor};
193 }
194 if (result.empty())
195 result = "0";
196 }
197 if (isSuffix)
198 result += unit.suffix(value.second);
199 return result;
200 }
201
202 // MARK: - Functions (const)
203
208 virtual T getUnit() const { return m_unitFinder ? m_unitFinder() : m_unit; }
209
210 // MARK: - Functions (mutating)
211
216 virtual void setUnit(const T& unit) { m_unit = unit; }
221 virtual void setUnit(UnitRetrieval unitFinder) { m_unitFinder = unitFinder; }
228 virtual Value& assign(const utility::String& val, const T& unit) {
229 //Mark the value as bad until we establish a valid measurement from the text
230 data = 0.0;
231 status = bad;
232 //Find any explicit units in the words to create a list of measurement expressions
233 using UnitExpression = std::pair<utility::String, Type>;
234 std::vector<UnitExpression> measureExpressions;
236 while (start < val.size()) {
237 if (auto match = unit.findSuffix(val, start); match) {
238 if (match->second == 0)
239 return *this; //A unit is leading the expression - not accepted as valid input
240 measureExpressions.push_back(UnitExpression(val.substr(start, match->second - start), match->first));
241 start = match->second + unit.suffix(match->first).size();
242 } else {
243 measureExpressions.push_back(UnitExpression(val.substr(start), unit.primary)); //Use the value unit unit as a default
244 break;
245 }
246 }
247 //Get the localised thousands separator
248 const auto& numPunct{std::use_facet<std::numpunct<char>>(std::locale{})};
249 utility::String thousandsSep{numPunct.thousands_sep()};
250 //Extract measurement values from each expression
251 for (auto& expression : measureExpressions) {
252 //Break the expression into whitespace separated words
253 auto words = utility::BufferIn{expression.first}.readWords();
254 if (words.empty())
255 continue;
256 for (auto& word : words) {
257 //Strip out the thousands separator
258 word.replaceAll(thousandsSep, utility::String{});
259 //Values can be expressed as a fraction - allow for dividend/divisor
260 auto dividend = 0.0, divisor = 1.0;
261 DoubleValue number;
262 //Attempt to extract a divisor
263 if (auto divisionPos = word.find("/"); divisionPos) {
264 if (number = word.substr(*divisionPos + 1); (number.status == good) && !math::isZero(number.data))
265 divisor = number;
266 else
267 return *this; //invalid divisor value
268 word = word.substr(0, divisionPos);
269 }
270 number = word;
271 if (number.status == good)
272 dividend = number;
273 else
274 return *this; //invalid dividend value
275 status = good;
276 data += unit.conversion(expression.second, dividend / divisor, true);
277 }
278 }
279 return *this;
280 }
281
282 private:
284 T m_unit;
286 UnitRetrieval m_unitFinder;
287 };
288
289}
290
291#endif //ACTIVE_SETTING_MEASURED_VALUE
Template specialisation of DoubleValue for values of measurement.
Definition MeasuredValue.h:85
std::function< T()> UnitRetrieval
Function retrieving a measument unit.
Definition MeasuredValue.h:93
MeasuredValue()
Definition MeasuredValue.h:100
virtual void setUnit(UnitRetrieval unitFinder)
Definition MeasuredValue.h:221
virtual void setUnit(const T &unit)
Definition MeasuredValue.h:216
Value & operator=(const utility::String &val) override
Definition MeasuredValue.h:128
MeasuredValue(UnitRetrieval unitFinder, double val=0.0)
Definition MeasuredValue.h:117
virtual T getUnit() const
Definition MeasuredValue.h:208
MeasuredValue(double val)
Definition MeasuredValue.h:105
virtual utility::String operator()(const T &unit) const
Definition MeasuredValue.h:143
virtual Value & assign(const utility::String &val, const T &unit)
Definition MeasuredValue.h:228
MeasuredValue(const T &unit, double val=0.0)
Definition MeasuredValue.h:111
Definition ValueBase.h:21
T data
The value data.
Definition ValueBase.h:58
Definition Value.h:31
Status status
The value status.
Definition Value.h:100
Definition BufferIn.h:27
std::vector< String > readWords(Memory::sizeOption howMany=std::nullopt, const String &division=String::allWhiteSpace) const
Definition BufferIn.cpp:385
A Unicode-aware string class.
Definition String.h:51
bool empty() const
Definition String.h:409
std::string::size_type size_type
Class size type.
Definition String.h:65
String substr(size_type startPos=0, sizeOption howMany=std::nullopt) const
Definition String.cpp:958
size_type size() const
Definition String.cpp:903
double round(const double &val, double module=eps)
Rounding functions.
Definition MathFunctions.h:160
Definition Transportable.h:13