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>
serialize(Archive & ar,boost::units::unit<Dim,System> &,const unsigned int)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>
serialize(Archive & ar,boost::units::quantity<Unit,Y> & q,const unsigned int)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.
to_string(const T & t)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.
to_string(const static_rational<N> &)69 template<integer_type N> std::string to_string(const static_rational<N>&)
70 {
71     return to_string(N);
72 }
73 
74 /// get string representation of @c static_rational.
to_string(const static_rational<N,D> &)75 template<integer_type N, integer_type D> std::string to_string(const static_rational<N,D>&)
76 {
77     return '(' + to_string(N) + '/' + to_string(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>
operator <<(std::basic_ostream<Char,Traits> & os,const static_rational<N,D> & r)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)
nameboost::units::base_unit_info95     static std::string name()
96     {
97         return(BaseUnit::name());
98     }
99     /// The symbol for the base unit (Returns BaseUnit::symbol() by default)
symbolboost::units::base_unit_info100     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 {
xalloc_key_initializer_tboost::units::detail::xalloc_key_initializer_t142     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.
get_flags(std::ios_base & ios,long mask)161 inline long get_flags(std::ios_base& ios, long mask)
162 {
163     return(ios.iword(detail::xalloc_key_holder<true>::value) & mask);
164 }
165 
166 /// Set new flags controlling output format.
set_flags(std::ios_base & ios,long new_flags,long mask)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(detail::xalloc_key_holder<true>::value);
171     flags = (flags & ~mask) | new_flags;
172 }
173 
174 /// returns flags controlling output format.
get_format(std::ios_base & ios)175 inline format_mode get_format(std::ios_base& ios)
176 {
177     return(static_cast<format_mode>((get_flags)(ios, fmt_mask)));
178 }
179 
180 /// Set new flags controlling output format.
set_format(std::ios_base & ios,format_mode new_mode)181 inline void set_format(std::ios_base& ios, format_mode new_mode)
182 {
183     (set_flags)(ios, new_mode, fmt_mask);
184 }
185 
186 /// Set new flags for type_name output format.
typename_format(std::ios_base & ios)187 inline std::ios_base& typename_format(std::ios_base& ios)
188 {
189     (set_format)(ios, typename_fmt);
190     return(ios);
191 }
192 
193 /// set new flag for raw format output, for example "m".
raw_format(std::ios_base & ios)194 inline std::ios_base& raw_format(std::ios_base& ios)
195 {
196     (set_format)(ios, raw_fmt);
197     return(ios);
198 }
199 
200 /// set new format flag for symbol output, for example "m".
symbol_format(std::ios_base & ios)201 inline std::ios_base& symbol_format(std::ios_base& ios)
202 {
203     (set_format)(ios, symbol_fmt);
204     return(ios);
205 }
206 
207 /// set new format for name output, for example "meter".
name_format(std::ios_base & ios)208 inline std::ios_base& name_format(std::ios_base& ios)
209 {
210     (set_format)(ios, name_fmt);
211     return(ios);
212 }
213 
214 /// get autoprefix flags for output.
get_autoprefix(std::ios_base & ios)215 inline autoprefix_mode get_autoprefix(std::ios_base& ios)
216 {
217     return static_cast<autoprefix_mode>((get_flags)(ios, autoprefix_mask));
218 }
219 
220 /// Get format for output.
set_autoprefix(std::ios_base & ios,autoprefix_mode new_mode)221 inline void set_autoprefix(std::ios_base& ios, autoprefix_mode new_mode)
222 {
223     (set_flags)(ios, new_mode, autoprefix_mask);
224 }
225 
226 /// Clear autoprefix flags.
no_prefix(std::ios_base & ios)227 inline std::ios_base& no_prefix(std::ios_base& ios)
228 {
229     (set_autoprefix)(ios, autoprefix_none);
230     return ios;
231 }
232 
233 /// Set flag for engineering prefix, so 1234.5 m displays as "1.2345 km".
engineering_prefix(std::ios_base & ios)234 inline std::ios_base& engineering_prefix(std::ios_base& ios)
235 {
236     (set_autoprefix)(ios, autoprefix_engineering);
237     return ios;
238 }
239 
240 /// Set flag for binary prefix, so 1024 byte displays as "1 Kib".
binary_prefix(std::ios_base & ios)241 inline std::ios_base& binary_prefix(std::ios_base& ios)
242 {
243     (set_autoprefix)(ios, 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>
exponent_string(const static_rational<N,D> & r)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<>
exponent_string(const static_rational<1> &)258 inline std::string exponent_string(const static_rational<1>&)
259 {
260     return "";
261 }
262 
263 template<class T>
base_unit_symbol_string(const 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>
base_unit_name_string(const 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;
valueboost::units::detail::symbol_string_impl::apply283         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     {
valueboost::units::detail::symbol_string_impl::apply297         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     {
valueboost::units::detail::symbol_string_impl::apply310         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     {
valueboost::units::detail::scale_symbol_string_impl::apply324         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     {
valueboost::units::detail::scale_symbol_string_impl::apply338         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;
valueboost::units::detail::name_string_impl::apply350         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     {
valueboost::units::detail::name_string_impl::apply364         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     {
valueboost::units::detail::name_string_impl::apply377         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     {
valueboost::units::detail::scale_name_string_impl::apply390         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     {
valueboost::units::detail::scale_name_string_impl::apply404         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
to_string_impl(const unit<Dimension,System> &,SubFormatter f)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
to_string_impl(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<Units,Dimension,dimensionless_type>>> &,Subformatter f)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
to_string_impl(const unit<dimensionless_type,heterogeneous_system<heterogeneous_system_impl<dimensionless_type,dimensionless_type,dimensionless_type>>> &,Subformatter)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
to_string_impl(const unit<dimensionless_type,heterogeneous_system<heterogeneous_system_impl<dimensionless_type,dimensionless_type,Scale>>> &,Subformatter f)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
to_string_impl(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<Units,Dimension,Scale>>> &,Subformatter f)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
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)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
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)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
to_string_impl(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<scaled_base_unit<Unit,UnitScale>,static_rational<1>>,dimensionless_type>,Dimension,Scale>>> &,Subformatter f,typename base_unit_info<scaled_base_unit<Unit,UnitScale>>::base_unit_info_primary_template * =0)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
to_string_impl(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<scaled_base_unit<Unit,UnitScale>,static_rational<1>>,dimensionless_type>,Dimension,dimensionless_type>>> &,Subformatter f,typename base_unit_info<scaled_base_unit<Unit,UnitScale>>::base_unit_info_primary_template * =0)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>
append_units_toboost::units::detail::format_raw_symbol_impl575     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>
append_scale_toboost::units::detail::format_raw_symbol_impl579     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>
operator ()boost::units::detail::format_raw_symbol_impl583     std::string operator()(const Unit& u) {
584         return(to_string_impl(u, *this));
585     }
586     template<class Unit>
is_default_stringboost::units::detail::format_raw_symbol_impl587     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>
operator ()boost::units::detail::format_symbol_impl594     std::string operator()(const Unit& u) {
595         return(symbol_string(u));
596     }
597     template<class Unit>
is_default_stringboost::units::detail::format_symbol_impl598     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>
append_units_toboost::units::detail::format_raw_name_impl605     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>
append_scale_toboost::units::detail::format_raw_name_impl609     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>
operator ()boost::units::detail::format_raw_name_impl613     std::string operator()(const Unit& u) {
614         return(to_string_impl(u, *this));
615     }
616     template<class Unit>
is_default_stringboost::units::detail::format_raw_name_impl617     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>
operator ()boost::units::detail::format_name_impl624     std::string operator()(const Unit& u) {
625         return(name_string(u));
626     }
627     template<class Unit>
is_default_stringboost::units::detail::format_name_impl628     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>
do_print(std::basic_ostream<Char,Traits> & os,const std::string & s)634 inline void do_print(std::basic_ostream<Char, Traits>& os, const std::string& s)
635 {
636     os << s.c_str();
637 }
638 
do_print(std::ostream & os,const std::string & s)639 inline void do_print(std::ostream& os, const std::string& s)
640 {
641     os << s;
642 }
643 
644 template<class Char, class Traits>
do_print(std::basic_ostream<Char,Traits> & os,const char * s)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;
callboost::units::autoprefix_norm_impl671     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;
callboost::units::autoprefix_norm_impl678     static one call(const T&) { return one(); }
679 };
680 
681 template<class T>
autoprefix_norm(const T & arg)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>
find_matching_scale_impl(End,End,Prev,T,double,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>
find_matching_scale_impl(Begin,End end,Prev prev,T t,double x,F 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>
find_matching_scale_i(End,End,T,double,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>
find_matching_scale_i(Begin,End end,T t,double x,F 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>
find_matching_scale(T t,double x,F 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;
operator ()boost::units::detail::print_default_t771     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>
print_default(Os & os,const Quantity & q)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>
operator ()boost::units::detail::print_scale_t790     void operator()(Prefix, const T& t) const
791     {
792         *prefixed = true;
793         *os << t / Prefix::value() << ' ';
794         switch(units::get_format(*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>
operator ()boost::units::detail::print_scale_t802     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>
print_scale(Os & os,bool & prefixed)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
maybe_parenthesize(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<Units,Dimension,Scale>>> &,Subformatter f)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
maybe_parenthesize(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit,static_rational<1>>,dimensionless_type>,Dimension,Scale>>> &,Subformatter f)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>
do_print_prefixed_impl(std::basic_ostream<CharT,Traits> & os,const quantity<Unit,T> & q,F default_)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(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
do_print_prefixed(std::basic_ostream<CharT,Traits> & os,const quantity<unit<Dimension,heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<scaled_base_unit<BaseUnit,BaseScale>,static_rational<1>>,dimensionless_type>,Dimension,Scale>>>,T> & q)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>
do_print_prefixed(std::basic_ostream<CharT,Traits> & os,const quantity<unit<Dimension,heterogeneous_system<heterogeneous_system_impl<L,Dimension,Scale>>>,T> & q)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>
do_print_prefixed(std::basic_ostream<CharT,Traits> & os,const quantity<unit<Dimension,System>,T> & q)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>
do_print_prefixed(std::basic_ostream<CharT,Traits> & os,const quantity<Unit,T> & q)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>
maybe_print_prefixed(std::basic_ostream<CharT,Traits> & os,const quantity<Unit,T> & q,mpl::true_)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>
maybe_print_prefixed(std::basic_ostream<CharT,Traits> & os,const quantity<Unit,T> & q,mpl::false_)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 
test_norm(double)978 inline mpl::true_ test_norm(double) { return mpl::true_(); }
test_norm(one)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
typename_string(const unit<Dimension,System> &)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
symbol_string(const unit<Dimension,System> &)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
name_string(const unit<Dimension,System> &)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>
operator <<(std::basic_ostream<Char,Traits> & os,const unit<Dimension,System> & u)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(os) == typename_fmt)
1014     {
1015         detail::do_print(os, typename_string(u));
1016     }
1017     else if (units::get_format(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(os) == symbol_fmt)
1022     {
1023         detail::do_print(os, symbol_string(u));
1024     }
1025     else if (units::get_format(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>
operator <<(std::basic_ostream<Char,Traits> & os,const quantity<Unit,T> & q)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(os) == autoprefix_none)
1047     {
1048         os << q.value() << ' ' << Unit();
1049     }
1050     else if (units::get_autoprefix(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(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