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>
serialize(Archive &,boost::units::unit<Dim,System> &,const unsigned int)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>
serialize(Archive & ar,boost::units::quantity<Unit,Y> & q,const unsigned int)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.
to_string(const T & t)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.
to_string(const static_rational<N> &)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.
to_string(const static_rational<N,D> &)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>
operator <<(std::basic_ostream<Char,Traits> & os,const static_rational<N,D> & r)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)
nameboost::units::base_unit_info96     static std::string name()
97     {
98         return(BaseUnit::name());
99     }
100     /// The symbol for the base unit (Returns BaseUnit::symbol() by default)
symbolboost::units::base_unit_info101     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 {
xalloc_key_initializer_tboost::units::detail::xalloc_key_initializer_t143     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.
get_flags(std::ios_base & ios,long mask)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.
set_flags(std::ios_base & ios,long new_flags,long mask)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.
get_format(std::ios_base & ios)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.
set_format(std::ios_base & ios,format_mode new_mode)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.
typename_format(std::ios_base & ios)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".
raw_format(std::ios_base & ios)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".
symbol_format(std::ios_base & ios)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".
name_format(std::ios_base & ios)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.
get_autoprefix(std::ios_base & ios)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.
set_autoprefix(std::ios_base & ios,autoprefix_mode new_mode)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.
no_prefix(std::ios_base & ios)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".
engineering_prefix(std::ios_base & ios)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".
binary_prefix(std::ios_base & ios)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>
exponent_string(const static_rational<N,D> & r)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<>
exponent_string(const static_rational<1> &)259 inline std::string exponent_string(const static_rational<1>&)
260 {
261     return "";
262 }
263 
264 template<class T>
base_unit_symbol_string(const 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>
base_unit_name_string(const 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;
valueboost::units::detail::symbol_string_impl::apply284         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     {
valueboost::units::detail::symbol_string_impl::apply298         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     {
valueboost::units::detail::symbol_string_impl::apply311         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     {
valueboost::units::detail::scale_symbol_string_impl::apply325         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     {
valueboost::units::detail::scale_symbol_string_impl::apply339         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;
valueboost::units::detail::name_string_impl::apply351         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     {
valueboost::units::detail::name_string_impl::apply365         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     {
valueboost::units::detail::name_string_impl::apply378         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     {
valueboost::units::detail::scale_name_string_impl::apply391         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     {
valueboost::units::detail::scale_name_string_impl::apply405         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
to_string_impl(const unit<Dimension,System> &,SubFormatter f)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
to_string_impl(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<Units,Dimension,dimensionless_type>>> &,Subformatter f)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
to_string_impl(const unit<dimensionless_type,heterogeneous_system<heterogeneous_system_impl<dimensionless_type,dimensionless_type,dimensionless_type>>> &,Subformatter)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
to_string_impl(const unit<dimensionless_type,heterogeneous_system<heterogeneous_system_impl<dimensionless_type,dimensionless_type,Scale>>> &,Subformatter f)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
to_string_impl(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<Units,Dimension,Scale>>> &,Subformatter f)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
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 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
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 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
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)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
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)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>
append_units_toboost::units::detail::format_raw_symbol_impl576     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>
append_scale_toboost::units::detail::format_raw_symbol_impl580     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>
operator ()boost::units::detail::format_raw_symbol_impl584     std::string operator()(const Unit& u) {
585         return(to_string_impl(u, *this));
586     }
587     template<class Unit>
is_default_stringboost::units::detail::format_raw_symbol_impl588     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>
operator ()boost::units::detail::format_symbol_impl595     std::string operator()(const Unit& u) {
596         return(symbol_string(u));
597     }
598     template<class Unit>
is_default_stringboost::units::detail::format_symbol_impl599     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>
append_units_toboost::units::detail::format_raw_name_impl606     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>
append_scale_toboost::units::detail::format_raw_name_impl610     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>
operator ()boost::units::detail::format_raw_name_impl614     std::string operator()(const Unit& u) {
615         return(to_string_impl(u, *this));
616     }
617     template<class Unit>
is_default_stringboost::units::detail::format_raw_name_impl618     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>
operator ()boost::units::detail::format_name_impl625     std::string operator()(const Unit& u) {
626         return(name_string(u));
627     }
628     template<class Unit>
is_default_stringboost::units::detail::format_name_impl629     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>
do_print(std::basic_ostream<Char,Traits> & os,const std::string & s)635 inline void do_print(std::basic_ostream<Char, Traits>& os, const std::string& s)
636 {
637     os << s.c_str();
638 }
639 
do_print(std::ostream & os,const std::string & s)640 inline void do_print(std::ostream& os, const std::string& s)
641 {
642     os << s;
643 }
644 
645 template<class Char, class Traits>
do_print(std::basic_ostream<Char,Traits> & os,const char * s)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;
callboost::units::autoprefix_norm_impl672     static BOOST_CONSTEXPR 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;
callboost::units::autoprefix_norm_impl679     static BOOST_CONSTEXPR one call(const T&) { return one(); }
680 };
681 
682 template<class T>
683 BOOST_CONSTEXPR
autoprefix_norm(const T & arg)684 typename autoprefix_norm_impl<T>::type autoprefix_norm(const T& arg)
685 {
686     return autoprefix_norm_impl<T>::call(arg);
687 }
688 
689 #endif
690 
691 namespace detail {
692 
693 template<class End, class Prev, class T, class F>
694 BOOST_CONSTEXPR
find_matching_scale_impl(End,End,Prev,T,double,F)695 bool find_matching_scale_impl(End, End, Prev, T, double, F)
696 {
697     return false;
698 }
699 
700 template<class Begin, class End, class Prev, class T, class F>
701 BOOST_CXX14_CONSTEXPR
find_matching_scale_impl(Begin,End end,Prev prev,T t,double x,F f)702 bool find_matching_scale_impl(Begin, End end, Prev prev, T t, double x, F f)
703 {
704     if(Begin::item::value() > x) {
705         f(prev, t);
706         return true;
707     } else {
708         return detail::find_matching_scale_impl(
709             typename Begin::next(),
710             end,
711             typename Begin::item(),
712             t,
713             x,
714             f
715         );
716     }
717 }
718 
719 template<class End, class T, class F>
720 BOOST_CONSTEXPR
find_matching_scale_i(End,End,T,double,F)721 bool find_matching_scale_i(End, End, T, double, F)
722 {
723     return false;
724 }
725 
726 template<class Begin, class End, class T, class F>
727 BOOST_CXX14_CONSTEXPR
find_matching_scale_i(Begin,End end,T t,double x,F f)728 bool find_matching_scale_i(Begin, End end, T t, double x, F f)
729 {
730     if(Begin::item::value() > x) {
731         return false;
732     } else {
733         return detail::find_matching_scale_impl(typename Begin::next(), end, typename Begin::item(), t, x, f);
734     }
735 }
736 
737 template<class Scales, class T, class F>
738 BOOST_CXX14_CONSTEXPR
find_matching_scale(T t,double x,F f)739 bool find_matching_scale(T t, double x, F f)
740 {
741     return detail::find_matching_scale_i(Scales(), dimensionless_type(), t, x, f);
742 }
743 
744 typedef list<scale<10, static_rational<-24> >,
745         list<scale<10, static_rational<-21> >,
746         list<scale<10, static_rational<-18> >,
747         list<scale<10, static_rational<-15> >,
748         list<scale<10, static_rational<-12> >,
749         list<scale<10, static_rational<-9> >,
750         list<scale<10, static_rational<-6> >,
751         list<scale<10, static_rational<-3> >,
752         list<scale<10, static_rational<0> >,
753         list<scale<10, static_rational<3> >,
754         list<scale<10, static_rational<6> >,
755         list<scale<10, static_rational<9> >,
756         list<scale<10, static_rational<12> >,
757         list<scale<10, static_rational<15> >,
758         list<scale<10, static_rational<18> >,
759         list<scale<10, static_rational<21> >,
760         list<scale<10, static_rational<24> >,
761         list<scale<10, static_rational<27> >,
762         dimensionless_type> > > > > > > > > > > > > > > > > > engineering_prefixes;
763 
764 typedef list<scale<2, static_rational<10> >,
765         list<scale<2, static_rational<20> >,
766         list<scale<2, static_rational<30> >,
767         list<scale<2, static_rational<40> >,
768         list<scale<2, static_rational<50> >,
769         list<scale<2, static_rational<60> >,
770         list<scale<2, static_rational<70> >,
771         list<scale<2, static_rational<80> >,
772         list<scale<2, static_rational<90> >,
773         dimensionless_type> > > > > > > > > binary_prefixes;
774 
775 template<class Os, class Quantity>
776 struct print_default_t {
777     typedef void result_type;
operator ()boost::units::detail::print_default_t778     void operator()() const
779     {
780         *os << q->value() << ' ' << typename Quantity::unit_type();
781     }
782     Os* os;
783     const Quantity* q;
784 };
785 
786 template<class Os, class Quantity>
print_default(Os & os,const Quantity & q)787 print_default_t<Os, Quantity> print_default(Os& os, const Quantity& q)
788 {
789     print_default_t<Os, Quantity> result = { &os, &q };
790     return result;
791 }
792 
793 template<class Os>
794 struct print_scale_t {
795     typedef void result_type;
796     template<class Prefix, class T>
operator ()boost::units::detail::print_scale_t797     void operator()(Prefix, const T& t) const
798     {
799         *prefixed = true;
800         *os << t / Prefix::value() << ' ';
801         switch(units::get_format(*os)) {
802             case name_fmt: do_print(*os, Prefix::name()); break;
803             case raw_fmt:
804             case symbol_fmt: do_print(*os, Prefix::symbol()); break;
805             case typename_fmt: do_print(*os, units::simplify_typename(Prefix())); *os << ' '; break;
806         }
807     }
808     template<long N, class T>
operator ()boost::units::detail::print_scale_t809     void operator()(scale<N, static_rational<0> >, const T& t) const
810     {
811         *prefixed = false;
812         *os << t << ' ';
813     }
814     Os* os;
815     bool* prefixed;
816 };
817 
818 template<class Os>
print_scale(Os & os,bool & prefixed)819 print_scale_t<Os> print_scale(Os& os, bool& prefixed)
820 {
821     print_scale_t<Os> result = { &os, &prefixed };
822     return result;
823 }
824 
825 // puts parentheses around a unit
826 /// INTERNAL ONLY
827 template<class Dimension,class Units,class Scale, class Subformatter>
828 inline std::string
maybe_parenthesize(const unit<Dimension,heterogeneous_system<heterogeneous_system_impl<Units,Dimension,Scale>>> &,Subformatter f)829 maybe_parenthesize(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, Scale> > >&, Subformatter f)
830 {
831     std::string str;
832 
833     std::string without_scale = f(unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, dimensionless_type> > >());
834 
835     if (f.is_default_string(without_scale, unit<Dimension, heterogeneous_system<heterogeneous_system_impl<Units, Dimension, dimensionless_type> > >()))
836     {
837         str += "(";
838         str += without_scale;
839         str += ")";
840     }
841     else
842     {
843         str += without_scale;
844     }
845 
846     return(str);
847 }
848 
849 // This overload catches scaled units that have a single base unit
850 // raised to the first power.  It causes si::nano * si::meters to not
851 // put parentheses around the meters.  i.e. nm rather than n(m)
852 /// INTERNAL ONLY
853 template<class Dimension,class Unit,class Scale, class Subformatter>
854 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)855 maybe_parenthesize(const unit<Dimension, heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit, static_rational<1> >,dimensionless_type>, Dimension, Scale> > >&, Subformatter f)
856 {
857     return f(unit<Dimension, heterogeneous_system<heterogeneous_system_impl<list<heterogeneous_system_dim<Unit, static_rational<1> >, dimensionless_type>, Dimension, dimensionless_type> > >());
858 }
859 
860 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_)861 void do_print_prefixed_impl(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q, F default_)
862 {
863     bool prefixed;
864     if(detail::find_matching_scale<Prefixes>(q.value(), autoprefix_norm(q.value()), detail::print_scale(os, prefixed))) {
865         if(prefixed) {
866             switch(units::get_format(os)) {
867                 case symbol_fmt: do_print(os, maybe_parenthesize(Unit(), format_symbol_impl())); break;
868                 case raw_fmt: do_print(os, maybe_parenthesize(Unit(), format_raw_symbol_impl())); break;
869                 case name_fmt: do_print(os, maybe_parenthesize(Unit(), format_name_impl())); break;
870                 case typename_fmt: do_print(os, simplify_typename(Unit())); break;
871             }
872         } else {
873             os << Unit();
874         }
875     } else {
876         default_();
877     }
878 }
879 
880 // Handle units like si::kilograms that have a scale embedded in the
881 // base unit.  This overload is disabled if the scaled base unit has
882 // a user-defined string representation.
883 template<class Prefixes, class CharT, class Traits, class Dimension, class BaseUnit, class BaseScale, class Scale, class T>
884 typename base_unit_info<
885     scaled_base_unit<BaseUnit, Scale>
886 >::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)887 do_print_prefixed(
888     std::basic_ostream<CharT, Traits>& os,
889     const quantity<
890         unit<
891             Dimension,
892             heterogeneous_system<
893                 heterogeneous_system_impl<
894                     list<
895                         heterogeneous_system_dim<
896                             scaled_base_unit<BaseUnit, BaseScale>,
897                             static_rational<1>
898                         >,
899                         dimensionless_type
900                     >,
901                     Dimension,
902                     Scale
903                 >
904             >
905         >,
906         T
907     >& q)
908 {
909     quantity<
910         unit<
911             Dimension,
912             heterogeneous_system<
913                 heterogeneous_system_impl<
914                     list<
915                         heterogeneous_system_dim<BaseUnit, static_rational<1> >,
916                         dimensionless_type
917                     >,
918                     Dimension,
919                     dimensionless_type
920                 >
921             >
922         >,
923         T
924     > unscaled(q);
925     detail::do_print_prefixed_impl<Prefixes>(os, unscaled, detail::print_default(os, q));
926 }
927 
928 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)929 void do_print_prefixed(
930     std::basic_ostream<CharT, Traits>& os,
931     const quantity<
932         unit<
933             Dimension,
934             heterogeneous_system<
935                 heterogeneous_system_impl<
936                     L,
937                     Dimension,
938                     Scale
939                 >
940             >
941         >,
942         T
943     >& q)
944 {
945     quantity<
946         unit<
947             Dimension,
948             heterogeneous_system<
949                 heterogeneous_system_impl<
950                     L,
951                     Dimension,
952                     dimensionless_type
953                 >
954             >
955         >,
956         T
957     > unscaled(q);
958     detail::do_print_prefixed_impl<Prefixes>(os, unscaled, detail::print_default(os, q));
959 }
960 
961 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)962 void do_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<unit<Dimension, System>, T>& q)
963 {
964     detail::do_print_prefixed<Prefixes>(os, quantity<unit<Dimension, typename make_heterogeneous_system<Dimension, System>::type>, T>(q));
965 }
966 
967 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)968 void do_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q)
969 {
970     detail::print_default(os, q)();
971 }
972 
973 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_)974 void maybe_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q, mpl::true_)
975 {
976     detail::do_print_prefixed<Prefixes>(os, q);
977 }
978 
979 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_)980 void maybe_print_prefixed(std::basic_ostream<CharT, Traits>& os, const quantity<Unit, T>& q, mpl::false_)
981 {
982     detail::print_default(os, q)();
983 }
984 
test_norm(double)985 inline BOOST_CONSTEXPR mpl::true_ test_norm(double) { return mpl::true_(); }
test_norm(one)986 inline BOOST_CONSTEXPR mpl::false_ test_norm(one) { return mpl::false_(); }
987 
988 } // namespace detail
989 
990 template<class Dimension,class System>
991 inline std::string
typename_string(const unit<Dimension,System> &)992 typename_string(const unit<Dimension, System>&)
993 {
994     return simplify_typename(typename reduce_unit< unit<Dimension,System> >::type());
995 }
996 
997 template<class Dimension,class System>
998 inline std::string
symbol_string(const unit<Dimension,System> &)999 symbol_string(const unit<Dimension, System>&)
1000 {
1001     return detail::to_string_impl(unit<Dimension,System>(), detail::format_symbol_impl());
1002 }
1003 
1004 template<class Dimension,class System>
1005 inline std::string
name_string(const unit<Dimension,System> &)1006 name_string(const unit<Dimension, System>&)
1007 {
1008     return detail::to_string_impl(unit<Dimension,System>(), detail::format_name_impl());
1009 }
1010 
1011 /// Print a @c unit as a list of base units and their exponents.
1012 ///
1013 ///     for @c symbol_format outputs e.g. "m s^-1" or "J".
1014 ///     for @c name_format  outputs e.g. "meter second^-1" or "joule".
1015 ///     for @c raw_format  outputs e.g. "m s^-1" or "meter kilogram^2 second^-2".
1016 ///     for @c typename_format  outputs the typename itself (currently demangled only on GCC).
1017 template<class Char, class Traits, class Dimension, class System>
operator <<(std::basic_ostream<Char,Traits> & os,const unit<Dimension,System> & u)1018 inline std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, const unit<Dimension, System>& u)
1019 {
1020     if (units::get_format(os) == typename_fmt)
1021     {
1022         detail::do_print(os, typename_string(u));
1023     }
1024     else if (units::get_format(os) == raw_fmt)
1025     {
1026         detail::do_print(os, detail::to_string_impl(u, detail::format_raw_symbol_impl()));
1027     }
1028     else if (units::get_format(os) == symbol_fmt)
1029     {
1030         detail::do_print(os, symbol_string(u));
1031     }
1032     else if (units::get_format(os) == name_fmt)
1033     {
1034         detail::do_print(os, name_string(u));
1035     }
1036     else
1037     {
1038         BOOST_ASSERT_MSG(false, "The format mode must be one of: typename_format, raw_format, name_format, symbol_format");
1039     }
1040 
1041     return(os);
1042 }
1043 
1044 /// \brief Print a @c quantity.
1045 /// \details Prints the value followed by the unit.
1046 /// If the engineering_prefix, or binary_prefix is set,
1047 /// tries to scale the value appropriately.
1048 /// For example, it might print 12.345 km instead of 12345 m.
1049 /// (Note does @b not attempt to automatically scale scalars like double, float...)
1050 template<class Char, class Traits, class Unit, class T>
operator <<(std::basic_ostream<Char,Traits> & os,const quantity<Unit,T> & q)1051 inline std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, const quantity<Unit, T>& q)
1052 {
1053     if (units::get_autoprefix(os) == autoprefix_none)
1054     {
1055         os << q.value() << ' ' << Unit();
1056     }
1057     else if (units::get_autoprefix(os) == autoprefix_engineering)
1058     {
1059         detail::maybe_print_prefixed<detail::engineering_prefixes>(os, q, detail::test_norm(autoprefix_norm(q.value())));
1060     }
1061     else if (units::get_autoprefix(os) == autoprefix_binary)
1062     {
1063         detail::maybe_print_prefixed<detail::binary_prefixes>(os, q, detail::test_norm(autoprefix_norm(q.value())));
1064     }
1065     else
1066     {
1067         BOOST_ASSERT_MSG(false, "Autoprefixing must be one of: no_prefix, engineering_prefix, binary_prefix");
1068     }
1069     return(os);
1070 }
1071 
1072 } // namespace units
1073 
1074 } // namespace boost
1075 
1076 #endif
1077