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