diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 7127e787e5..fbecdec87c 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -36,6 +36,7 @@ CoreUI wpiutil/src/main/native/resources/coreui-* Feather Icons wpiutil/src/main/native/resources/feather-* jQuery wpiutil/src/main/native/resources/jquery-* popper.js wpiutil/src/main/native/resources/popper-* +units wpiutil/src/main/native/include/units/units.h ============================================================================== @@ -339,3 +340,29 @@ 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. + + +============= +units License +============= +The MIT License (MIT) + +Copyright (c) 2016 Nic Holthaus + +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. diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 3cb0ebc2e9..4febea193a 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -11,6 +11,7 @@ generatedFileExclude { src/main/native/cpp/http_parser\.cpp$ src/main/native/cpp/llvm/ src/main/native/include/llvm/ + src/main/native/include/units/units\.h$ src/main/native/include/wpi/AlignOf\.h$ src/main/native/include/wpi/ArrayRef\.h$ src/main/native/include/wpi/Chrono\.h$ @@ -62,6 +63,7 @@ generatedFileExclude { src/main/native/include/uv/ src/main/native/libuv/ src/main/native/resources/ + src/test/native/cpp/UnitsTest\.cpp$ } licenseUpdateExclude { diff --git a/wpiutil/src/main/native/include/units/units.h b/wpiutil/src/main/native/include/units/units.h new file mode 100644 index 0000000000..5dc80bf882 --- /dev/null +++ b/wpiutil/src/main/native/include/units/units.h @@ -0,0 +1,4853 @@ +//-------------------------------------------------------------------------------------------------- +// +// 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