1 | // Boost.Units - A C++ library for zero-overhead dimensional analysis and |
2 | // unit/quantity manipulation and conversion |
3 | // |
4 | // Copyright (C) 2003-2008 Matthias Christian Schabel |
5 | // Copyright (C) 2007-2010 Steven Watanabe |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See |
8 | // accompanying file LICENSE_1_0.txt or copy at |
9 | // http://www.boost.org/LICENSE_1_0.txt) |
10 | |
11 | #ifndef BOOST_UNITS_IO_HPP |
12 | #define BOOST_UNITS_IO_HPP |
13 | |
14 | /// \file |
15 | /// \brief Stream input and output for rationals, units and quantities. |
16 | /// \details Functions and manipulators for output and input of units and quantities. |
17 | /// symbol and name format, and engineering and binary autoprefix. |
18 | /// Serialization output is also supported. |
19 | |
20 | #include <cassert> |
21 | #include <cmath> |
22 | #include <string> |
23 | #include <iosfwd> |
24 | #include <ios> |
25 | #include <sstream> |
26 | |
27 | #include <boost/serialization/nvp.hpp> |
28 | |
29 | #include <boost/units/units_fwd.hpp> |
30 | #include <boost/units/heterogeneous_system.hpp> |
31 | #include <boost/units/make_scaled_unit.hpp> |
32 | #include <boost/units/quantity.hpp> |
33 | #include <boost/units/scale.hpp> |
34 | #include <boost/units/static_rational.hpp> |
35 | #include <boost/units/unit.hpp> |
36 | #include <boost/units/detail/utility.hpp> |
37 | |
38 | namespace boost { |
39 | |
40 | namespace serialization { |
41 | |
42 | /// Boost Serialization library support for units. |
43 | template<class Archive,class System,class Dim> |
44 | inline void serialize(Archive& ar,boost::units::unit<Dim,System>&,const unsigned int /*version*/) |
45 | { } |
46 | |
47 | /// Boost Serialization library support for quantities. |
48 | template<class Archive,class Unit,class Y> |
49 | inline void serialize(Archive& ar,boost::units::quantity<Unit,Y>& q,const unsigned int /*version*/) |
50 | { |
51 | ar & boost::serialization::make_nvp("value" , units::quantity_cast<Y&>(q)); |
52 | } |
53 | |
54 | } // namespace serialization |
55 | |
56 | namespace units { |
57 | |
58 | // get string representation of arbitrary type. |
59 | template<class T> std::string to_string(const T& t) |
60 | { |
61 | std::stringstream sstr; |
62 | |
63 | sstr << t; |
64 | |
65 | return sstr.str(); |
66 | } |
67 | |
68 | /// get string representation of integral-valued @c static_rational. |
69 | template<integer_type N> std::string to_string(const static_rational<N>&) |
70 | { |
71 | return to_string(t: N); |
72 | } |
73 | |
74 | /// get string representation of @c static_rational. |
75 | template<integer_type N, integer_type D> std::string to_string(const static_rational<N,D>&) |
76 | { |
77 | return '(' + to_string(t: N) + '/' + to_string(t: D) + ')'; |
78 | } |
79 | |
80 | /// Write @c static_rational to @c std::basic_ostream. |
81 | template<class Char, class Traits, integer_type N, integer_type D> |
82 | inline std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os,const static_rational<N,D>& r) |
83 | { |
84 | os << to_string(r); |
85 | return os; |
86 | } |
87 | |
88 | /// traits template for unit names. |
89 | template<class BaseUnit> |
90 | struct base_unit_info |
91 | { |
92 | /// INTERNAL ONLY |
93 | typedef void base_unit_info_primary_template; |
94 | /// The full name of the unit (returns BaseUnit::name() by default) |
95 | static std::string name() |
96 | { |
97 | return(BaseUnit::name()); |
98 | } |
99 | /// The symbol for the base unit (Returns BaseUnit::symbol() by default) |
100 | static std::string symbol() |
101 | { |
102 | return(BaseUnit::symbol()); /// \returns BaseUnit::symbol(), for example "m" |
103 | } |
104 | }; |
105 | |
106 | /// \enum format_mode format of output of units, for example "m" or "meter". |
107 | enum format_mode |
108 | { |
109 | symbol_fmt = 0, /// default - reduces unit names to known symbols for both base and derived units. |
110 | name_fmt = 1, /// output full unit names for base and derived units, for example "meter". |
111 | raw_fmt = 2, /// output only symbols for base units (but not derived units), for example "m". |
112 | typename_fmt = 3, /// output demangled typenames (useful only for diagnosis). |
113 | fmt_mask = 3 /// Bits used for format. |
114 | }; |
115 | |
116 | /// \enum autoprefix_mode automatic scaling and prefix (controlled by value of quantity) a, if any, |
117 | enum autoprefix_mode |
118 | { |
119 | autoprefix_none = 0, /// No automatic prefix. |
120 | autoprefix_engineering = 4, /// Scale and prefix with 10^3 multiples, 1234.5 m output as 1.2345 km. |
121 | autoprefix_binary = 8, /// Scale and prefix with 2^10 (1024) multiples, 1024 as 1 kb. |
122 | autoprefix_mask = 12 /// Bits used for autoprefix. |
123 | }; |
124 | |
125 | namespace detail { |
126 | |
127 | template<bool> |
128 | struct xalloc_key_holder |
129 | { |
130 | static int value; |
131 | static bool initialized; |
132 | }; |
133 | |
134 | template<bool b> |
135 | int xalloc_key_holder<b>::value = 0; |
136 | |
137 | template<bool b> |
138 | bool xalloc_key_holder<b>::initialized = 0; |
139 | |
140 | struct xalloc_key_initializer_t |
141 | { |
142 | xalloc_key_initializer_t() |
143 | { |
144 | if (!xalloc_key_holder<true>::initialized) |
145 | { |
146 | xalloc_key_holder<true>::value = std::ios_base::xalloc(); |
147 | xalloc_key_holder<true>::initialized = true; |
148 | } |
149 | } |
150 | }; |
151 | |
152 | namespace /**/ { |
153 | |
154 | xalloc_key_initializer_t xalloc_key_initializer; |
155 | |
156 | } // namespace |
157 | |
158 | } // namespace detail |
159 | |
160 | /// returns flags controlling output. |
161 | inline long get_flags(std::ios_base& ios, long mask) |
162 | { |
163 | return(ios.iword(ix: detail::xalloc_key_holder<true>::value) & mask); |
164 | } |
165 | |
166 | /// Set new flags controlling output format. |
167 | inline void set_flags(std::ios_base& ios, long new_flags, long mask) |
168 | { |
169 | assert((~mask & new_flags) == 0); |
170 | long& flags = ios.iword(ix: detail::xalloc_key_holder<true>::value); |
171 | flags = (flags & ~mask) | new_flags; |
172 | } |
173 | |
174 | /// returns flags controlling output format. |
175 | inline format_mode get_format(std::ios_base& ios) |
176 | { |
177 | return(static_cast<format_mode>((get_flags)(ios, mask: fmt_mask))); |
178 | } |
179 | |
180 | /// Set new flags controlling output format. |
181 | inline void set_format(std::ios_base& ios, format_mode new_mode) |
182 | { |
183 | (set_flags)(ios, new_flags: new_mode, mask: fmt_mask); |
184 | } |
185 | |
186 | /// Set new flags for type_name output format. |
187 | inline std::ios_base& typename_format(std::ios_base& ios) |
188 | { |
189 | (set_format)(ios, new_mode: typename_fmt); |
190 | return(ios); |
191 | } |
192 | |
193 | /// set new flag for raw format output, for example "m". |
194 | inline std::ios_base& raw_format(std::ios_base& ios) |
195 | { |
196 | (set_format)(ios, new_mode: raw_fmt); |
197 | return(ios); |
198 | } |
199 | |
200 | /// set new format flag for symbol output, for example "m". |
201 | inline std::ios_base& symbol_format(std::ios_base& ios) |
202 | { |
203 | (set_format)(ios, new_mode: symbol_fmt); |
204 | return(ios); |
205 | } |
206 | |
207 | /// set new format for name output, for example "meter". |
208 | inline std::ios_base& name_format(std::ios_base& ios) |
209 | { |
210 | (set_format)(ios, new_mode: name_fmt); |
211 | return(ios); |
212 | } |
213 | |
214 | /// get autoprefix flags for output. |
215 | inline autoprefix_mode get_autoprefix(std::ios_base& ios) |
216 | { |
217 | return static_cast<autoprefix_mode>((get_flags)(ios, mask: autoprefix_mask)); |
218 | } |
219 | |
220 | /// Get format for output. |
221 | inline void set_autoprefix(std::ios_base& ios, autoprefix_mode new_mode) |
222 | { |
223 | (set_flags)(ios, new_flags: new_mode, mask: autoprefix_mask); |
224 | } |
225 | |
226 | /// Clear autoprefix flags. |
227 | inline std::ios_base& no_prefix(std::ios_base& ios) |
228 | { |
229 | (set_autoprefix)(ios, new_mode: autoprefix_none); |
230 | return ios; |
231 | } |
232 | |
233 | /// Set flag for engineering prefix, so 1234.5 m displays as "1.2345 km". |
234 | inline std::ios_base& engineering_prefix(std::ios_base& ios) |
235 | { |
236 | (set_autoprefix)(ios, new_mode: autoprefix_engineering); |
237 | return ios; |
238 | } |
239 | |
240 | /// Set flag for binary prefix, so 1024 byte displays as "1 Kib". |
241 | inline std::ios_base& binary_prefix(std::ios_base& ios) |
242 | { |
243 | (set_autoprefix)(ios, new_mode: autoprefix_binary); |
244 | return ios; |
245 | } |
246 | |
247 | namespace detail { |
248 | |
249 | /// \return exponent string like "^1/2". |
250 | template<integer_type N, integer_type D> |
251 | inline std::string exponent_string(const static_rational<N,D>& r) |
252 | { |
253 | return '^' + to_string(r); |
254 | } |
255 | |
256 | /// \return empty exponent string for integer rational like 2. |
257 | template<> |
258 | inline std::string exponent_string(const static_rational<1>&) |
259 | { |
260 | return "" ; |
261 | } |
262 | |
263 | template<class T> |
264 | inline std::string base_unit_symbol_string(const T&) |
265 | { |
266 | return base_unit_info<typename T::tag_type>::symbol() + exponent_string(typename T::value_type()); |
267 | } |
268 | |
269 | template<class T> |
270 | inline std::string base_unit_name_string(const T&) |
271 | { |
272 | return base_unit_info<typename T::tag_type>::name() + exponent_string(typename T::value_type()); |
273 | } |
274 | |
275 | // stringify with symbols. |
276 | template<int N> |
277 | struct symbol_string_impl |
278 | { |
279 | template<class Begin> |
280 | struct apply |
281 | { |
282 | typedef typename symbol_string_impl<N-1>::template apply<typename Begin::next> next; |
283 | static void value(std::string& str) |
284 | { |
285 | str += base_unit_symbol_string(typename Begin::item()) + ' '; |
286 | next::value(str); |
287 | } |
288 | }; |
289 | }; |
290 | |
291 | template<> |
292 | struct symbol_string_impl<1> |
293 | { |
294 | template<class Begin> |
295 | struct apply |
296 | { |
297 | static void value(std::string& str) |
298 | { |
299 | str += base_unit_symbol_string(typename Begin::item()); |
300 | }; |
301 | }; |
302 | }; |
303 | |
304 | template<> |
305 | struct symbol_string_impl<0> |
306 | { |
307 | template<class Begin> |
308 | struct apply |
309 | { |
310 | static void value(std::string& str) |
311 | { |
312 | // better shorthand for dimensionless? |
313 | str += "dimensionless" ; |
314 | } |
315 | }; |
316 | }; |
317 | |
318 | template<int N> |
319 | struct scale_symbol_string_impl |
320 | { |
321 | template<class Begin> |
322 | struct apply |
323 | { |
324 | static void value(std::string& str) |
325 | { |
326 | str += Begin::item::symbol(); |
327 | scale_symbol_string_impl<N - 1>::template apply<typename Begin::next>::value(str); |
328 | } |
329 | }; |
330 | }; |
331 | |
332 | template<> |
333 | struct scale_symbol_string_impl<0> |
334 | { |
335 | template<class Begin> |
336 | struct apply |
337 | { |
338 | static void value(std::string&) { } |
339 | }; |
340 | }; |
341 | |
342 | // stringify with names. |
343 | template<int N> |
344 | struct name_string_impl |
345 | { |
346 | template<class Begin> |
347 | struct apply |
348 | { |
349 | typedef typename name_string_impl<N-1>::template apply<typename Begin::next> next; |
350 | static void value(std::string& str) |
351 | { |
352 | str += base_unit_name_string(typename Begin::item()) + ' '; |
353 | next::value(str); |
354 | } |
355 | }; |
356 | }; |
357 | |
358 | template<> |
359 | struct name_string_impl<1> |
360 | { |
361 | template<class Begin> |
362 | struct apply |
363 | { |
364 | static void value(std::string& str) |
365 | { |
366 | str += base_unit_name_string(typename Begin::item()); |
367 | }; |
368 | }; |
369 | }; |
370 | |
371 | template<> |
372 | struct name_string_impl<0> |
373 | { |
374 | template<class Begin> |
375 | struct apply |
376 | { |
377 | static void value(std::string& str) |
378 | { |
379 | str += "dimensionless" ; |
380 | } |
381 | }; |
382 | }; |
383 | |
384 | template<int N> |
385 | struct scale_name_string_impl |
386 | { |
387 | template<class Begin> |
388 | struct apply |
389 | { |
390 | static void value(std::string& str) |
391 | { |
392 | str += Begin::item::name(); |
393 | scale_name_string_impl<N - 1>::template apply<typename Begin::next>::value(str); |
394 | } |
395 | }; |
396 | }; |
397 | |
398 | template<> |
399 | struct scale_name_string_impl<0> |
400 | { |
401 | template<class Begin> |
402 | struct apply |
403 | { |
404 | static void value(std::string&) { } |
405 | }; |
406 | }; |
407 | |
408 | } // namespace detail |
409 | |
410 | namespace detail { |
411 | |
412 | // These two overloads of symbol_string and name_string will |
413 | // will pick up homogeneous_systems. They simply call the |
414 | // appropriate function with a heterogeneous_system. |
415 | template<class Dimension,class System, class SubFormatter> |
416 | inline std::string |
417 | to_string_impl(const unit<Dimension,System>&, SubFormatter f) |
418 | { |
419 | return f(typename reduce_unit<unit<Dimension, System> >::type()); |
420 | } |
421 | |
422 | /// INTERNAL ONLY |
423 | // this overload picks up heterogeneous units that are not scaled. |
424 | template<class Dimension,class Units, class Subformatter> |
425 | inline std::string |
426 | to_string_impl(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, dimensionless_type> > >&, Subformatter f) |
427 | { |
428 | std::string str; |
429 | f.template append_units_to<Units>(str); |
430 | return(str); |
431 | } |
432 | |
433 | // This overload is a special case for heterogeneous_system which |
434 | // is really unitless |
435 | /// INTERNAL ONLY |
436 | template<class Subformatter> |
437 | inline std::string |
438 | to_string_impl(const unit<dimensionless_type, heterogeneous_system<heterogeneous_system_impl<dimensionless_type, dimensionless_type, dimensionless_type> > >&, Subformatter) |
439 | { |
440 | return("dimensionless" ); |
441 | } |
442 | |
443 | // this overload deals with heterogeneous_systems which are unitless |
444 | // but scaled. |
445 | /// INTERNAL ONLY |
446 | template<class Scale, class Subformatter> |
447 | inline std::string |
448 | to_string_impl(const unit<dimensionless_type, heterogeneous_system<heterogeneous_system_impl<dimensionless_type, dimensionless_type, Scale> > >&, Subformatter f) |
449 | { |
450 | std::string str; |
451 | f.template append_scale_to<Scale>(str); |
452 | return(str); |
453 | } |
454 | |
455 | // this overload deals with scaled units. |
456 | /// INTERNAL ONLY |
457 | template<class Dimension,class Units,class Scale, class Subformatter> |
458 | inline std::string |
459 | to_string_impl(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, Scale> > >&, Subformatter f) |
460 | { |
461 | std::string str; |
462 | |
463 | f.template append_scale_to<Scale>(str); |
464 | |
465 | std::string without_scale = f(unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, dimensionless_type> > >()); |
466 | |
467 | if (f.is_default_string(without_scale, unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, dimensionless_type> > >())) |
468 | { |
469 | str += "(" ; |
470 | str += without_scale; |
471 | str += ")" ; |
472 | } |
473 | else |
474 | { |
475 | str += without_scale; |
476 | } |
477 | |
478 | return(str); |
479 | } |
480 | |
481 | // This overload catches scaled units that have a single base unit |
482 | // raised to the first power. It causes si::nano * si::meters to not |
483 | // put parentheses around the meters. i.e. nm rather than n(m) |
484 | /// INTERNAL ONLY |
485 | template<class Dimension,class Unit,class Scale, class Subformatter> |
486 | inline std::string |
487 | to_string_impl(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit, static_rational<1> >,dimensionless_type>, Dimension, Scale> > >&, Subformatter f) |
488 | { |
489 | std::string str; |
490 | |
491 | f.template append_scale_to<Scale>(str); |
492 | str += f(unit<Dimension, heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit, static_rational<1> >, dimensionless_type>, Dimension, dimensionless_type> > >()); |
493 | |
494 | return(str); |
495 | } |
496 | |
497 | // This overload is necessary to disambiguate. |
498 | // it catches units that are unscaled and have a single |
499 | // base unit raised to the first power. It is treated the |
500 | // same as any other unscaled unit. |
501 | /// INTERNAL ONLY |
502 | template<class Dimension,class Unit,class Subformatter> |
503 | inline std::string |
504 | to_string_impl(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit, static_rational<1> >,dimensionless_type>, Dimension, dimensionless_type> > >&, Subformatter f) |
505 | { |
506 | std::string str; |
507 | f.template append_units_to<list<heterogeneous_system_dim<Unit, static_rational<1> >,dimensionless_type> >(str); |
508 | return(str); |
509 | } |
510 | |
511 | // This overload catches scaled units that have a single scaled base unit |
512 | // raised to the first power. It moves that scaling on the base unit |
513 | // to the unit level scaling and recurses. By doing this we make sure that |
514 | // si::milli * si::kilograms will print g rather than mkg. |
515 | // |
516 | // This transformation will not be applied if base_unit_info is specialized |
517 | // for the scaled base unit. |
518 | // |
519 | /// INTERNAL ONLY |
520 | template<class Dimension,class Unit,class UnitScale, class Scale, class Subformatter> |
521 | inline std::string |
522 | to_string_impl( |
523 | const unit< |
524 | Dimension, |
525 | heterogeneous_system< |
526 | heterogeneous_system_impl< |
527 | list<heterogeneous_system_dim<scaled_base_unit<Unit, UnitScale>, static_rational<1> >, dimensionless_type>, |
528 | Dimension, |
529 | Scale |
530 | > |
531 | > |
532 | >&, |
533 | Subformatter f, |
534 | typename base_unit_info<scaled_base_unit<Unit, UnitScale> >::base_unit_info_primary_template* = 0) |
535 | { |
536 | return(f( |
537 | unit< |
538 | Dimension, |
539 | heterogeneous_system< |
540 | heterogeneous_system_impl< |
541 | list<heterogeneous_system_dim<Unit, static_rational<1> >, dimensionless_type>, |
542 | Dimension, |
543 | typename mpl::times<Scale, list<scale_list_dim<UnitScale>, dimensionless_type> >::type |
544 | > |
545 | > |
546 | >())); |
547 | } |
548 | |
549 | // this overload disambuguates between the overload for an unscaled unit |
550 | // and the overload for a scaled base unit raised to the first power. |
551 | /// INTERNAL ONLY |
552 | template<class Dimension,class Unit,class UnitScale,class Subformatter> |
553 | inline std::string |
554 | to_string_impl( |
555 | const unit< |
556 | Dimension, |
557 | heterogeneous_system< |
558 | heterogeneous_system_impl< |
559 | list<heterogeneous_system_dim<scaled_base_unit<Unit, UnitScale>, static_rational<1> >, dimensionless_type>, |
560 | Dimension, |
561 | dimensionless_type |
562 | > |
563 | > |
564 | >&, |
565 | Subformatter f, |
566 | typename base_unit_info<scaled_base_unit<Unit, UnitScale> >::base_unit_info_primary_template* = 0) |
567 | { |
568 | std::string str; |
569 | f.template append_units_to<list<heterogeneous_system_dim<scaled_base_unit<Unit, UnitScale>, static_rational<1> >, dimensionless_type> >(str); |
570 | return(str); |
571 | } |
572 | |
573 | struct format_raw_symbol_impl { |
574 | template<class Units> |
575 | void append_units_to(std::string& str) { |
576 | detail::symbol_string_impl<Units::size::value>::template apply<Units>::value(str); |
577 | } |
578 | template<class Scale> |
579 | void append_scale_to(std::string& str) { |
580 | detail::scale_symbol_string_impl<Scale::size::value>::template apply<Scale>::value(str); |
581 | } |
582 | template<class Unit> |
583 | std::string operator()(const Unit& u) { |
584 | return(to_string_impl(u, *this)); |
585 | } |
586 | template<class Unit> |
587 | bool is_default_string(const std::string&, const Unit&) { |
588 | return(true); |
589 | } |
590 | }; |
591 | |
592 | struct format_symbol_impl : format_raw_symbol_impl { |
593 | template<class Unit> |
594 | std::string operator()(const Unit& u) { |
595 | return(symbol_string(u)); |
596 | } |
597 | template<class Unit> |
598 | bool is_default_string(const std::string& str, const Unit& u) { |
599 | return(str == to_string_impl(u, format_raw_symbol_impl())); |
600 | } |
601 | }; |
602 | |
603 | struct format_raw_name_impl { |
604 | template<class Units> |
605 | void append_units_to(std::string& str) { |
606 | detail::name_string_impl<(Units::size::value)>::template apply<Units>::value(str); |
607 | } |
608 | template<class Scale> |
609 | void append_scale_to(std::string& str) { |
610 | detail::scale_name_string_impl<Scale::size::value>::template apply<Scale>::value(str); |
611 | } |
612 | template<class Unit> |
613 | std::string operator()(const Unit& u) { |
614 | return(to_string_impl(u, *this)); |
615 | } |
616 | template<class Unit> |
617 | bool is_default_string(const std::string&, const Unit&) { |
618 | return(true); |
619 | } |
620 | }; |
621 | |
622 | struct format_name_impl : format_raw_name_impl { |
623 | template<class Unit> |
624 | std::string operator()(const Unit& u) { |
625 | return(name_string(u)); |
626 | } |
627 | template<class Unit> |
628 | bool is_default_string(const std::string& str, const Unit& u) { |
629 | return(str == to_string_impl(u, format_raw_name_impl())); |
630 | } |
631 | }; |
632 | |
633 | template<class Char, class Traits> |
634 | inline void do_print(std::basic_ostream<Char, Traits>& os, const std::string& s) |
635 | { |
636 | os << s.c_str(); |
637 | } |
638 | |
639 | inline void do_print(std::ostream& os, const std::string& s) |
640 | { |
641 | os << s; |
642 | } |
643 | |
644 | template<class Char, class Traits> |
645 | inline void do_print(std::basic_ostream<Char, Traits>& os, const char* s) |
646 | { |
647 | os << s; |
648 | } |
649 | |
650 | // For automatically applying the appropriate prefixes. |
651 | |
652 | } |
653 | |
654 | #ifdef BOOST_UNITS_DOXYGEN |
655 | |
656 | /// ADL customization point for automatic prefixing. |
657 | /// Returns a non-negative value. Implemented as std::abs |
658 | /// for built-in types. |
659 | template<class T> |
660 | double autoprefix_norm(const T& arg); |
661 | |
662 | #else |
663 | |
664 | template<class T, bool C = boost::is_arithmetic<T>::value> |
665 | struct autoprefix_norm_impl; |
666 | |
667 | template<class T> |
668 | struct autoprefix_norm_impl<T, true> |
669 | { |
670 | typedef double type; |
671 | static double call(const T& arg) { return std::abs(arg); } |
672 | }; |
673 | |
674 | template<class T> |
675 | struct autoprefix_norm_impl<T, false> |
676 | { |
677 | typedef one type; |
678 | static one call(const T&) { return one(); } |
679 | }; |
680 | |
681 | template<class T> |
682 | typename autoprefix_norm_impl<T>::type autoprefix_norm(const T& arg) |
683 | { |
684 | return autoprefix_norm_impl<T>::call(arg); |
685 | } |
686 | |
687 | #endif |
688 | |
689 | namespace detail { |
690 | |
691 | template<class End, class Prev, class T, class F> |
692 | bool find_matching_scale_impl(End, End, Prev, T, double, F) |
693 | { |
694 | return false; |
695 | } |
696 | |
697 | template<class Begin, class End, class Prev, class T, class F> |
698 | bool find_matching_scale_impl(Begin, End end, Prev prev, T t, double x, F f) |
699 | { |
700 | if(Begin::item::value() > x) { |
701 | f(prev, t); |
702 | return true; |
703 | } else { |
704 | return detail::find_matching_scale_impl( |
705 | typename Begin::next(), |
706 | end, |
707 | typename Begin::item(), |
708 | t, |
709 | x, |
710 | f |
711 | ); |
712 | } |
713 | } |
714 | |
715 | template<class End, class T, class F> |
716 | bool find_matching_scale_i(End, End, T, double, F) |
717 | { |
718 | return false; |
719 | } |
720 | |
721 | template<class Begin, class End, class T, class F> |
722 | bool find_matching_scale_i(Begin, End end, T t, double x, F f) |
723 | { |
724 | if(Begin::item::value() > x) { |
725 | return false; |
726 | } else { |
727 | return detail::find_matching_scale_impl(typename Begin::next(), end, typename Begin::item(), t, x, f); |
728 | } |
729 | } |
730 | |
731 | template<class Scales, class T, class F> |
732 | bool find_matching_scale(T t, double x, F f) |
733 | { |
734 | return detail::find_matching_scale_i(Scales(), dimensionless_type(), t, x, f); |
735 | } |
736 | |
737 | typedef list<scale<10, static_rational<-24> >, |
738 | list<scale<10, static_rational<-21> >, |
739 | list<scale<10, static_rational<-18> >, |
740 | list<scale<10, static_rational<-15> >, |
741 | list<scale<10, static_rational<-12> >, |
742 | list<scale<10, static_rational<-9> >, |
743 | list<scale<10, static_rational<-6> >, |
744 | list<scale<10, static_rational<-3> >, |
745 | list<scale<10, static_rational<0> >, |
746 | list<scale<10, static_rational<3> >, |
747 | list<scale<10, static_rational<6> >, |
748 | list<scale<10, static_rational<9> >, |
749 | list<scale<10, static_rational<12> >, |
750 | list<scale<10, static_rational<15> >, |
751 | list<scale<10, static_rational<18> >, |
752 | list<scale<10, static_rational<21> >, |
753 | list<scale<10, static_rational<24> >, |
754 | list<scale<10, static_rational<27> >, |
755 | dimensionless_type> > > > > > > > > > > > > > > > > > engineering_prefixes; |
756 | |
757 | typedef list<scale<2, static_rational<10> >, |
758 | list<scale<2, static_rational<20> >, |
759 | list<scale<2, static_rational<30> >, |
760 | list<scale<2, static_rational<40> >, |
761 | list<scale<2, static_rational<50> >, |
762 | list<scale<2, static_rational<60> >, |
763 | list<scale<2, static_rational<70> >, |
764 | list<scale<2, static_rational<80> >, |
765 | list<scale<2, static_rational<90> >, |
766 | dimensionless_type> > > > > > > > > binary_prefixes; |
767 | |
768 | template<class Os, class Quantity> |
769 | struct print_default_t { |
770 | typedef void result_type; |
771 | void operator()() const |
772 | { |
773 | *os << q->value() << ' ' << typename Quantity::unit_type(); |
774 | } |
775 | Os* os; |
776 | const Quantity* q; |
777 | }; |
778 | |
779 | template<class Os, class Quantity> |
780 | print_default_t<Os, Quantity> print_default(Os& os, const Quantity& q) |
781 | { |
782 | print_default_t<Os, Quantity> result = { &os, &q }; |
783 | return result; |
784 | } |
785 | |
786 | template<class Os> |
787 | struct print_scale_t { |
788 | typedef void result_type; |
789 | template<class Prefix, class T> |
790 | void operator()(Prefix, const T& t) const |
791 | { |
792 | *prefixed = true; |
793 | *os << t / Prefix::value() << ' '; |
794 | switch(units::get_format(ios&: *os)) { |
795 | case name_fmt: do_print(*os, Prefix::name()); break; |
796 | case raw_fmt: |
797 | case symbol_fmt: do_print(*os, Prefix::symbol()); break; |
798 | case typename_fmt: do_print(*os, units::simplify_typename(Prefix())); *os << ' '; break; |
799 | } |
800 | } |
801 | template<long N, class T> |
802 | void operator()(scale<N, static_rational<0> >, const T& t) const |
803 | { |
804 | *prefixed = false; |
805 | *os << t << ' '; |
806 | } |
807 | Os* os; |
808 | bool* prefixed; |
809 | }; |
810 | |
811 | template<class Os> |
812 | print_scale_t<Os> print_scale(Os& os, bool& prefixed) |
813 | { |
814 | print_scale_t<Os> result = { &os, &prefixed }; |
815 | return result; |
816 | } |
817 | |
818 | // puts parentheses around a unit |
819 | /// INTERNAL ONLY |
820 | template<class Dimension,class Units,class Scale, class Subformatter> |
821 | inline std::string |
822 | maybe_parenthesize(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, Scale> > >&, Subformatter f) |
823 | { |
824 | std::string str; |
825 | |
826 | std::string without_scale = f(unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, dimensionless_type> > >()); |
827 | |
828 | if (f.is_default_string(without_scale, unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, dimensionless_type> > >())) |
829 | { |
830 | str += "(" ; |
831 | str += without_scale; |
832 | str += ")" ; |
833 | } |
834 | else |
835 | { |
836 | str += without_scale; |
837 | } |
838 | |
839 | return(str); |
840 | } |
841 | |
842 | // This overload catches scaled units that have a single base unit |
843 | // raised to the first power. It causes si::nano * si::meters to not |
844 | // put parentheses around the meters. i.e. nm rather than n(m) |
845 | /// INTERNAL ONLY |
846 | template<class Dimension,class Unit,class Scale, class Subformatter> |
847 | inline std::string |
848 | maybe_parenthesize(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit, static_rational<1> >,dimensionless_type>, Dimension, Scale> > >&, Subformatter f) |
849 | { |
850 | return f(unit<Dimension, heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit, static_rational<1> >, dimensionless_type>, Dimension, dimensionless_type> > >()); |
851 | } |
852 | |
853 | template<class Prefixes, class CharT, class Traits, class Unit, class T, class F> |
854 | void do_print_prefixed_impl(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q, F default_) |
855 | { |
856 | bool prefixed; |
857 | if(detail::find_matching_scale<Prefixes>(q.value(), autoprefix_norm(q.value()), detail::print_scale(os, prefixed))) { |
858 | if(prefixed) { |
859 | switch(units::get_format(ios&: os)) { |
860 | case symbol_fmt: do_print(os, maybe_parenthesize(Unit(), format_symbol_impl())); break; |
861 | case raw_fmt: do_print(os, maybe_parenthesize(Unit(), format_raw_symbol_impl())); break; |
862 | case name_fmt: do_print(os, maybe_parenthesize(Unit(), format_name_impl())); break; |
863 | case typename_fmt: do_print(os, simplify_typename(Unit())); break; |
864 | } |
865 | } else { |
866 | os << Unit(); |
867 | } |
868 | } else { |
869 | default_(); |
870 | } |
871 | } |
872 | |
873 | // Handle units like si::kilograms that have a scale embedded in the |
874 | // base unit. This overload is disabled if the scaled base unit has |
875 | // a user-defined string representation. |
876 | template<class Prefixes, class CharT, class Traits, class Dimension, class BaseUnit, class BaseScale, class Scale, class T> |
877 | typename base_unit_info< |
878 | scaled_base_unit<BaseUnit, Scale> |
879 | >::base_unit_info_primary_template |
880 | do_print_prefixed( |
881 | std::basic_ostream<CharT, Traits>& os, |
882 | const quantity< |
883 | unit< |
884 | Dimension, |
885 | heterogeneous_system< |
886 | heterogeneous_system_impl< |
887 | list< |
888 | heterogeneous_system_dim< |
889 | scaled_base_unit<BaseUnit, BaseScale>, |
890 | static_rational<1> |
891 | >, |
892 | dimensionless_type |
893 | >, |
894 | Dimension, |
895 | Scale |
896 | > |
897 | > |
898 | >, |
899 | T |
900 | >& q) |
901 | { |
902 | quantity< |
903 | unit< |
904 | Dimension, |
905 | heterogeneous_system< |
906 | heterogeneous_system_impl< |
907 | list< |
908 | heterogeneous_system_dim<BaseUnit, static_rational<1> >, |
909 | dimensionless_type |
910 | >, |
911 | Dimension, |
912 | dimensionless_type |
913 | > |
914 | > |
915 | >, |
916 | T |
917 | > unscaled(q); |
918 | detail::do_print_prefixed_impl<Prefixes>(os, unscaled, detail::print_default(os, q)); |
919 | } |
920 | |
921 | template<class Prefixes, class CharT, class Traits, class Dimension, class L, class Scale, class T> |
922 | void do_print_prefixed( |
923 | std::basic_ostream<CharT, Traits>& os, |
924 | const quantity< |
925 | unit< |
926 | Dimension, |
927 | heterogeneous_system< |
928 | heterogeneous_system_impl< |
929 | L, |
930 | Dimension, |
931 | Scale |
932 | > |
933 | > |
934 | >, |
935 | T |
936 | >& q) |
937 | { |
938 | quantity< |
939 | unit< |
940 | Dimension, |
941 | heterogeneous_system< |
942 | heterogeneous_system_impl< |
943 | L, |
944 | Dimension, |
945 | dimensionless_type |
946 | > |
947 | > |
948 | >, |
949 | T |
950 | > unscaled(q); |
951 | detail::do_print_prefixed_impl<Prefixes>(os, unscaled, detail::print_default(os, q)); |
952 | } |
953 | |
954 | template<class Prefixes, class CharT, class Traits, class Dimension, class System, class T> |
955 | void do_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<unit<Dimension, System>, T>& q) |
956 | { |
957 | detail::do_print_prefixed<Prefixes>(os, quantity<unit<Dimension, typename make_heterogeneous_system<Dimension, System>::type>, T>(q)); |
958 | } |
959 | |
960 | template<class Prefixes, class CharT, class Traits, class Unit, class T> |
961 | void do_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q) |
962 | { |
963 | detail::print_default(os, q)(); |
964 | } |
965 | |
966 | template<class Prefixes, class CharT, class Traits, class Unit, class T> |
967 | void maybe_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q, mpl::true_) |
968 | { |
969 | detail::do_print_prefixed<Prefixes>(os, q); |
970 | } |
971 | |
972 | template<class Prefixes, class CharT, class Traits, class Unit, class T> |
973 | void maybe_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q, mpl::false_) |
974 | { |
975 | detail::print_default(os, q)(); |
976 | } |
977 | |
978 | inline mpl::true_ test_norm(double) { return mpl::true_(); } |
979 | inline mpl::false_ test_norm(one) { return mpl::false_(); } |
980 | |
981 | } // namespace detail |
982 | |
983 | template<class Dimension,class System> |
984 | inline std::string |
985 | typename_string(const unit<Dimension, System>&) |
986 | { |
987 | return simplify_typename(typename reduce_unit< unit<Dimension,System> >::type()); |
988 | } |
989 | |
990 | template<class Dimension,class System> |
991 | inline std::string |
992 | symbol_string(const unit<Dimension, System>&) |
993 | { |
994 | return detail::to_string_impl(unit<Dimension,System>(), detail::format_symbol_impl()); |
995 | } |
996 | |
997 | template<class Dimension,class System> |
998 | inline std::string |
999 | name_string(const unit<Dimension, System>&) |
1000 | { |
1001 | return detail::to_string_impl(unit<Dimension,System>(), detail::format_name_impl()); |
1002 | } |
1003 | |
1004 | /// Print a @c unit as a list of base units and their exponents. |
1005 | /// |
1006 | /// for @c symbol_format outputs e.g. "m s^-1" or "J". |
1007 | /// for @c name_format outputs e.g. "meter second^-1" or "joule". |
1008 | /// for @c raw_format outputs e.g. "m s^-1" or "meter kilogram^2 second^-2". |
1009 | /// for @c typename_format outputs the typename itself (currently demangled only on GCC). |
1010 | template<class Char, class Traits, class Dimension, class System> |
1011 | inline std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, const unit<Dimension, System>& u) |
1012 | { |
1013 | if (units::get_format(ios&: os) == typename_fmt) |
1014 | { |
1015 | detail::do_print(os, typename_string(u)); |
1016 | } |
1017 | else if (units::get_format(ios&: os) == raw_fmt) |
1018 | { |
1019 | detail::do_print(os, detail::to_string_impl(u, detail::format_raw_symbol_impl())); |
1020 | } |
1021 | else if (units::get_format(ios&: os) == symbol_fmt) |
1022 | { |
1023 | detail::do_print(os, symbol_string(u)); |
1024 | } |
1025 | else if (units::get_format(ios&: os) == name_fmt) |
1026 | { |
1027 | detail::do_print(os, name_string(u)); |
1028 | } |
1029 | else |
1030 | { |
1031 | assert(!"The format mode must be one of: typename_format, raw_format, name_format, symbol_format" ); |
1032 | } |
1033 | |
1034 | return(os); |
1035 | } |
1036 | |
1037 | /// \brief Print a @c quantity. |
1038 | /// \details Prints the value followed by the unit. |
1039 | /// If the engineering_prefix, or binary_prefix is set, |
1040 | /// tries to scale the value appropriately. |
1041 | /// For example, it might print 12.345 km instead of 12345 m. |
1042 | /// (Note does @b not attempt to automatically scale scalars like double, float...) |
1043 | template<class Char, class Traits, class Unit, class T> |
1044 | inline std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, const quantity<Unit, T>& q) |
1045 | { |
1046 | if (units::get_autoprefix(ios&: os) == autoprefix_none) |
1047 | { |
1048 | os << q.value() << ' ' << Unit(); |
1049 | } |
1050 | else if (units::get_autoprefix(ios&: os) == autoprefix_engineering) |
1051 | { |
1052 | detail::maybe_print_prefixed<detail::engineering_prefixes>(os, q, detail::test_norm(autoprefix_norm(q.value()))); |
1053 | } |
1054 | else if (units::get_autoprefix(ios&: os) == autoprefix_binary) |
1055 | { |
1056 | detail::maybe_print_prefixed<detail::binary_prefixes>(os, q, detail::test_norm(autoprefix_norm(q.value()))); |
1057 | } |
1058 | else |
1059 | { |
1060 | assert(!"Autoprefixing must be one of: no_prefix, engineering_prefix, binary_prefix" ); |
1061 | } |
1062 | return(os); |
1063 | } |
1064 | |
1065 | } // namespace units |
1066 | |
1067 | } // namespace boost |
1068 | |
1069 | #endif |
1070 | |