/* 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 CG_UNITS_HPP_INCLUDED #define CG_UNITS_HPP_INCLUDED #include 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; } // 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< Value, compose > 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< Value, compose > > 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()) ); } } /*****************************************************************************/ // Implementation namespace units { namespace internal { // Ensures (at compile-time) that the template argument is true. template struct static_assert; template<> struct 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: static_assert< convertible::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< compose > { template static T fn() { return scaling_factor::template fn() * scaling_factor::template fn(); } }; template struct scaling_factor< scale > { template static T fn() { return scaling_factor::template fn() * static_cast(N) / static_cast(D); } }; template struct scaling_factor< pow > { template static T fn() { return fixed_power::pow( scaling_factor::template fn() ); } }; template struct scaling_factor< translate > { template static T fn() { return scaling_factor::template fn(); } }; } } /*****************************************************************************/ // 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"; } }; } // 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< compose > { template static void fn(Stream &os) { output_unit::fn(os); os << '.'; output_unit::fn(os); } }; template struct output_unit2< pow > { 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< translate > { template static void fn(Stream &os) { os << '('; output_unit::fn(os); os << '+' << Num; if(Den!=1) os << '/' << Den; os << ')'; } }; template struct output_unit2< scale > { template static void fn(Stream &os) { os << Den; if(Num != 1) os << '/' << Num; os << '.'; output_unit::fn(os); } }; } template Str & operator<<(Str & os, const value & value ) { os << value.get() << ' '; output_unit::fn( os ); return os; } } /*****************************************************************************/ // Additional units namespace units { namespace units { typedef ::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 } 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 > S; 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; } 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; } 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" ); 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 > meters_per_second; typedef compose > knot; typedef scale mach; // Angles typedef scale degree; typedef scale grad; typedef scale< degree, 60 > degree_minute; typedef scale< degree_minute, 60 > degree_second; // Pressure typedef scale kPa; typedef scale psi; typedef scale millibar; // Other typedef scale rpm; typedef scale percent; typedef scale dozen; typedef scale bakers_dozen; } 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; // 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 S; 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; // 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 meters_per_second; 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 percent; typedef value rpm; typedef value dozen; typedef value bakers_dozen; } 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.566370614); 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 ); } // 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() ); } } #endif