// Boost.Units - A C++ library for zero-overhead dimensional analysis and // unit/quantity manipulation and conversion // // Copyright (C) 2003-2008 Matthias Christian Schabel // Copyright (C) 2007-2010 Steven Watanabe // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_UNITS_IO_HPP #define BOOST_UNITS_IO_HPP /// \file /// \brief Stream input and output for rationals, units and quantities. /// \details Functions and manipulators for output and input of units and quantities. /// symbol and name format, and engineering and binary autoprefix. /// Serialization output is also supported. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace serialization { /// Boost Serialization library support for units. template inline void serialize(Archive& ar,boost::units::unit&,const unsigned int /*version*/) { } /// Boost Serialization library support for quantities. template inline void serialize(Archive& ar,boost::units::quantity& q,const unsigned int /*version*/) { ar & boost::serialization::make_nvp("value", units::quantity_cast(q)); } } // namespace serialization namespace units { // get string representation of arbitrary type. template std::string to_string(const T& t) { std::stringstream sstr; sstr << t; return sstr.str(); } /// get string representation of integral-valued @c static_rational. template std::string to_string(const static_rational&) { return to_string(N); } /// get string representation of @c static_rational. template std::string to_string(const static_rational&) { return '(' + to_string(N) + '/' + to_string(D) + ')'; } /// Write @c static_rational to @c std::basic_ostream. template inline std::basic_ostream& operator<<(std::basic_ostream& os,const static_rational& r) { os << to_string(r); return os; } /// traits template for unit names. template struct base_unit_info { /// INTERNAL ONLY typedef void base_unit_info_primary_template; /// The full name of the unit (returns BaseUnit::name() by default) static std::string name() { return(BaseUnit::name()); } /// The symbol for the base unit (Returns BaseUnit::symbol() by default) static std::string symbol() { return(BaseUnit::symbol()); /// \returns BaseUnit::symbol(), for example "m" } }; /// \enum format_mode format of output of units, for example "m" or "meter". enum format_mode { symbol_fmt = 0, /// default - reduces unit names to known symbols for both base and derived units. name_fmt = 1, /// output full unit names for base and derived units, for example "meter". raw_fmt = 2, /// output only symbols for base units (but not derived units), for example "m". typename_fmt = 3, /// output demangled typenames (useful only for diagnosis). fmt_mask = 3 /// Bits used for format. }; /// \enum autoprefix_mode automatic scaling and prefix (controlled by value of quantity) a, if any, enum autoprefix_mode { autoprefix_none = 0, /// No automatic prefix. autoprefix_engineering = 4, /// Scale and prefix with 10^3 multiples, 1234.5 m output as 1.2345 km. autoprefix_binary = 8, /// Scale and prefix with 2^10 (1024) multiples, 1024 as 1 kb. autoprefix_mask = 12 /// Bits used for autoprefix. }; namespace detail { template struct xalloc_key_holder { static int value; static bool initialized; }; template int xalloc_key_holder::value = 0; template bool xalloc_key_holder::initialized = 0; struct xalloc_key_initializer_t { xalloc_key_initializer_t() { if (!xalloc_key_holder::initialized) { xalloc_key_holder::value = std::ios_base::xalloc(); xalloc_key_holder::initialized = true; } } }; namespace /**/ { xalloc_key_initializer_t xalloc_key_initializer; } // namespace } // namespace detail /// returns flags controlling output. inline long get_flags(std::ios_base& ios, long mask) { return(ios.iword(detail::xalloc_key_holder::value) & mask); } /// Set new flags controlling output format. inline void set_flags(std::ios_base& ios, long new_flags, long mask) { assert((~mask & new_flags) == 0); long& flags = ios.iword(detail::xalloc_key_holder::value); flags = (flags & ~mask) | new_flags; } /// returns flags controlling output format. inline format_mode get_format(std::ios_base& ios) { return(static_cast((get_flags)(ios, fmt_mask))); } /// Set new flags controlling output format. inline void set_format(std::ios_base& ios, format_mode new_mode) { (set_flags)(ios, new_mode, fmt_mask); } /// Set new flags for type_name output format. inline std::ios_base& typename_format(std::ios_base& ios) { (set_format)(ios, typename_fmt); return(ios); } /// set new flag for raw format output, for example "m". inline std::ios_base& raw_format(std::ios_base& ios) { (set_format)(ios, raw_fmt); return(ios); } /// set new format flag for symbol output, for example "m". inline std::ios_base& symbol_format(std::ios_base& ios) { (set_format)(ios, symbol_fmt); return(ios); } /// set new format for name output, for example "meter". inline std::ios_base& name_format(std::ios_base& ios) { (set_format)(ios, name_fmt); return(ios); } /// get autoprefix flags for output. inline autoprefix_mode get_autoprefix(std::ios_base& ios) { return static_cast((get_flags)(ios, autoprefix_mask)); } /// Get format for output. inline void set_autoprefix(std::ios_base& ios, autoprefix_mode new_mode) { (set_flags)(ios, new_mode, autoprefix_mask); } /// Clear autoprefix flags. inline std::ios_base& no_prefix(std::ios_base& ios) { (set_autoprefix)(ios, autoprefix_none); return ios; } /// Set flag for engineering prefix, so 1234.5 m displays as "1.2345 km". inline std::ios_base& engineering_prefix(std::ios_base& ios) { (set_autoprefix)(ios, autoprefix_engineering); return ios; } /// Set flag for binary prefix, so 1024 byte displays as "1 Kib". inline std::ios_base& binary_prefix(std::ios_base& ios) { (set_autoprefix)(ios, autoprefix_binary); return ios; } namespace detail { /// \return exponent string like "^1/2". template inline std::string exponent_string(const static_rational& r) { return '^' + to_string(r); } /// \return empty exponent string for integer rational like 2. template<> inline std::string exponent_string(const static_rational<1>&) { return ""; } template inline std::string base_unit_symbol_string(const T&) { return base_unit_info::symbol() + exponent_string(typename T::value_type()); } template inline std::string base_unit_name_string(const T&) { return base_unit_info::name() + exponent_string(typename T::value_type()); } // stringify with symbols. template struct symbol_string_impl { template struct apply { typedef typename symbol_string_impl::template apply next; static void value(std::string& str) { str += base_unit_symbol_string(typename Begin::item()) + ' '; next::value(str); } }; }; template<> struct symbol_string_impl<1> { template struct apply { static void value(std::string& str) { str += base_unit_symbol_string(typename Begin::item()); }; }; }; template<> struct symbol_string_impl<0> { template struct apply { static void value(std::string& str) { // better shorthand for dimensionless? str += "dimensionless"; } }; }; template struct scale_symbol_string_impl { template struct apply { static void value(std::string& str) { str += Begin::item::symbol(); scale_symbol_string_impl::template apply::value(str); } }; }; template<> struct scale_symbol_string_impl<0> { template struct apply { static void value(std::string&) { } }; }; // stringify with names. template struct name_string_impl { template struct apply { typedef typename name_string_impl::template apply next; static void value(std::string& str) { str += base_unit_name_string(typename Begin::item()) + ' '; next::value(str); } }; }; template<> struct name_string_impl<1> { template struct apply { static void value(std::string& str) { str += base_unit_name_string(typename Begin::item()); }; }; }; template<> struct name_string_impl<0> { template struct apply { static void value(std::string& str) { str += "dimensionless"; } }; }; template struct scale_name_string_impl { template struct apply { static void value(std::string& str) { str += Begin::item::name(); scale_name_string_impl::template apply::value(str); } }; }; template<> struct scale_name_string_impl<0> { template struct apply { static void value(std::string&) { } }; }; } // namespace detail namespace detail { // These two overloads of symbol_string and name_string will // will pick up homogeneous_systems. They simply call the // appropriate function with a heterogeneous_system. template inline std::string to_string_impl(const unit&, SubFormatter f) { return f(typename reduce_unit >::type()); } /// INTERNAL ONLY // this overload picks up heterogeneous units that are not scaled. template inline std::string to_string_impl(const unit > >&, Subformatter f) { std::string str; f.template append_units_to(str); return(str); } // This overload is a special case for heterogeneous_system which // is really unitless /// INTERNAL ONLY template inline std::string to_string_impl(const unit > >&, Subformatter) { return("dimensionless"); } // this overload deals with heterogeneous_systems which are unitless // but scaled. /// INTERNAL ONLY template inline std::string to_string_impl(const unit > >&, Subformatter f) { std::string str; f.template append_scale_to(str); return(str); } // this overload deals with scaled units. /// INTERNAL ONLY template inline std::string to_string_impl(const unit > >&, Subformatter f) { std::string str; f.template append_scale_to(str); std::string without_scale = f(unit > >()); if (f.is_default_string(without_scale, unit > >())) { str += "("; str += without_scale; str += ")"; } else { str += without_scale; } return(str); } // This overload catches scaled units that have a single base unit // raised to the first power. It causes si::nano * si::meters to not // put parentheses around the meters. i.e. nm rather than n(m) /// INTERNAL ONLY template inline std::string to_string_impl(const unit >,dimensionless_type>, Dimension, Scale> > >&, Subformatter f) { std::string str; f.template append_scale_to(str); str += f(unit >, dimensionless_type>, Dimension, dimensionless_type> > >()); return(str); } // This overload is necessary to disambiguate. // it catches units that are unscaled and have a single // base unit raised to the first power. It is treated the // same as any other unscaled unit. /// INTERNAL ONLY template inline std::string to_string_impl(const unit >,dimensionless_type>, Dimension, dimensionless_type> > >&, Subformatter f) { std::string str; f.template append_units_to >,dimensionless_type> >(str); return(str); } // This overload catches scaled units that have a single scaled base unit // raised to the first power. It moves that scaling on the base unit // to the unit level scaling and recurses. By doing this we make sure that // si::milli * si::kilograms will print g rather than mkg. // // This transformation will not be applied if base_unit_info is specialized // for the scaled base unit. // /// INTERNAL ONLY template inline std::string to_string_impl( const unit< Dimension, heterogeneous_system< heterogeneous_system_impl< list, static_rational<1> >, dimensionless_type>, Dimension, Scale > > >&, Subformatter f, typename base_unit_info >::base_unit_info_primary_template* = 0) { return(f( unit< Dimension, heterogeneous_system< heterogeneous_system_impl< list >, dimensionless_type>, Dimension, typename mpl::times, dimensionless_type> >::type > > >())); } // this overload disambuguates between the overload for an unscaled unit // and the overload for a scaled base unit raised to the first power. /// INTERNAL ONLY template inline std::string to_string_impl( const unit< Dimension, heterogeneous_system< heterogeneous_system_impl< list, static_rational<1> >, dimensionless_type>, Dimension, dimensionless_type > > >&, Subformatter f, typename base_unit_info >::base_unit_info_primary_template* = 0) { std::string str; f.template append_units_to, static_rational<1> >, dimensionless_type> >(str); return(str); } struct format_raw_symbol_impl { template void append_units_to(std::string& str) { detail::symbol_string_impl::template apply::value(str); } template void append_scale_to(std::string& str) { detail::scale_symbol_string_impl::template apply::value(str); } template std::string operator()(const Unit& u) { return(to_string_impl(u, *this)); } template bool is_default_string(const std::string&, const Unit&) { return(true); } }; struct format_symbol_impl : format_raw_symbol_impl { template std::string operator()(const Unit& u) { return(symbol_string(u)); } template bool is_default_string(const std::string& str, const Unit& u) { return(str == to_string_impl(u, format_raw_symbol_impl())); } }; struct format_raw_name_impl { template void append_units_to(std::string& str) { detail::name_string_impl<(Units::size::value)>::template apply::value(str); } template void append_scale_to(std::string& str) { detail::scale_name_string_impl::template apply::value(str); } template std::string operator()(const Unit& u) { return(to_string_impl(u, *this)); } template bool is_default_string(const std::string&, const Unit&) { return(true); } }; struct format_name_impl : format_raw_name_impl { template std::string operator()(const Unit& u) { return(name_string(u)); } template bool is_default_string(const std::string& str, const Unit& u) { return(str == to_string_impl(u, format_raw_name_impl())); } }; template inline void do_print(std::basic_ostream& os, const std::string& s) { os << s.c_str(); } inline void do_print(std::ostream& os, const std::string& s) { os << s; } template inline void do_print(std::basic_ostream& os, const char* s) { os << s; } // For automatically applying the appropriate prefixes. } #ifdef BOOST_UNITS_DOXYGEN /// ADL customization point for automatic prefixing. /// Returns a non-negative value. Implemented as std::abs /// for built-in types. template double autoprefix_norm(const T& arg); #else template::value> struct autoprefix_norm_impl; template struct autoprefix_norm_impl { typedef double type; static double call(const T& arg) { return std::abs(arg); } }; template struct autoprefix_norm_impl { typedef one type; static one call(const T&) { return one(); } }; template typename autoprefix_norm_impl::type autoprefix_norm(const T& arg) { return autoprefix_norm_impl::call(arg); } #endif namespace detail { template bool find_matching_scale_impl(End, End, Prev, T, double, F) { return false; } template bool find_matching_scale_impl(Begin, End end, Prev prev, T t, double x, F f) { if(Begin::item::value() > x) { f(prev, t); return true; } else { return detail::find_matching_scale_impl( typename Begin::next(), end, typename Begin::item(), t, x, f ); } } template bool find_matching_scale_i(End, End, T, double, F) { return false; } template bool find_matching_scale_i(Begin, End end, T t, double x, F f) { if(Begin::item::value() > x) { return false; } else { return detail::find_matching_scale_impl(typename Begin::next(), end, typename Begin::item(), t, x, f); } } template bool find_matching_scale(T t, double x, F f) { return detail::find_matching_scale_i(Scales(), dimensionless_type(), t, x, f); } typedef list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, list >, dimensionless_type> > > > > > > > > > > > > > > > > > engineering_prefixes; typedef list >, list >, list >, list >, list >, list >, list >, dimensionless_type> > > > > > > binary_prefixes; template struct print_default_t { typedef void result_type; void operator()() const { *os << q->value() << ' ' << typename Quantity::unit_type(); } Os* os; const Quantity* q; }; template print_default_t print_default(Os& os, const Quantity& q) { print_default_t result = { &os, &q }; return result; } template struct print_scale_t { typedef void result_type; template void operator()(Prefix, const T& t) const { *prefixed = true; *os << t / Prefix::value() << ' '; switch(units::get_format(*os)) { case name_fmt: do_print(*os, Prefix::name()); break; case raw_fmt: case symbol_fmt: do_print(*os, Prefix::symbol()); break; case typename_fmt: do_print(*os, units::simplify_typename(Prefix())); *os << ' '; break; } } template void operator()(scale >, const T& t) const { *prefixed = false; *os << t << ' '; } Os* os; bool* prefixed; }; template print_scale_t print_scale(Os& os, bool& prefixed) { print_scale_t result = { &os, &prefixed }; return result; } // puts parentheses around a unit /// INTERNAL ONLY template inline std::string maybe_parenthesize(const unit > >&, Subformatter f) { std::string str; std::string without_scale = f(unit > >()); if (f.is_default_string(without_scale, unit > >())) { str += "("; str += without_scale; str += ")"; } else { str += without_scale; } return(str); } // This overload catches scaled units that have a single base unit // raised to the first power. It causes si::nano * si::meters to not // put parentheses around the meters. i.e. nm rather than n(m) /// INTERNAL ONLY template inline std::string maybe_parenthesize(const unit >,dimensionless_type>, Dimension, Scale> > >&, Subformatter f) { return f(unit >, dimensionless_type>, Dimension, dimensionless_type> > >()); } template void do_print_prefixed_impl(std::basic_ostream& os, const quantity& q, F default_) { bool prefixed; if(detail::find_matching_scale(q.value(), autoprefix_norm(q.value()), detail::print_scale(os, prefixed))) { if(prefixed) { switch(units::get_format(os)) { case symbol_fmt: do_print(os, maybe_parenthesize(Unit(), format_symbol_impl())); break; case raw_fmt: do_print(os, maybe_parenthesize(Unit(), format_raw_symbol_impl())); break; case name_fmt: do_print(os, maybe_parenthesize(Unit(), format_name_impl())); break; case typename_fmt: do_print(os, simplify_typename(Unit())); break; } } else { os << Unit(); } } else { default_(); } } // Handle units like si::kilograms that have a scale embedded in the // base unit. This overload is disabled if the scaled base unit has // a user-defined string representation. template typename base_unit_info< scaled_base_unit >::base_unit_info_primary_template do_print_prefixed( std::basic_ostream& os, const quantity< unit< Dimension, heterogeneous_system< heterogeneous_system_impl< list< heterogeneous_system_dim< scaled_base_unit, static_rational<1> >, dimensionless_type >, Dimension, Scale > > >, T >& q) { quantity< unit< Dimension, heterogeneous_system< heterogeneous_system_impl< list< heterogeneous_system_dim >, dimensionless_type >, Dimension, dimensionless_type > > >, T > unscaled(q); detail::do_print_prefixed_impl(os, unscaled, detail::print_default(os, q)); } template void do_print_prefixed( std::basic_ostream& os, const quantity< unit< Dimension, heterogeneous_system< heterogeneous_system_impl< L, Dimension, Scale > > >, T >& q) { quantity< unit< Dimension, heterogeneous_system< heterogeneous_system_impl< L, Dimension, dimensionless_type > > >, T > unscaled(q); detail::do_print_prefixed_impl(os, unscaled, detail::print_default(os, q)); } template void do_print_prefixed(std::basic_ostream& os, const quantity, T>& q) { detail::do_print_prefixed(os, quantity::type>, T>(q)); } template void do_print_prefixed(std::basic_ostream& os, const quantity& q) { detail::print_default(os, q)(); } template void maybe_print_prefixed(std::basic_ostream& os, const quantity& q, mpl::true_) { detail::do_print_prefixed(os, q); } template void maybe_print_prefixed(std::basic_ostream& os, const quantity& q, mpl::false_) { detail::print_default(os, q)(); } inline mpl::true_ test_norm(double) { return mpl::true_(); } inline mpl::false_ test_norm(one) { return mpl::false_(); } } // namespace detail template inline std::string typename_string(const unit&) { return simplify_typename(typename reduce_unit< unit >::type()); } template inline std::string symbol_string(const unit&) { return detail::to_string_impl(unit(), detail::format_symbol_impl()); } template inline std::string name_string(const unit&) { return detail::to_string_impl(unit(), detail::format_name_impl()); } /// Print a @c unit as a list of base units and their exponents. /// /// for @c symbol_format outputs e.g. "m s^-1" or "J". /// for @c name_format outputs e.g. "meter second^-1" or "joule". /// for @c raw_format outputs e.g. "m s^-1" or "meter kilogram^2 second^-2". /// for @c typename_format outputs the typename itself (currently demangled only on GCC). template inline std::basic_ostream& operator<<(std::basic_ostream& os, const unit& u) { if (units::get_format(os) == typename_fmt) { detail::do_print(os, typename_string(u)); } else if (units::get_format(os) == raw_fmt) { detail::do_print(os, detail::to_string_impl(u, detail::format_raw_symbol_impl())); } else if (units::get_format(os) == symbol_fmt) { detail::do_print(os, symbol_string(u)); } else if (units::get_format(os) == name_fmt) { detail::do_print(os, name_string(u)); } else { assert(!"The format mode must be one of: typename_format, raw_format, name_format, symbol_format"); } return(os); } /// \brief Print a @c quantity. /// \details Prints the value followed by the unit. /// If the engineering_prefix, or binary_prefix is set, /// tries to scale the value appropriately. /// For example, it might print 12.345 km instead of 12345 m. /// (Note does @b not attempt to automatically scale scalars like double, float...) template inline std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { if (units::get_autoprefix(os) == autoprefix_none) { os << q.value() << ' ' << Unit(); } else if (units::get_autoprefix(os) == autoprefix_engineering) { detail::maybe_print_prefixed(os, q, detail::test_norm(autoprefix_norm(q.value()))); } else if (units::get_autoprefix(os) == autoprefix_binary) { detail::maybe_print_prefixed(os, q, detail::test_norm(autoprefix_norm(q.value()))); } else { assert(!"Autoprefixing must be one of: no_prefix, engineering_prefix, binary_prefix"); } return(os); } } // namespace units } // namespace boost #endif