/* Written by Calum Grant Copyright (C) Calum Grant 2007 Home page: http://calumgrant.net/units File location: http://calumgrant.net/units/units.hpp Copying permitted under the terms of the Boost software license. This library is distributed as a single file for convenience. Normally it should be split up! Library name: units Purpose: Decorate values with units. Provides safety and automatic conversion between values of different units. */ #ifndef __INET_UNITS_H #define __INET_UNITS_H #include #include namespace inet { namespace units { namespace internal // Boost would call this "detail" { // Forward template struct convert; // Allows construction of no units struct none; // Forward template struct fixed_power; } // namespace internal // Construct a unit equivalent to Unit1*Unit2 template struct compose; // Constructs a unit equivalent to Unit*Num/Den template struct scale; // Constructs a unit equivalent to Unit+Num/Den template struct translate; // Constructs a unit equivalent to Unit^(Num/Den) template struct pow; // A unit which is effectively no units at all. typedef pow unit; // A value with a unit. // Value is the type you are storing // Units is the units of the value template class value { public: typedef Value value_type; typedef Units unit; value() : m_rep() { } explicit value(const value_type& v) : m_rep(v) { } template value(const value& v) : m_rep(internal::convert::fn(v.get())) { } const value_type& get() const { return m_rep; } template value& operator=(const value& other) { m_rep = value(other).get(); return *this; } template value operator+(const value& other) const { return value(get() + value(other).get()); } template value& operator+=(const value& other) { m_rep += value(other).get(); return *this; } template value& operator-=(const value& other) { m_rep -= value(other).get(); return *this; } template value operator-(const value& other) const { return value(get() - value(other).get()); } value operator-() const { return value(-get()); } template value > operator*(const value& other) const { return value >(get() * other.get()); } value operator*(const value_type& v) const { return value(get() * v); } value& operator*=(const value_type& v) { m_rep *= v; return *this; } template value > > operator/(const value& other) const { return value > >(get() / other.get()); } value operator/(const value_type& v) const { return value(get() / v); } value& operator/=(const value_type& v) { m_rep /= v; return *this; } template bool operator==(const value& other) const { return get() == value(other).get(); } template bool operator!=(const value& other) const { return get() != value(other).get(); } template bool operator<(const value& other) const { return get() < value(other).get(); } template bool operator<=(const value& other) const { return get() <= value(other).get(); } template bool operator>(const value& other) const { return get() > value(other).get(); } template bool operator>=(const value& other) const { return get() >= value(other).get(); } value& operator++() { ++m_rep; return *this; } value operator++(int) { value v = *this; ++m_rep; return v; } value& operator--() { --m_rep; return *this; } value operator--(int) { value v = *this; --m_rep; return v; } private: value_type m_rep; }; template value > operator/(const Value& a, const value& b) { return value >(a / b.get()); } template value operator*(const Value& a, const value& b) { return value(a * b.get()); } template value > sqrt(const value& a) { return value >(std::sqrt(a.get())); } template value > raise(const value& a) { return value >(internal::fixed_power::pow(a.get())); } } // namespace units /*****************************************************************************/ // Implementation namespace units { namespace internal { // Ensures (at compile-time) that the template argument is true. template struct legacy_static_assert; template<> struct legacy_static_assert {}; // Forward template struct convertible; // Forward template struct scaling_factor; // Converts T1 to T2. // Stage 3 - performed after Stage 1 and Stage 2. // The reason we perform convert in stages is so that the compiler // can resolve templates in the order we want it to. template struct convert3 { // The default implementation assumes that the two quantities are in compatible // units up to some scaling factor. Find the scaling factor and apply it. template static V fn(const V& v) { return v * scaling_factor::template fn() / scaling_factor::template fn(); } }; // Converts T1 to T2. // Template matches the first argument (T1), // this is the fall-through to convert3. template struct convert2 { template static V fn(const V& v) { return convert3::fn(v); } }; // Converts T1 to T2. // If you really want to implement your own conversion routine, // specialise this template. // The default implementation falls through to convert2. template struct convert { // If this fails, then T1 is not convertible to T2: legacy_static_assert::value> check_convertible; template static V fn(const V& v) { return convert2::fn(v); } }; // If we convert to the same type, the conversion function // is trivial. template struct convert { template static const U& fn(const U& u) { return u; } }; // Convert to same type. template struct convert3 { template static const U& fn(const U& u) { return u; } }; // Convert from a scaled unit template struct convert2, U> { template static V fn(const V& v) { return convert::fn((v * Den) / Num); } }; // Convert to a scaled unit template struct convert3 > { template static V fn(const V& v) { return (convert::fn(v) * Num) / Den; } }; // Convert from a translated unit template struct convert2, U> { template static V fn(const V& v) { return convert::fn(v - static_cast(Num) / static_cast(Den)); } }; // Convert to a translated unit template struct convert3 > { template static V fn(const V& v) { return convert::fn(v) + static_cast(Num) / static_cast(Den); } }; // Count the power to which unit Term is raised in the unit List. // Returns a rational num/den of the power of term Term in List. // The default assumes that Term is not found (num/den=0) template struct count_terms { static const int num = 0; static const int den = 1; }; template struct count_terms { static const int num = 1; static const int den = 1; }; // count_terms ignores scaling factors - that is taken care of by scaling_factor. template struct count_terms > { typedef count_terms result; static const int num = result::num; static const int den = result::den; }; // count_terms ignores translation. template struct count_terms > { typedef count_terms result; static const int num = result::num; static const int den = result::den; }; // Addition of fractions. template struct count_terms > { typedef count_terms result1; typedef count_terms result2; static const int num = result1::num * result2::den + result1::den * result2::num; static const int den = result1::den * result2::den; }; // Multiplication of fractions. template struct count_terms > { typedef count_terms result; static const int num = N * result::num; static const int den = D * result::den; }; // Counts the power of the unit Term in units T1 and T2. // Reports if they are equal, using equality of fractions. // Does a depth-first search of the unit "Term", // or counts the terms in the default case. template struct check_terms_equal { typedef count_terms count1; typedef count_terms count2; static const bool value = count1::num * count2::den == count1::den * count2::num; }; template struct check_terms_equal, T1, T2> { static const bool value = check_terms_equal::value; }; template struct check_terms_equal, T1, T2> { static const bool value = check_terms_equal::value; }; template struct check_terms_equal, T1, T2> { static const bool value = check_terms_equal::value; }; template struct check_terms_equal, T3, T4> { static const bool value = check_terms_equal::value && check_terms_equal::value; }; // Determines whether two types are convertible // Counts the powers in the LHS and RHS and ensures they are equal. template struct convertible { static const bool value = check_terms_equal::value && check_terms_equal::value; }; // A functor that raises a value to the power Num/Den. // The template is specialised for efficiency // so that we don't always have to call the std::pow function. template struct fixed_power { template static T pow(const T& t) { return std::pow(t, static_cast(Num) / static_cast(Den)); } }; template struct fixed_power { template static const T& pow(const T& t) { return t; } }; template struct fixed_power { template static T pow(const T& t) { return t * t; } }; template struct fixed_power { template static T pow(const T& t) { return t * t * t; } }; template struct fixed_power { template static const T& pow(const T& t) { T u = t * t; return u * u; } }; template struct fixed_power { template static T pow(const T& t) { return 1 / t; } }; template struct fixed_power { template static T pow(const T& t) { return 1 / (t * t); } }; template struct fixed_power { template static T pow(const T& t) { return 1; } }; // Determine the scaling factor of a unit in relation to its "base" units. // Default is that U is a primitive unit and is not scaled. template struct scaling_factor { template static T fn() { return 1; } }; template struct scaling_factor > { template static T fn() { return scaling_factor::template fn() * scaling_factor::template fn(); } }; template struct scaling_factor > { template static T fn() { return scaling_factor::template fn() * static_cast(N) / static_cast(D); } }; template struct scaling_factor > { template static T fn() { return fixed_power::pow(scaling_factor::template fn()); } }; template struct scaling_factor > { template static T fn() { return scaling_factor::template fn(); } }; } // namespace internal } // namespace units /*****************************************************************************/ // Display #define UNIT_DISPLAY_NAME(unit, string) \ template<> struct output_unit { \ template static void fn(Stream & os) { os << string; } \ }; #define UNITS_DISPLAY_NAME(unit, string) \ namespace units { UNIT_DISPLAY_NAME(unit, string) } namespace units { namespace internal { // This is the default unit formatting mechanism template struct output_unit2 { template static void fn(Stream& os) { os << "units"; } }; } // namespace internal // Functor to write unit text to stream template struct output_unit { template static void fn(Stream& os) { internal::output_unit2::fn(os); } }; UNIT_DISPLAY_NAME(unit, "1"); namespace internal { template struct output_unit2 > { template static void fn(Stream& os) { output_unit::fn(os); os << '.'; output_unit::fn(os); } }; template struct output_unit2 > { template static void fn(Stream& os) { if (Num != Den) os << '('; output_unit::fn(os); if (Num != Den) { os << ')'; os << '^' << Num; if (Num % Den) { os << '/' << Den; } } } }; template struct output_unit2 > { template static void fn(Stream& os) { os << '('; output_unit::fn(os); os << '+' << Num; if (Den != 1) os << '/' << Den; os << ')'; } }; template struct output_unit2 > { template static void fn(Stream& os) { os << Den; if (Num != 1) os << '/' << Num; os << '.'; output_unit::fn(os); } }; } // namespace internal template std::ostream& operator<<(std::ostream& os, const value& value) { os << value.get(); // << ' '; output_unit::fn(os); return os; } } // namespace units /*****************************************************************************/ // Additional units namespace units { namespace units { typedef ::inet::units::unit unit; // SI base units: struct m; // meter struct kg; // kilogram struct s; // second struct K; // Kelvin struct A; // Ampere struct mol; // mole struct cd; // candela struct b; // bit } // namespace units UNIT_DISPLAY_NAME(units::m, "m"); UNIT_DISPLAY_NAME(units::kg, "kg"); UNIT_DISPLAY_NAME(units::s, "s"); UNIT_DISPLAY_NAME(units::K, "K"); UNIT_DISPLAY_NAME(units::A, "A"); UNIT_DISPLAY_NAME(units::mol, "mol"); UNIT_DISPLAY_NAME(units::cd, "cd"); namespace units { // SI derived units: typedef compose > rad; typedef compose, pow > sr; typedef pow Hz; typedef compose > > N; typedef compose > Pa; typedef compose J; typedef compose > W; typedef compose C; typedef compose > V; typedef compose > F; typedef compose > Ohm; typedef compose Ohmm; typedef compose > S; typedef compose > Spm; typedef compose Wb; typedef compose > T; typedef compose > H; typedef cd lm; typedef compose > lx; typedef pow Bq; typedef compose > Gy; typedef Gy Sv; typedef compose, mol> kat; } // namespace units UNIT_DISPLAY_NAME(units::rad, "rad"); UNIT_DISPLAY_NAME(units::sr, "sr"); UNIT_DISPLAY_NAME(units::Hz, "Hz"); // Too problematic UNIT_DISPLAY_NAME(units::N, "N"); UNIT_DISPLAY_NAME(units::Pa, "Pa"); UNIT_DISPLAY_NAME(units::J, "J"); UNIT_DISPLAY_NAME(units::W, "W"); UNIT_DISPLAY_NAME(units::C, "C"); UNIT_DISPLAY_NAME(units::V, "V"); UNIT_DISPLAY_NAME(units::F, "F"); UNIT_DISPLAY_NAME(units::Ohm, "Ohm"); UNIT_DISPLAY_NAME(units::S, "S"); UNIT_DISPLAY_NAME(units::Wb, "Wb"); UNIT_DISPLAY_NAME(units::T, "T"); UNIT_DISPLAY_NAME(units::H, "H"); UNIT_DISPLAY_NAME(units::lx, "lx"); UNIT_DISPLAY_NAME(units::Gy, "Gy"); UNIT_DISPLAY_NAME(units::kat, "kat"); namespace units { // SI prefixes: template struct deca { typedef scale type; }; template struct hecto { typedef scale type; }; template struct kilo { typedef scale type; }; template struct mega { typedef scale::type, 1, 1000> type; }; template struct giga { typedef scale::type, 1, 1000> type; }; template struct tera { typedef scale::type, 1, 1000> type; }; template struct peta { typedef scale::type, 1, 1000> type; }; template struct exa { typedef scale::type, 1, 1000> type; }; template struct zetta { typedef scale::type, 1, 1000> type; }; template struct yotta { typedef scale::type, 1, 1000> type; }; template struct deci { typedef scale type; }; template struct centi { typedef scale type; }; template struct milli { typedef scale type; }; template struct micro { typedef scale::type, 1000> type; }; template struct nano { typedef scale::type, 1000> type; }; template struct pico { typedef scale::type, 1000> type; }; template struct femto { typedef scale::type, 1000> type; }; template struct atto { typedef scale::type, 1000> type; }; template struct zepto { typedef scale::type, 1000> type; }; template struct yocto { typedef scale::type, 1000> type; }; // Some prefixed SI units: typedef centi::type cm; typedef milli::type mm; typedef kilo::type km; typedef milli::type g; typedef milli::type mg; typedef milli::type ms; typedef milli::type mW; typedef kilo::type kHz; typedef mega::type MHz; typedef giga::type GHz; } // namespace units UNIT_DISPLAY_NAME(units::cm, "cm"); UNIT_DISPLAY_NAME(units::mm, "mm"); UNIT_DISPLAY_NAME(units::km, "km"); UNIT_DISPLAY_NAME(units::g, "g"); UNIT_DISPLAY_NAME(units::mg, "mg"); UNIT_DISPLAY_NAME(units::ms, "ms"); UNIT_DISPLAY_NAME(units::mW, "mW"); UNIT_DISPLAY_NAME(units::kHz, "kHz"); UNIT_DISPLAY_NAME(units::MHz, "MHz"); UNIT_DISPLAY_NAME(units::GHz, "GHz"); namespace units { // Non-SI mass typedef scale lb; typedef scale oz; typedef scale tonne; // Non-SI temperature typedef translate Celsius; typedef translate, 32> Fahrenheit; // Non-SI time typedef scale minute; typedef scale hour; typedef scale day; typedef scale week; struct month; // No fixed ratio with week typedef scale year; typedef scale century; typedef scale millennium; // Non-SI length typedef scale inch; typedef scale foot; typedef scale yard; typedef scale mile; typedef scale nautical_mile; // Non-SI area typedef pow m2; typedef scale hectare; typedef scale are; typedef pow inch2; typedef scale acre; // Non-SI volume typedef pow cm3; typedef cm3 ml; typedef scale liter; typedef scale dl; typedef scale cl; typedef pow m3; // Non-SI velocity typedef compose > mph; typedef compose > kph; typedef compose > mps; typedef compose > spm; typedef compose > knot; typedef scale mach; // Angles typedef scale degree; typedef scale grad; typedef scale degree_minute; typedef scale degree_second; // Pressure typedef scale kPa; typedef scale psi; typedef scale millibar; // Informatics typedef compose > bps; typedef scale kbps; typedef scale Mbps; // Other typedef scale rpm; typedef scale percent; typedef scale dozen; typedef scale bakers_dozen; } // namespace units UNIT_DISPLAY_NAME(units::lb, "lb"); UNIT_DISPLAY_NAME(units::oz, "oz"); UNIT_DISPLAY_NAME(units::tonne, "tonnes"); UNIT_DISPLAY_NAME(units::Celsius, "'C"); UNIT_DISPLAY_NAME(units::Fahrenheit, "'F"); UNIT_DISPLAY_NAME(units::minute, "minutes"); UNIT_DISPLAY_NAME(units::hour, "hours"); UNIT_DISPLAY_NAME(units::day, "days"); UNIT_DISPLAY_NAME(units::week, "weeks"); UNIT_DISPLAY_NAME(units::month, "months"); UNIT_DISPLAY_NAME(units::year, "years"); UNIT_DISPLAY_NAME(units::century, "centuries"); UNIT_DISPLAY_NAME(units::millennium, "millennia"); UNIT_DISPLAY_NAME(units::inch, "inches"); UNIT_DISPLAY_NAME(units::foot, "foot"); UNIT_DISPLAY_NAME(units::yard, "yards"); UNIT_DISPLAY_NAME(units::mile, "miles"); UNIT_DISPLAY_NAME(units::nautical_mile, "nautical miles"); UNIT_DISPLAY_NAME(units::hectare, "ha"); UNIT_DISPLAY_NAME(units::are, "are"); UNIT_DISPLAY_NAME(units::acre, "acres"); UNIT_DISPLAY_NAME(units::ml, "ml"); UNIT_DISPLAY_NAME(units::liter, "l"); UNIT_DISPLAY_NAME(units::dl, "dl"); UNIT_DISPLAY_NAME(units::cl, "cl"); UNIT_DISPLAY_NAME(units::mph, "mph"); UNIT_DISPLAY_NAME(units::kph, "km/h"); UNIT_DISPLAY_NAME(units::knot, "knots"); UNIT_DISPLAY_NAME(units::mach, "mach"); UNIT_DISPLAY_NAME(units::degree, "deg"); UNIT_DISPLAY_NAME(units::grad, "grad"); UNIT_DISPLAY_NAME(units::degree_minute, "'"); UNIT_DISPLAY_NAME(units::degree_second, "\""); UNIT_DISPLAY_NAME(units::kPa, "kPa"); UNIT_DISPLAY_NAME(units::psi, "PSI"); UNIT_DISPLAY_NAME(units::millibar, "millibars"); UNIT_DISPLAY_NAME(units::percent, "%"); UNIT_DISPLAY_NAME(units::rpm, "rpm"); UNIT_DISPLAY_NAME(units::dozen, "dozen"); UNIT_DISPLAY_NAME(units::bakers_dozen, "bakers dozen"); namespace values { typedef value unit; // SI units typedef value m; typedef value kg; typedef value s; typedef value K; typedef value A; typedef value mol; typedef value cd; typedef value b; // SI derived typedef value rad; typedef value sr; typedef value Hz; typedef value N; typedef value Pa; typedef value J; typedef value W; typedef value C; typedef value V; typedef value F; typedef value Ohm; typedef value Ohmm; typedef value S; typedef value Spm; typedef value Wb; typedef value T; typedef value H; typedef value lm; typedef value lx; typedef value Bq; typedef value Gy; typedef value Sv; typedef value kat; // Prefixed units typedef value cm; typedef value mm; typedef value km; typedef value g; typedef value mg; typedef value ms; typedef value mW; typedef value kHz; typedef value MHz; typedef value GHz; // Non-SI typedef value lb; typedef value oz; typedef value tonne; typedef value Celsius; typedef value Fahrenheit; typedef value minute; typedef value hour; typedef value day; typedef value week; typedef value month; typedef value year; typedef value century; typedef value millennium; typedef value inch; typedef value foot; typedef value yard; typedef value mile; typedef value nautical_mile; typedef value m2; typedef value hectare; typedef value are; typedef value inch2; typedef value acre; typedef value cm3; typedef value ml; typedef value cl; typedef value liter; typedef value dl; typedef value m3; typedef value mph; typedef value kph; typedef value mps; typedef value spm; typedef value knot; typedef value mach; typedef value degree; typedef value grad; typedef value degree_minute; typedef value degree_second; typedef value kPa; typedef value psi; typedef value millibar; typedef value bps; typedef value kbps; typedef value Mbps; typedef value percent; typedef value rpm; typedef value dozen; typedef value bakers_dozen; } // namespace values namespace constants { // Physical constants: const value > > k(1.3806504e-23); const value mu(1.660538782e-27); const value > NA(6.02214179e23); const value G0(7.7480917004e-5); const value > > e0(8.854187817e-12); const value me(9.10938215e-31); const value eV(1.602176487e-19); const value e(1.602176487e-19); const value F(96485.3399); const value alpha(7.2973525376e-3); const value inv_alpha(137.035999679); const value > > u0(12.566370614e-7); const value phi0(2.067833667e-15); // ?? const value, pow > > > R(8.314472); const value, compose, pow > > > G(6.67428e-11); const value > h(6.62606896e-34); const value > h_bar(1.054571628e-34); const value mp(1.672621637e-27); const value mpme(1836.15267247); const value > Rinf(10973731.568527); const value > > c(299792458); const value, pow > > > rho(5.6704e-8); // Other constants: const value pi(3.141592653589793); const value lightyear(9.4605284e15); const value > > g(9.80665); } // namespace constants // Trigonometry template Value sin(const value& angle) { return std::sin(value(angle).get()); } template Value cos(const value& angle) { return std::cos(value(angle).get()); } template Value tan(const value& angle) { return std::tan(value(angle).get()); } } // namespace units } // namespace inet #endif // ifndef __INET_UNITS_H