//-------------------------------------------------------------------------------------------------- // // Units: A compile-time c++14 unit conversion library with no dependencies // //-------------------------------------------------------------------------------------------------- // // The MIT License (MIT) // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software // and associated documentation files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // //-------------------------------------------------------------------------------------------------- // // Copyright (c) 2016 Nic Holthaus // //-------------------------------------------------------------------------------------------------- // // ATTRIBUTION: // Parts of this work have been adapted from: // http://stackoverflow.com/questions/35069778/create-comparison-trait-for-template-classes-whose-parameters-are-in-a-different // http://stackoverflow.com/questions/28253399/check-traits-for-all-variadic-template-arguments/28253503 // http://stackoverflow.com/questions/36321295/rational-approximation-of-square-root-of-stdratio-at-compile-time?noredirect=1#comment60266601_36321295 // //-------------------------------------------------------------------------------------------------- // /// @file units.h /// @brief Complete implementation of `units` - a compile-time, header-only, unit conversion /// library built on c++14 with no dependencies. // //-------------------------------------------------------------------------------------------------- #pragma once #ifndef units_h__ #define units_h__ #ifdef _MSC_VER # pragma push_macro("pascal") # undef pascal # if _MSC_VER <= 1800 # define _ALLOW_KEYWORD_MACROS # pragma warning(push) # pragma warning(disable : 4520) # pragma push_macro("constexpr") # define constexpr /*constexpr*/ # pragma push_macro("noexcept") # define noexcept throw() # endif // _MSC_VER < 1800 #endif // _MSC_VER #if !defined(_MSC_VER) || _MSC_VER > 1800 # define UNIT_HAS_LITERAL_SUPPORT # define UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT #endif #ifndef UNIT_LIB_DEFAULT_TYPE # define UNIT_LIB_DEFAULT_TYPE double #endif //-------------------- // INCLUDES //-------------------- #include #include #include #include #include #include #if !defined(UNIT_LIB_DISABLE_IOSTREAM) #include #include #include //------------------------------ // STRING FORMATTER //------------------------------ namespace units { namespace detail { template std::string to_string(const T& t) { std::string str{ std::to_string(t) }; int offset{ 1 }; // remove trailing decimal points for integer value units. Locale aware! struct lconv * lc; lc = localeconv(); char decimalPoint = *lc->decimal_point; if (str.find_last_not_of('0') == str.find(decimalPoint)) { offset = 0; } str.erase(str.find_last_not_of('0') + offset, std::string::npos); return str; } } } #endif namespace units { template inline constexpr const char* name(const T&); template inline constexpr const char* abbreviation(const T&); } //------------------------------ // MACROS //------------------------------ /** * @def UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, definition) * @brief Helper macro for generating the boiler-plate code generating the tags of a new unit. * @details The macro generates singular, plural, and abbreviated forms * of the unit definition (e.g. `meter`, `meters`, and `m`), as aliases for the * unit tag. * @param namespaceName namespace in which the new units will be encapsulated. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param namePlural - plural version of the unit name, e.g. 'meters' * @param abbreviation - abbreviated unit name, e.g. 'm' * @param definition - the variadic parameter is used for the definition of the unit * (e.g. `unit, units::category::length_unit>`) * @note a variadic template is used for the definition to allow templates with * commas to be easily expanded. All the variadic 'arguments' should together * comprise the unit definition. */ #define UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, /*definition*/...)\ namespace namespaceName\ {\ /** @name Units (full names plural) */ /** @{ */ typedef __VA_ARGS__ namePlural; /** @} */\ /** @name Units (full names singular) */ /** @{ */ typedef namePlural nameSingular; /** @} */\ /** @name Units (abbreviated) */ /** @{ */ typedef namePlural abbreviation; /** @} */\ } /** * @def UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular) * @brief Macro for generating the boiler-plate code for the unit_t type definition. * @details The macro generates the definition of the unit container types, e.g. `meter_t` * @param namespaceName namespace in which the new units will be encapsulated. * @param nameSingular singular version of the unit name, e.g. 'meter' */ #define UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\ namespace namespaceName\ {\ /** @name Unit Containers */ /** @{ */ typedef unit_t nameSingular ## _t; /** @} */\ } /** * @def UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular,underlyingType) * @brief Macro for generating the boiler-plate code for a unit_t type definition with a non-default underlying type. * @details The macro generates the definition of the unit container types, e.g. `meter_t` * @param namespaceName namespace in which the new units will be encapsulated. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param underlyingType the underlying type */ #define UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular, underlyingType)\ namespace namespaceName\ {\ /** @name Unit Containers */ /** @{ */ typedef unit_t nameSingular ## _t; /** @} */\ } /** * @def UNIT_ADD_IO(namespaceName,nameSingular, abbreviation) * @brief Macro for generating the boiler-plate code needed for I/O for a new unit. * @details The macro generates the code to insert units into an ostream. It * prints both the value and abbreviation of the unit when invoked. * @param namespaceName namespace in which the new units will be encapsulated. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param abbrev - abbreviated unit name, e.g. 'm' * @note When UNIT_LIB_DISABLE_IOSTREAM is defined, the macro does not generate any code */ #if defined(UNIT_LIB_DISABLE_IOSTREAM) #define UNIT_ADD_IO(namespaceName, nameSingular, abbrev) #else #define UNIT_ADD_IO(namespaceName, nameSingular, abbrev)\ namespace namespaceName\ {\ inline std::ostream& operator<<(std::ostream& os, const nameSingular ## _t& obj) \ {\ os << obj() << " "#abbrev; return os; \ }\ inline std::string to_string(const nameSingular ## _t& obj)\ {\ return units::detail::to_string(obj()) + std::string(" "#abbrev);\ }\ } #endif /** * @def UNIT_ADD_NAME(namespaceName,nameSingular,abbreviation) * @brief Macro for generating constexpr names/abbreviations for units. * @details The macro generates names for units. E.g. name() of 1_m would be "meter", and * abbreviation would be "m". * @param namespaceName namespace in which the new units will be encapsulated. All literal values * are placed in the `units::literals` namespace. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param abbreviation - abbreviated unit name, e.g. 'm' */ #define UNIT_ADD_NAME(namespaceName, nameSingular, abbrev)\ template<> inline constexpr const char* name(const namespaceName::nameSingular ## _t&)\ {\ return #nameSingular;\ }\ template<> inline constexpr const char* abbreviation(const namespaceName::nameSingular ## _t&)\ {\ return #abbrev;\ } /** * @def UNIT_ADD_LITERALS(namespaceName,nameSingular,abbreviation) * @brief Macro for generating user-defined literals for units. * @details The macro generates user-defined literals for units. A literal suffix is created * using the abbreviation (e.g. `10.0_m`). * @param namespaceName namespace in which the new units will be encapsulated. All literal values * are placed in the `units::literals` namespace. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param abbreviation - abbreviated unit name, e.g. 'm' * @note When UNIT_HAS_LITERAL_SUPPORT is not defined, the macro does not generate any code */ #if defined(UNIT_HAS_LITERAL_SUPPORT) #define UNIT_ADD_LITERALS(namespaceName, nameSingular, abbreviation)\ namespace literals\ {\ inline constexpr namespaceName::nameSingular ## _t operator""_ ## abbreviation(long double d)\ {\ return namespaceName::nameSingular ## _t(static_cast(d));\ }\ inline constexpr namespaceName::nameSingular ## _t operator""_ ## abbreviation (unsigned long long d)\ {\ return namespaceName::nameSingular ## _t(static_cast(d));\ }\ } #else #define UNIT_ADD_LITERALS(namespaceName, nameSingular, abbreviation) #endif /** * @def UNIT_ADD(namespaceName,nameSingular, namePlural, abbreviation, definition) * @brief Macro for generating the boiler-plate code needed for a new unit. * @details The macro generates singular, plural, and abbreviated forms * of the unit definition (e.g. `meter`, `meters`, and `m`), as well as the * appropriately named unit container (e.g. `meter_t`). A literal suffix is created * using the abbreviation (e.g. `10.0_m`). It also defines a class-specific * cout function which prints both the value and abbreviation of the unit when invoked. * @param namespaceName namespace in which the new units will be encapsulated. All literal values * are placed in the `units::literals` namespace. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param namePlural - plural version of the unit name, e.g. 'meters' * @param abbreviation - abbreviated unit name, e.g. 'm' * @param definition - the variadic parameter is used for the definition of the unit * (e.g. `unit, units::category::length_unit>`) * @note a variadic template is used for the definition to allow templates with * commas to be easily expanded. All the variadic 'arguments' should together * comprise the unit definition. */ #define UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, __VA_ARGS__)\ UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\ UNIT_ADD_NAME(namespaceName,nameSingular, abbreviation)\ UNIT_ADD_IO(namespaceName,nameSingular, abbreviation)\ UNIT_ADD_LITERALS(namespaceName,nameSingular, abbreviation) /** * @def UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName,nameSingular, namePlural, abbreviation, underlyingType, definition) * @brief Macro for generating the boiler-plate code needed for a new unit with a non-default underlying type. * @details The macro generates singular, plural, and abbreviated forms * of the unit definition (e.g. `meter`, `meters`, and `m`), as well as the * appropriately named unit container (e.g. `meter_t`). A literal suffix is created * using the abbreviation (e.g. `10.0_m`). It also defines a class-specific * cout function which prints both the value and abbreviation of the unit when invoked. * @param namespaceName namespace in which the new units will be encapsulated. All literal values * are placed in the `units::literals` namespace. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param namePlural - plural version of the unit name, e.g. 'meters' * @param abbreviation - abbreviated unit name, e.g. 'm' * @param underlyingType - the underlying type, e.g. 'int' or 'float' * @param definition - the variadic parameter is used for the definition of the unit * (e.g. `unit, units::category::length_unit>`) * @note a variadic template is used for the definition to allow templates with * commas to be easily expanded. All the variadic 'arguments' should together * comprise the unit definition. */ #define UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName, nameSingular, namePlural, abbreviation, underlyingType, /*definition*/...)\ UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, __VA_ARGS__)\ UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular,underlyingType)\ UNIT_ADD_IO(namespaceName,nameSingular, abbreviation)\ UNIT_ADD_LITERALS(namespaceName,nameSingular, abbreviation) /** * @def UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation) * @brief Macro to create decibel container and literals for an existing unit type. * @details This macro generates the decibel unit container, cout overload, and literal definitions. * @param namespaceName namespace in which the new units will be encapsulated. All literal values * are placed in the `units::literals` namespace. * @param nameSingular singular version of the base unit name, e.g. 'watt' * @param abbreviation - abbreviated decibel unit name, e.g. 'dBW' */ #define UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation)\ namespace namespaceName\ {\ /** @name Unit Containers */ /** @{ */ typedef unit_t abbreviation ## _t; /** @} */\ }\ UNIT_ADD_IO(namespaceName, abbreviation, abbreviation)\ UNIT_ADD_LITERALS(namespaceName, abbreviation, abbreviation) /** * @def UNIT_ADD_CATEGORY_TRAIT(unitCategory, baseUnit) * @brief Macro to create the `is_category_unit` type trait. * @details This trait allows users to test whether a given type matches * an intended category. This macro comprises all the boiler-plate * code necessary to do so. * @param unitCategory The name of the category of unit, e.g. length or mass. */ #define UNIT_ADD_CATEGORY_TRAIT_DETAIL(unitCategory)\ namespace traits\ {\ /** @cond */\ namespace detail\ {\ template struct is_ ## unitCategory ## _unit_impl : std::false_type {};\ template\ struct is_ ## unitCategory ## _unit_impl> : std::is_same>::base_unit_type>, units::category::unitCategory ## _unit>::type {};\ template class N>\ struct is_ ## unitCategory ## _unit_impl> : std::is_same>::unit_type>, units::category::unitCategory ## _unit>::type {};\ }\ /** @endcond */\ } #if defined(UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT) #define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ namespace traits\ {\ template struct is_ ## unitCategory ## _unit : std::integral_constant>::value...>::value> {};\ } #else #define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ namespace traits\ {\ template\ struct is_ ## unitCategory ## _unit : std::integral_constant::type>::value &&\ units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value &&\ units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value>{};\ } #endif #define UNIT_ADD_CATEGORY_TRAIT(unitCategory)\ UNIT_ADD_CATEGORY_TRAIT_DETAIL(unitCategory)\ /** @ingroup TypeTraits*/\ /** @brief Trait which tests whether a type represents a unit of unitCategory*/\ /** @details Inherits from `std::true_type` or `std::false_type`. Use `is_ ## unitCategory ## _unit::value` to test the unit represents a unitCategory quantity.*/\ /** @tparam T one or more types to test*/\ UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory) /** * @def UNIT_ADD_WITH_METRIC_PREFIXES(nameSingular, namePlural, abbreviation, definition) * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric * prefixes from femto to peta. * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `meters` and 'meter_t', * it also creates corresponding units with metric suffixes such as `millimeters`, and `millimeter_t`), as well as the * literal suffixes (e.g. `10.0_mm`). * @param namespaceName namespace in which the new units will be encapsulated. All literal values * are placed in the `units::literals` namespace. * @param nameSingular singular version of the unit name, e.g. 'meter' * @param namePlural - plural version of the unit name, e.g. 'meters' * @param abbreviation - abbreviated unit name, e.g. 'm' * @param definition - the variadic parameter is used for the definition of the unit * (e.g. `unit, units::category::length_unit>`) * @note a variadic template is used for the definition to allow templates with * commas to be easily expanded. All the variadic 'arguments' should together * comprise the unit definition. */ #define UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, __VA_ARGS__)\ UNIT_ADD(namespaceName, femto ## nameSingular, femto ## namePlural, f ## abbreviation, femto)\ UNIT_ADD(namespaceName, pico ## nameSingular, pico ## namePlural, p ## abbreviation, pico)\ UNIT_ADD(namespaceName, nano ## nameSingular, nano ## namePlural, n ## abbreviation, nano)\ UNIT_ADD(namespaceName, micro ## nameSingular, micro ## namePlural, u ## abbreviation, micro)\ UNIT_ADD(namespaceName, milli ## nameSingular, milli ## namePlural, m ## abbreviation, milli)\ UNIT_ADD(namespaceName, centi ## nameSingular, centi ## namePlural, c ## abbreviation, centi)\ UNIT_ADD(namespaceName, deci ## nameSingular, deci ## namePlural, d ## abbreviation, deci)\ UNIT_ADD(namespaceName, deca ## nameSingular, deca ## namePlural, da ## abbreviation, deca)\ UNIT_ADD(namespaceName, hecto ## nameSingular, hecto ## namePlural, h ## abbreviation, hecto)\ UNIT_ADD(namespaceName, kilo ## nameSingular, kilo ## namePlural, k ## abbreviation, kilo)\ UNIT_ADD(namespaceName, mega ## nameSingular, mega ## namePlural, M ## abbreviation, mega)\ UNIT_ADD(namespaceName, giga ## nameSingular, giga ## namePlural, G ## abbreviation, giga)\ UNIT_ADD(namespaceName, tera ## nameSingular, tera ## namePlural, T ## abbreviation, tera)\ UNIT_ADD(namespaceName, peta ## nameSingular, peta ## namePlural, P ## abbreviation, peta)\ /** * @def UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(nameSingular, namePlural, abbreviation, definition) * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric * prefixes from femto to peta, and binary prefixes from kibi to exbi. * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `bytes` and 'byte_t', * it also creates corresponding units with metric suffixes such as `millimeters`, and `millimeter_t`), as well as the * literal suffixes (e.g. `10.0_B`). * @param namespaceName namespace in which the new units will be encapsulated. All literal values * are placed in the `units::literals` namespace. * @param nameSingular singular version of the unit name, e.g. 'byte' * @param namePlural - plural version of the unit name, e.g. 'bytes' * @param abbreviation - abbreviated unit name, e.g. 'B' * @param definition - the variadic parameter is used for the definition of the unit * (e.g. `unit, units::category::data_unit>`) * @note a variadic template is used for the definition to allow templates with * commas to be easily expanded. All the variadic 'arguments' should together * comprise the unit definition. */ #define UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, __VA_ARGS__)\ UNIT_ADD(namespaceName, kibi ## nameSingular, kibi ## namePlural, Ki ## abbreviation, kibi)\ UNIT_ADD(namespaceName, mebi ## nameSingular, mebi ## namePlural, Mi ## abbreviation, mebi)\ UNIT_ADD(namespaceName, gibi ## nameSingular, gibi ## namePlural, Gi ## abbreviation, gibi)\ UNIT_ADD(namespaceName, tebi ## nameSingular, tebi ## namePlural, Ti ## abbreviation, tebi)\ UNIT_ADD(namespaceName, pebi ## nameSingular, pebi ## namePlural, Pi ## abbreviation, pebi)\ UNIT_ADD(namespaceName, exbi ## nameSingular, exbi ## namePlural, Ei ## abbreviation, exbi) //-------------------- // UNITS NAMESPACE //-------------------- /** * @namespace units * @brief Unit Conversion Library namespace */ namespace units { //---------------------------------- // DOXYGEN //---------------------------------- /** * @defgroup UnitContainers Unit Containers * @brief Defines a series of classes which contain dimensioned values. Unit containers * store a value, and support various arithmetic operations. */ /** * @defgroup UnitTypes Unit Types * @brief Defines a series of classes which represent units. These types are tags used by * the conversion function, to create compound units, or to create `unit_t` types. * By themselves, they are not containers and have no stored value. */ /** * @defgroup UnitManipulators Unit Manipulators * @brief Defines a series of classes used to manipulate unit types, such as `inverse<>`, `squared<>`, and metric prefixes. * Unit manipulators can be chained together, e.g. `inverse>>` to * represent picoseconds^-2. */ /** * @defgroup CompileTimeUnitManipulators Compile-time Unit Manipulators * @brief Defines a series of classes used to manipulate `unit_value_t` types at compile-time, such as `unit_value_add<>`, `unit_value_sqrt<>`, etc. * Compile-time manipulators can be chained together, e.g. `unit_value_sqrt, unit_value_power>>` to * represent `c = sqrt(a^2 + b^2). */ /** * @defgroup UnitMath Unit Math * @brief Defines a collection of unit-enabled, strongly-typed versions of `` functions. * @details Includes most c++11 extensions. */ /** * @defgroup Conversion Explicit Conversion * @brief Functions used to convert values of one logical type to another. */ /** * @defgroup TypeTraits Type Traits * @brief Defines a series of classes to obtain unit type information at compile-time. */ //------------------------------ // FORWARD DECLARATIONS //------------------------------ /** @cond */ // DOXYGEN IGNORE namespace constants { namespace detail { static constexpr const UNIT_LIB_DEFAULT_TYPE PI_VAL = 3.14159265358979323846264338327950288419716939937510; } } /** @endcond */ // END DOXYGEN IGNORE //------------------------------ // RATIO TRAITS //------------------------------ /** * @ingroup TypeTraits * @{ */ /** @cond */ // DOXYGEN IGNORE namespace detail { /// has_num implementation. template struct has_num_impl { template static constexpr auto test(U*)->std::is_integral {return std::is_integral{}; } template static constexpr std::false_type test(...) { return std::false_type{}; } using type = decltype(test(0)); }; } /** * @brief Trait which checks for the existence of a static numerator. * @details Inherits from `std::true_type` or `std::false_type`. Use `has_num::value` to test * whether `class T` has a numerator static member. */ template struct has_num : units::detail::has_num_impl::type {}; namespace detail { /// has_den implementation. template struct has_den_impl { template static constexpr auto test(U*)->std::is_integral { return std::is_integral{}; } template static constexpr std::false_type test(...) { return std::false_type{}; } using type = decltype(test(0)); }; } /** * @brief Trait which checks for the existence of a static denominator. * @details Inherits from `std::true_type` or `std::false_type`. Use `has_den::value` to test * whether `class T` has a denominator static member. */ template struct has_den : units::detail::has_den_impl::type {}; /** @endcond */ // END DOXYGEN IGNORE namespace traits { /** * @brief Trait that tests whether a type represents a std::ratio. * @details Inherits from `std::true_type` or `std::false_type`. Use `is_ratio::value` to test * whether `class T` implements a std::ratio. */ template struct is_ratio : std::integral_constant::value && has_den::value> {}; } //------------------------------ // UNIT TRAITS //------------------------------ /** @cond */ // DOXYGEN IGNORE /** * @brief void type. * @details Helper class for creating type traits. */ template struct void_t { typedef void type; }; /** * @brief parameter pack for boolean arguments. */ template struct bool_pack {}; /** * @brief Trait which tests that a set of other traits are all true. */ template struct all_true : std::is_same, units::bool_pack> {}; /** @endcond */ // DOXYGEN IGNORE /** * @brief namespace representing type traits which can access the properties of types provided by the units library. */ namespace traits { #ifdef FOR_DOXYGEN_PURPOSES_ONLY /** * @ingroup TypeTraits * @brief Traits class defining the properties of units. * @details The units library determines certain properties of the units passed to * them and what they represent by using the members of the corresponding * unit_traits instantiation. */ template struct unit_traits { typedef typename T::base_unit_type base_unit_type; ///< Unit type that the unit was derived from. May be a `base_unit` or another `unit`. Use the `base_unit_of` trait to find the SI base unit type. This will be `void` if type `T` is not a unit. typedef typename T::conversion_ratio conversion_ratio; ///< `std::ratio` representing the conversion factor to the `base_unit_type`. This will be `void` if type `T` is not a unit. typedef typename T::pi_exponent_ratio pi_exponent_ratio; ///< `std::ratio` representing the exponent of pi to be used in the conversion. This will be `void` if type `T` is not a unit. typedef typename T::translation_ratio translation_ratio; ///< `std::ratio` representing a datum translation to the base unit (i.e. degrees C to degrees F conversion). This will be `void` if type `T` is not a unit. }; #endif /** @cond */ // DOXYGEN IGNORE /** * @brief unit traits implementation for classes which are not units. */ template struct unit_traits { typedef void base_unit_type; typedef void conversion_ratio; typedef void pi_exponent_ratio; typedef void translation_ratio; }; template struct unit_traits ::type> { typedef typename T::base_unit_type base_unit_type; ///< Unit type that the unit was derived from. May be a `base_unit` or another `unit`. Use the `base_unit_of` trait to find the SI base unit type. This will be `void` if type `T` is not a unit. typedef typename T::conversion_ratio conversion_ratio; ///< `std::ratio` representing the conversion factor to the `base_unit_type`. This will be `void` if type `T` is not a unit. typedef typename T::pi_exponent_ratio pi_exponent_ratio; ///< `std::ratio` representing the exponent of pi to be used in the conversion. This will be `void` if type `T` is not a unit. typedef typename T::translation_ratio translation_ratio; ///< `std::ratio` representing a datum translation to the base unit (i.e. degrees C to degrees F conversion). This will be `void` if type `T` is not a unit. }; /** @endcond */ // END DOXYGEN IGNORE } /** @cond */ // DOXYGEN IGNORE namespace detail { /** * @brief helper type to identify base units. * @details A non-templated base class for `base_unit` which enables RTTI testing. */ struct _base_unit_t {}; } /** @endcond */ // END DOXYGEN IGNORE namespace traits { /** * @ingroup TypeTraits * @brief Trait which tests if a class is a `base_unit` type. * @details Inherits from `std::true_type` or `std::false_type`. Use `is_base_unit::value` to test * whether `class T` implements a `base_unit`. */ template struct is_base_unit : std::is_base_of {}; } /** @cond */ // DOXYGEN IGNORE namespace detail { /** * @brief helper type to identify units. * @details A non-templated base class for `unit` which enables RTTI testing. */ struct _unit {}; template using meter_ratio = std::ratio; } /** @endcond */ // END DOXYGEN IGNORE namespace traits { /** * @ingroup TypeTraits * @brief Traits which tests if a class is a `unit` * @details Inherits from `std::true_type` or `std::false_type`. Use `is_unit::value` to test * whether `class T` implements a `unit`. */ template struct is_unit : std::is_base_of::type {}; } /** @} */ // end of TypeTraits //------------------------------ // BASE UNIT CLASS //------------------------------ /** * @ingroup UnitTypes * @brief Class representing SI base unit types. * @details Base units are represented by a combination of `std::ratio` template parameters, each * describing the exponent of the type of unit they represent. Example: meters per second * would be described by a +1 exponent for meters, and a -1 exponent for seconds, thus: * `base_unit, std::ratio<0>, std::ratio<-1>>` * @tparam Meter `std::ratio` representing the exponent value for meters. * @tparam Kilogram `std::ratio` representing the exponent value for kilograms. * @tparam Second `std::ratio` representing the exponent value for seconds. * @tparam Radian `std::ratio` representing the exponent value for radians. Although radians are not SI base units, they are included because radians are described by the SI as m * m^-1, which would make them indistinguishable from scalars. * @tparam Ampere `std::ratio` representing the exponent value for amperes. * @tparam Kelvin `std::ratio` representing the exponent value for Kelvin. * @tparam Mole `std::ratio` representing the exponent value for moles. * @tparam Candela `std::ratio` representing the exponent value for candelas. * @tparam Byte `std::ratio` representing the exponent value for bytes. * @sa category for type aliases for SI base_unit types. */ template, class Kilogram = std::ratio<0>, class Second = std::ratio<0>, class Radian = std::ratio<0>, class Ampere = std::ratio<0>, class Kelvin = std::ratio<0>, class Mole = std::ratio<0>, class Candela = std::ratio<0>, class Byte = std::ratio<0>> struct base_unit : units::detail::_base_unit_t { static_assert(traits::is_ratio::value, "Template parameter `Meter` must be a `std::ratio` representing the exponent of meters the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Kilogram` must be a `std::ratio` representing the exponent of kilograms the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Second` must be a `std::ratio` representing the exponent of seconds the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Ampere` must be a `std::ratio` representing the exponent of amperes the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Kelvin` must be a `std::ratio` representing the exponent of kelvin the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Candela` must be a `std::ratio` representing the exponent of candelas the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Mole` must be a `std::ratio` representing the exponent of moles the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Radian` must be a `std::ratio` representing the exponent of radians the unit has"); static_assert(traits::is_ratio::value, "Template parameter `Byte` must be a `std::ratio` representing the exponent of bytes the unit has"); typedef Meter meter_ratio; typedef Kilogram kilogram_ratio; typedef Second second_ratio; typedef Radian radian_ratio; typedef Ampere ampere_ratio; typedef Kelvin kelvin_ratio; typedef Mole mole_ratio; typedef Candela candela_ratio; typedef Byte byte_ratio; }; //------------------------------ // UNIT CATEGORIES //------------------------------ /** * @brief namespace representing the implemented base and derived unit types. These will not generally be needed by library users. * @sa base_unit for the definition of the category parameters. */ namespace category { // SCALAR (DIMENSIONLESS) TYPES typedef base_unit<> scalar_unit; ///< Represents a quantity with no dimension. typedef base_unit<> dimensionless_unit; ///< Represents a quantity with no dimension. // SI BASE UNIT TYPES // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY typedef base_unit> length_unit; ///< Represents an SI base unit of length typedef base_unit, std::ratio<1>> mass_unit; ///< Represents an SI base unit of mass typedef base_unit, std::ratio<0>, std::ratio<1>> time_unit; ///< Represents an SI base unit of time typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<1>> angle_unit; ///< Represents an SI base unit of angle typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> current_unit; ///< Represents an SI base unit of current typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> temperature_unit; ///< Represents an SI base unit of temperature typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> substance_unit; ///< Represents an SI base unit of amount of substance typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> luminous_intensity_unit; ///< Represents an SI base unit of luminous intensity // SI DERIVED UNIT TYPES // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> solid_angle_unit; ///< Represents an SI derived unit of solid angle typedef base_unit, std::ratio<0>, std::ratio<-1>> frequency_unit; ///< Represents an SI derived unit of frequency typedef base_unit, std::ratio<0>, std::ratio<-1>> velocity_unit; ///< Represents an SI derived unit of velocity typedef base_unit, std::ratio<0>, std::ratio<-1>, std::ratio<1>> angular_velocity_unit; ///< Represents an SI derived unit of angular velocity typedef base_unit, std::ratio<0>, std::ratio<-2>> acceleration_unit; ///< Represents an SI derived unit of acceleration typedef base_unit, std::ratio<1>, std::ratio<-2>> force_unit; ///< Represents an SI derived unit of force typedef base_unit, std::ratio<1>, std::ratio<-2>> pressure_unit; ///< Represents an SI derived unit of pressure typedef base_unit, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<1>> charge_unit; ///< Represents an SI derived unit of charge typedef base_unit, std::ratio<1>, std::ratio<-2>> energy_unit; ///< Represents an SI derived unit of energy typedef base_unit, std::ratio<1>, std::ratio<-3>> power_unit; ///< Represents an SI derived unit of power typedef base_unit, std::ratio<1>, std::ratio<-3>, std::ratio<0>, std::ratio<-1>> voltage_unit; ///< Represents an SI derived unit of voltage typedef base_unit, std::ratio<-1>, std::ratio<4>, std::ratio<0>, std::ratio<2>> capacitance_unit; ///< Represents an SI derived unit of capacitance typedef base_unit, std::ratio<1>, std::ratio<-3>, std::ratio<0>, std::ratio<-2>> impedance_unit; ///< Represents an SI derived unit of impedance typedef base_unit, std::ratio<-1>, std::ratio<3>, std::ratio<0>, std::ratio<2>> conductance_unit; ///< Represents an SI derived unit of conductance typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-1>> magnetic_flux_unit; ///< Represents an SI derived unit of magnetic flux typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-1>> magnetic_field_strength_unit; ///< Represents an SI derived unit of magnetic field strength typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-2>> inductance_unit; ///< Represents an SI derived unit of inductance typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> luminous_flux_unit; ///< Represents an SI derived unit of luminous flux typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> illuminance_unit; ///< Represents an SI derived unit of illuminance typedef base_unit, std::ratio<0>, std::ratio<-1>> radioactivity_unit; ///< Represents an SI derived unit of radioactivity // OTHER UNIT TYPES // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY typedef base_unit, std::ratio<1>, std::ratio<-2>> torque_unit; ///< Represents an SI derived unit of torque typedef base_unit> area_unit; ///< Represents an SI derived unit of area typedef base_unit> volume_unit; ///< Represents an SI derived unit of volume typedef base_unit, std::ratio<1>> density_unit; ///< Represents an SI derived unit of density typedef base_unit<> concentration_unit; ///< Represents a unit of concentration typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> data_unit; ///< Represents a unit of data size typedef base_unit, std::ratio<0>, std::ratio<-1>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> data_transfer_rate_unit; ///< Represents a unit of data transfer rate } //------------------------------ // UNIT CLASSES //------------------------------ /** @cond */ // DOXYGEN IGNORE /** * @brief unit type template specialization for units derived from base units. */ template struct unit; template struct unit, PiExponent, Translation> : units::detail::_unit { static_assert(traits::is_ratio::value, "Template parameter `Conversion` must be a `std::ratio` representing the conversion factor to `BaseUnit`."); static_assert(traits::is_ratio::value, "Template parameter `PiExponent` must be a `std::ratio` representing the exponents of Pi the unit has."); static_assert(traits::is_ratio::value, "Template parameter `Translation` must be a `std::ratio` representing an additive translation required by the unit conversion."); typedef typename units::base_unit base_unit_type; typedef Conversion conversion_ratio; typedef Translation translation_ratio; typedef PiExponent pi_exponent_ratio; }; /** @endcond */ // END DOXYGEN IGNORE /** * @brief Type representing an arbitrary unit. * @ingroup UnitTypes * @details `unit` types are used as tags for the `conversion` function. They are *not* containers * (see `unit_t` for a container class). Each unit is defined by: * * - A `std::ratio` defining the conversion factor to the base unit type. (e.g. `std::ratio<1,12>` for inches to feet) * - A base unit that the unit is derived from (or a unit category. Must be of type `unit` or `base_unit`) * - An exponent representing factors of PI required by the conversion. (e.g. `std::ratio<-1>` for a radians to degrees conversion) * - a ratio representing a datum translation required for the conversion (e.g. `std::ratio<32>` for a farenheit to celsius conversion) * * Typically, a specific unit, like `meters`, would be implemented as a type alias * of `unit`, i.e. `using meters = unit, units::category::length_unit`, or * `using inches = unit, feet>`. * @tparam Conversion std::ratio representing scalar multiplication factor. * @tparam BaseUnit Unit type which this unit is derived from. May be a `base_unit`, or another `unit`. * @tparam PiExponent std::ratio representing the exponent of pi required by the conversion. * @tparam Translation std::ratio representing any datum translation required by the conversion. */ template, class Translation = std::ratio<0>> struct unit : units::detail::_unit { static_assert(traits::is_unit::value, "Template parameter `BaseUnit` must be a `unit` type."); static_assert(traits::is_ratio::value, "Template parameter `Conversion` must be a `std::ratio` representing the conversion factor to `BaseUnit`."); static_assert(traits::is_ratio::value, "Template parameter `PiExponent` must be a `std::ratio` representing the exponents of Pi the unit has."); typedef typename units::traits::unit_traits::base_unit_type base_unit_type; typedef typename std::ratio_multiply conversion_ratio; typedef typename std::ratio_add pi_exponent_ratio; typedef typename std::ratio_add, typename BaseUnit::translation_ratio> translation_ratio; }; //------------------------------ // BASE UNIT MANIPULATORS //------------------------------ /** @cond */ // DOXYGEN IGNORE namespace detail { /** * @brief base_unit_of trait implementation * @details recursively seeks base_unit type that a unit is derived from. Since units can be * derived from other units, the `base_unit_type` typedef may not represent this value. */ template struct base_unit_of_impl; template struct base_unit_of_impl> : base_unit_of_impl {}; template struct base_unit_of_impl> { typedef base_unit type; }; template<> struct base_unit_of_impl { typedef void type; }; } /** @endcond */ // END DOXYGEN IGNORE namespace traits { /** * @brief Trait which returns the `base_unit` type that a unit is originally derived from. * @details Since units can be derived from other `unit` types in addition to `base_unit` types, * the `base_unit_type` typedef will not always be a `base_unit` (or unit category). * Since compatible */ template using base_unit_of = typename units::detail::base_unit_of_impl::type; } /** @cond */ // DOXYGEN IGNORE namespace detail { /** * @brief implementation of base_unit_multiply * @details 'multiples' (adds exponent ratios of) two base unit types. Base units can be found * using `base_unit_of`. */ template struct base_unit_multiply_impl; template struct base_unit_multiply_impl, base_unit> { using type = base_unit...>; }; /** * @brief represents type of two base units multiplied together */ template using base_unit_multiply = typename base_unit_multiply_impl::type; /** * @brief implementation of base_unit_divide * @details 'dived' (subtracts exponent ratios of) two base unit types. Base units can be found * using `base_unit_of`. */ template struct base_unit_divide_impl; template struct base_unit_divide_impl, base_unit> { using type = base_unit...>; }; /** * @brief represents the resulting type of `base_unit` U1 divided by U2. */ template using base_unit_divide = typename base_unit_divide_impl::type; /** * @brief implementation of inverse_base * @details multiplies all `base_unit` exponent ratios by -1. The resulting type represents * the inverse base unit of the given `base_unit` type. */ template struct inverse_base_impl; template struct inverse_base_impl> { using type = base_unit>...>; }; /** * @brief represent the inverse type of `class U` * @details E.g. if `U` is `length_unit`, then `inverse` will represent `length_unit^-1`. */ template using inverse_base = typename inverse_base_impl::type; /** * @brief implementation of `squared_base` * @details multiplies all the exponent ratios of the given class by 2. The resulting type is * equivalent to the given type squared. */ template struct squared_base_impl; template struct squared_base_impl> { using type = base_unit>...>; }; /** * @brief represents the type of a `base_unit` squared. * @details E.g. `squared` will represent `length_unit^2`. */ template using squared_base = typename squared_base_impl::type; /** * @brief implementation of `cubed_base` * @details multiplies all the exponent ratios of the given class by 3. The resulting type is * equivalent to the given type cubed. */ template struct cubed_base_impl; template struct cubed_base_impl> { using type = base_unit>...>; }; /** * @brief represents the type of a `base_unit` cubed. * @details E.g. `cubed` will represent `length_unit^3`. */ template using cubed_base = typename cubed_base_impl::type; /** * @brief implementation of `sqrt_base` * @details divides all the exponent ratios of the given class by 2. The resulting type is * equivalent to the square root of the given type. */ template struct sqrt_base_impl; template struct sqrt_base_impl> { using type = base_unit>...>; }; /** * @brief represents the square-root type of a `base_unit`. * @details E.g. `sqrt` will represent `length_unit^(1/2)`. */ template using sqrt_base = typename sqrt_base_impl::type; /** * @brief implementation of `cbrt_base` * @details divides all the exponent ratios of the given class by 3. The resulting type is * equivalent to the given type's cube-root. */ template struct cbrt_base_impl; template struct cbrt_base_impl> { using type = base_unit>...>; }; /** * @brief represents the cube-root type of a `base_unit` . * @details E.g. `cbrt` will represent `length_unit^(1/3)`. */ template using cbrt_base = typename cbrt_base_impl::type; } /** @endcond */ // END DOXYGEN IGNORE //------------------------------ // UNIT MANIPULATORS //------------------------------ /** @cond */ // DOXYGEN IGNORE namespace detail { /** * @brief implementation of `unit_multiply`. * @details multiplies two units. The base unit becomes the base units of each with their exponents * added together. The conversion factors of each are multiplied by each other. Pi exponent ratios * are added, and datum translations are removed. */ template struct unit_multiply_impl { using type = unit < std::ratio_multiply, base_unit_multiply , traits::base_unit_of>, std::ratio_add, std::ratio < 0 >> ; }; /** * @brief represents the type of two units multiplied together. * @details recalculates conversion and exponent ratios at compile-time. */ template using unit_multiply = typename unit_multiply_impl::type; /** * @brief implementation of `unit_divide`. * @details divides two units. The base unit becomes the base units of each with their exponents * subtracted from each other. The conversion factors of each are divided by each other. Pi exponent ratios * are subtracted, and datum translations are removed. */ template struct unit_divide_impl { using type = unit < std::ratio_divide, base_unit_divide, traits::base_unit_of>, std::ratio_subtract, std::ratio < 0 >> ; }; /** * @brief represents the type of two units divided by each other. * @details recalculates conversion and exponent ratios at compile-time. */ template using unit_divide = typename unit_divide_impl::type; /** * @brief implementation of `inverse` * @details inverts a unit (equivalent to 1/unit). The `base_unit` and pi exponents are all multiplied by * -1. The conversion ratio numerator and denominator are swapped. Datum translation * ratios are removed. */ template struct inverse_impl { using type = unit < std::ratio, inverse_base::base_unit_type>>, std::ratio_multiply::pi_exponent_ratio, std::ratio<-1>>, std::ratio < 0 >> ; // inverses are rates or change, the translation factor goes away. }; } /** @endcond */ // END DOXYGEN IGNORE /** * @brief represents the inverse unit type of `class U`. * @ingroup UnitManipulators * @tparam U `unit` type to invert. * @details E.g. `inverse` will represent meters^-1 (i.e. 1/meters). */ template using inverse = typename units::detail::inverse_impl::type; /** @cond */ // DOXYGEN IGNORE namespace detail { /** * @brief implementation of `squared` * @details Squares the conversion ratio, `base_unit` exponents, pi exponents, and removes * datum translation ratios. */ template struct squared_impl { static_assert(traits::is_unit::value, "Template parameter `Unit` must be a `unit` type."); using Conversion = typename Unit::conversion_ratio; using type = unit < std::ratio_multiply, squared_base>, std::ratio_multiply>, typename Unit::translation_ratio > ; }; } /** @endcond */ // END DOXYGEN IGNORE /** * @brief represents the unit type of `class U` squared * @ingroup UnitManipulators * @tparam U `unit` type to square. * @details E.g. `square` will represent meters^2. */ template using squared = typename units::detail::squared_impl::type; /** @cond */ // DOXYGEN IGNORE namespace detail { /** * @brief implementation of `cubed` * @details Cubes the conversion ratio, `base_unit` exponents, pi exponents, and removes * datum translation ratios. */ template struct cubed_impl { static_assert(traits::is_unit::value, "Template parameter `Unit` must be a `unit` type."); using Conversion = typename Unit::conversion_ratio; using type = unit < std::ratio_multiply>, cubed_base>, std::ratio_multiply>, typename Unit::translation_ratio> ; }; } /** @endcond */ // END DOXYGEN IGNORE /** * @brief represents the type of `class U` cubed. * @ingroup UnitManipulators * @tparam U `unit` type to cube. * @details E.g. `cubed` will represent meters^3. */ template using cubed = typename units::detail::cubed_impl::type; /** @cond */ // DOXYGEN IGNORE namespace detail { //---------------------------------- // RATIO_SQRT IMPLEMENTATION //---------------------------------- using Zero = std::ratio<0>; using One = std::ratio<1>; template using Square = std::ratio_multiply; // Find the largest std::integer N such that Predicate::value is true. template