| 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/core/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(t: 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(t: N) + '/' + to_string(t: 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(ix: 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(ix: 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, mask: 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_flags: new_mode, mask: 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, new_mode: 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, new_mode: 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, new_mode: 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, new_mode: 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, mask: 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_flags: new_mode, mask: autoprefix_mask); |
| 225 | } |
| 226 | |
| 227 | /// Clear autoprefix flags. |
| 228 | inline std::ios_base& no_prefix(std::ios_base& ios) |
| 229 | { |
| 230 | (set_autoprefix)(ios, new_mode: 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, new_mode: 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, new_mode: 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 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; |
| 679 | static BOOST_CONSTEXPR one call(const T&) { return one(); } |
| 680 | }; |
| 681 | |
| 682 | template<class T> |
| 683 | BOOST_CONSTEXPR |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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; |
| 778 | 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> |
| 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> |
| 797 | void operator()(Prefix, const T& t) const |
| 798 | { |
| 799 | *prefixed = true; |
| 800 | *os << t / Prefix::value() << ' '; |
| 801 | switch(units::get_format(ios&: *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> |
| 809 | 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> |
| 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 |
| 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 |
| 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> |
| 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(ios&: 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 |
| 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> |
| 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> |
| 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> |
| 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> |
| 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> |
| 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 | |
| 985 | inline BOOST_CONSTEXPR mpl::true_ test_norm(double) { return mpl::true_(); } |
| 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 |
| 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 |
| 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 |
| 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> |
| 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(ios&: os) == typename_fmt) |
| 1021 | { |
| 1022 | detail::do_print(os, typename_string(u)); |
| 1023 | } |
| 1024 | else if (units::get_format(ios&: 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(ios&: os) == symbol_fmt) |
| 1029 | { |
| 1030 | detail::do_print(os, symbol_string(u)); |
| 1031 | } |
| 1032 | else if (units::get_format(ios&: 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> |
| 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(ios&: os) == autoprefix_none) |
| 1054 | { |
| 1055 | os << q.value() << ' ' << Unit(); |
| 1056 | } |
| 1057 | else if (units::get_autoprefix(ios&: 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(ios&: 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 | |