diff --git a/.clang-format b/.clang-format index ef4261c8..b37a7280 100644 --- a/.clang-format +++ b/.clang-format @@ -25,15 +25,15 @@ AccessModifierOffset: -2 # AlwaysBreakTemplateDeclarations: Yes # BinPackArguments: true # BinPackParameters: true -# BraceWrapping: +BraceWrapping: # AfterCaseLabel: false -# AfterClass: false + AfterClass: false # AfterControlStatement: false # AfterEnum: false -# AfterFunction: false + AfterFunction: true # AfterNamespace: false # AfterObjCDeclaration: false -# AfterStruct: false + AfterStruct: false # AfterUnion: false # AfterExternBlock: false # BeforeCatch: false @@ -44,9 +44,9 @@ AccessModifierOffset: -2 # SplitEmptyNamespace: true # BreakAfterJavaFieldAnnotations: false # BreakBeforeBinaryOperators: None -BreakBeforeBraces: Stroustrup +BreakBeforeBraces: Custom # BreakBeforeInheritanceComma: false -BreakBeforeTernaryOperators: false +# BreakBeforeTernaryOperators: true BreakConstructorInitializers: AfterColon BreakConstructorInitializersBeforeComma: true BreakInheritanceList: AfterColon @@ -66,6 +66,7 @@ ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH + - SECTION - GIVEN - WHEN - THEN @@ -93,7 +94,7 @@ IncludeCategories: # MacroBlockBegin: '' # MacroBlockEnd: '' # MaxEmptyLinesToKeep: 1 -NamespaceIndentation: All +# NamespaceIndentation: None # ObjCBinPackProtocolList: Never # ObjCBlockIndentWidth: 2 # ObjCSpaceAfterProperty: false @@ -143,9 +144,9 @@ NamespaceIndentation: All SpaceAfterTemplateKeyword: false # SpaceBeforeAssignmentOperators: true # SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: false +# SpaceBeforeCtorInitializerColon: true # SpaceBeforeInheritanceColon: true -SpaceBeforeParens: Never +# SpaceBeforeParens: ControlStatements # SpaceBeforeRangeBasedForLoopColon: true # SpaceInEmptyBlock: false # SpaceInEmptyParentheses: false diff --git a/.gitattributes b/.gitattributes index 314766e9..a802e460 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,16 @@ +# Set the default behavior * text=auto eol=lf *.{cmd,[cC][mM][dD]} text eol=crlf *.{bat,[bB][aA][tT]} text eol=crlf + +# Explicitly declare text files to always be normalized and converted to native line endings on checkout +*.cpp text +*.h text +*.py text +*.txt text +*.md text +*.yml text + +# Denote all files that are truly binary and should not be modified +*.png binary +*.jpg binary diff --git a/README.md b/README.md index da45f8f0..0ccf03fc 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,32 @@ # `mp-units` - A Units Library for C++ +## TL;DR + +```cpp +constexpr Velocity auto avg_speed(Length auto d, Time auto t) +{ + return d / t; +} + +int main() +{ + using namespace si::literals; + Velocity auto v1 = avg_speed(220km, 2h); + Velocity auto v2 = avg_speed(140mi, 2h); + + std::cout << v1 << '\n'; // 110 km/h + std::cout << quantity_cast(speed) << '\n'; // 30.5556 m/s + std::cout << v2 << '\n'; // 70 mi/h +} +``` + +Try it on [Compiler Explorer](https://godbolt.org/z/OVpkT1). + + ## Summary -`Units` is a compile-time enabled Modern C++ library that provides compile-time dimensional +`mp-units` is a compile-time enabled Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The basic idea and design heavily bases on `std::chrono::duration` and extends it to work properly with many dimensions. @@ -31,74 +54,120 @@ static_assert(1000 / 1s == 1kHz); static_assert(10km / 5km == 2); ``` -## Usage Overview -The library framework consists of a few concepts: quantities, units, dimensions and their exponents. From the user's -point of view the most important is a `quantity`. +## Getting Started -Quantity is a concrete amount of a unit for a specified dimension with a specific representation: +The library framework consists of a few concepts: quantities, units, dimensions and their +exponents. From the user's point of view the most important is a `quantity`. + +A quantity is a concrete amount of a unit for a specified dimension with a specific representation: ```cpp -units::quantity d1(123); -auto d2 = 123km; // units::quantity +units::quantity d(123); ``` -There are C++ concepts provided for each such quantity type: +To simplify quantity creation the library provides helper aliases for quantities of different +dimensions. Thanks to then the above example can be rewritten as follows: ```cpp -template -concept Length = QuantityOf; +units::si::length d(123); ``` -With that we can easily write a function template like this: +To simplify creations of compile-time known constants the library provides UDLs for each unit. +Thanks to them the same code can be as simple as: ```cpp -constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t) +using namespace units::si::literals; +auto d = 123km; // units::length +``` + +For brevity, the next examples will assume: + +```cpp +using namespace units; +``` + +Let's assume that the user wants to write the following code: + +```cpp +int main() +{ + using namespace si::literals; + auto v1 = avg_speed(220km, 2h); + auto v2 = avg_speed(140mi, 2h); + // ... +} +``` + +`avg_speed` is a simple function calculating an average speed from a distance and duration. It can +be implemented as: + +```cpp +constexpr si::velocity avg_speed(si::length d, si::time t) { return d / t; } ``` -Concepts usage in the function above guarantee correctness enforced in compile-time and the -guarantee that no unneeded intermediate conversions will ever be applied no matter which -units the user will decide to pass to such function template: +However, this function will perform unnecessary intermediate conversions (from kilometers to meters, +from hours to seconds, and from meters per second to kilometers per hour). To eliminate all that +overhead we have to write a template function: ```cpp -using namespace units; - -constexpr quantity distance(220); -constexpr quantity time(2); -constexpr Velocity speed = avg_speed(distance, time); - -static_assert(std::same_as, quantity>); -static_assert(speed.count() == 110); +template +constexpr auto avg_speed(si::length d, si::time t) +{ + return d / t; +} ``` -The units library also tries really hard to printing any quantity in the most user friendly fashion: +This function will work for every SI unit and representation without any unnecessary overhead. +It is also simple enough to ensure that the returned type is actually a velocity. However, +it might not always be the case. For more complicated calculations we would like to ensure +that we are returning a correct type and also inform the user of that fact in the function +template interface. Also we might want to implement a truly generic function that will work +efficiently not only with SI units but also with other systems of units like CGS. The solution +to this are C++20 concepts and generic functions. ```cpp -std::cout << speed << '\n'; -std::cout << quantity_cast(speed) << '\n'; -std::cout << avg_speed(140.mi, 2h) << '\n'; +constexpr Velocity auto avg_speed(Length auto d, Time auto t) +{ + return d / t; +} ``` -Try it on [Compiler Explorer](https://godbolt.org/z/OVpkT1). +The units library also tries really hard to print any quantity in the most user friendly +fashion: + +```cpp +int main() +{ + using namespace si::literals; + Velocity auto v1 = avg_speed(220km, 2h); + Velocity auto v2 = avg_speed(140mi, 2h); + + std::cout << v1 << '\n'; // 110 km/h + std::cout << quantity_cast(speed) << '\n'; // 30.5556 m/s + std::cout << v2 << '\n'; // 70 mi/h +} +``` ## Library design -`mp-units` library design rationale and documentation can be found in [doc/DESIGN.md](doc/DESIGN.md) +A detailed `mp-units` library design rationale and documentation can be found in +[doc/DESIGN.md](doc/DESIGN.md) ## Repository structure -That repository contains the following independent `cmake`-based projects: -- `./src` - header-only project for `units` -- `.` - project used for development needs that wraps `./src` project together with - usage examples and unit tests -- `./test_package` - library installation and Conan package verification +This repository contains three independent `cmake`-based projects: +1. `./src` - header-only project containing whole `mp-units` library +2. `.` - project used as an entry point for library development (it wraps `./src` project + together with usage examples and tests) +3. `./test_package` - library installation and Conan package verification -Please note that the projects depend on `cmake` git submodule in the `./cmake/common` +NOTE: Please note that this repository depends on a git submodule in the `./cmake/common` subdirectory. @@ -112,6 +181,16 @@ NOTE: This library as of now compiles correctly only with gcc-9.1 and newer. ## Release notes +- 0.5.0 ??? + - Major refactoring and rewrite of the library + - Units are now independent from dimensions + - Dimensions are now depended on units (base or coherent units are provided in a class template) + - Quantity gets a Dimension template parameter again (as unit does not provide information about + its dimension anymore) + - Added official CGS system support + - Added official data information system support + - Repository file tree cleanup + - 0.4.0 Nov 17, 2019 - Support for derived dimensions in `exp` added - Added `pow()` and `sqrt()` operations on quantities diff --git a/conanfile.py b/conanfile.py index 4463ad45..56035345 100644 --- a/conanfile.py +++ b/conanfile.py @@ -47,9 +47,10 @@ class UnitsConan(ConanFile): exports_sources = ["src/*", "test/*", "cmake/*", "example/*","CMakeLists.txt"] settings = "os", "compiler", "build_type", "arch" requires = ( - "range-v3/0.9.1@ericniebler/stable", - "Catch2/2.10.0@catchorg/stable", - "fmt/6.0.0" + "fmt/6.1.0" + ) + build_requires = ( + "Catch2/2.11.0@catchorg/stable" ) generators = "cmake" @@ -73,6 +74,10 @@ class UnitsConan(ConanFile): cmake.configure(source_folder="src", build_folder="src") return cmake + def requirements(self): + if Version(self.settings.compiler.version) < "10": + self.requires("range-v3/0.10.0@ericniebler/stable") + def build(self): cmake = self._configure_cmake() cmake.build() @@ -91,8 +96,7 @@ class UnitsConan(ConanFile): "-fconcepts", "-Wno-literal-suffix", "-Wno-non-template-friend", - "-Wno-stringop-overflow", - "-Wno-pedantic" + "-Wno-stringop-overflow" ] def package_id(self): diff --git a/doc/DESIGN.md b/doc/DESIGN.md index caa14c28..2d543005 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -1,9 +1,9 @@ -# `mp-units` - A Units Library for C++ +# `mp-units` - Design Overview ## Summary -`Units` is a compile-time enabled Modern C++ library that provides compile-time dimensional +`mp-units` is a compile-time enabled Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The basic idea and design heavily bases on `std::chrono::duration` and extends it to work properly with many dimensions. @@ -46,48 +46,237 @@ static_assert(10km / 5km == 2); ## Basic Concepts -Below UML diagram shows the most important entities in the library design and how they relate to -each other: +The most important concepts in the library are `Unit`, `Dimension`, and `Quantity`: -![UML](units_uml.png) +![Design UML](design.png) -### `Dimensions` +`Unit` is a basic building block of the library. Every dimension works with a concrete +hierarchy of units. Such hierarchy defines a reference unit and often a few scaled versions of +it. -`units::dimension` represents a derived dimension and is implemented as a type-list like type that -stores an ordered list of exponents of one or more base dimensions: +`Dimension` concept matches a dimension of either a base or derived quantity. `base_dimension` +is instantiated with a unique symbol identifier and a base unit. `derived_unit` is a list of +exponents of either base or other derived dimensions. + +`Quantity` is a concrete amount of a unit for a specified dimension with a specific +representation. + + +## `Unit` + +All units are represented in the framework by a `scaled_unit` class template: ```cpp -template -struct dimension : downcast_base> {}; +template +struct scaled_unit : downcast_base> { + using ratio = R; + using reference = U; +}; ``` -`units::Dimension` is a concept that is satisfied by a type that is empty and publicly -derived from `units::dimension` class template: +The above type is a framework's private type and the user should never instantiate it directly. +The public user interface to create units consists of: + +![Units UML](units.png) + +All below class templates indirectly derive from a `scaled_unit` class template and satisfy a +`Unit` concept: +- `unit` + - Defines a new unnamed, in most cases coherent derived unit of a specific derived + dimension and it should be passed in this dimension's definition. +- `named_unit` + - Defines a named, in most cases base or coherent unit that is then passed to a dimension's + definition. + - A named unit may be used by other units defined with the prefix of the same type, unless + `no_prefix` is provided for `PrefixType` template parameter (in such a case it is impossible + to define a prefixed unit based on this one). +- `named_scaled_unit` + - Defines a new named unit that is a scaled version of another unit. + - Such unit can be used by other units defined with the prefix of the same type, unless + `no_prefix` is provided for `PrefixType` template parameter (in such a case it is impossible + to define a prefixed unit based on this one). +- `prefixed_unit` + - Defines a new unit that is a scaled version of another unit by the provided prefix. + - It is only possible to create such a unit if the given prefix type matches the one defined + in a reference unit. +- `deduced_unit` + - Defines a new unit with a deduced ratio and symbol based on the recipe from the provided + derived dimension. + - The number and order of provided units should match the recipe of the derived dimension. + - All of the units provided should also be a named ones so it is possible to create a deduced + symbol text. + +Some of the above types depend on `PrefixType` and `no_prefix`. `PrefixType` is a concept that +is defined as: ```cpp template -concept Dimension = - std::is_empty_v && - detail::is_dimension>; // exposition only +concept PrefixType = std::derived_from; ``` -#### `Exponents` +where `prefix_type` is just an empty tag type used to identify the beginning of prefix types +hierarchy and `no_prefix` is one of its children: -`units::Exponent` concept is satisfied if provided type is an instantiation of `units::exp` class -template: +```cpp +struct prefix_type {}; +struct no_prefix : prefix_type {}; +``` + +Concrete prefix derives from a `prefix` class template: + +```cpp +template + requires (!std::same_as) +struct prefix; +``` + +You could notice that both units and above `prefix` class template take `Child` as a first +template parameter. `mp-units` library heavily relies on CRTP (Curiously Recurring Template +Parameter) idiom to provide the best user experience in terms of readability of compilation +errors and during debugging. It is possible thanks to the downcasting facility described later +in the design documentation. + +Coming back to units, here are a few examples of unit definitions: + +```cpp +namespace units::si { + +// prefixes +struct prefix : prefix_type {}; +struct centi : units::prefix> {}; +struct kilo : units::prefix> {}; + +// length +struct metre : named_unit {}; +struct centimetre : prefixed_unit {}; +struct kilometre : prefixed_unit {}; +struct yard : named_scaled_unit, metre> {}; +struct mile : named_scaled_unit, yard> {}; + +// time +struct second : named_unit {}; +struct hour : named_scaled_unit, second> {}; + +// velocity +struct metre_per_second : unit {}; +struct kilometre_per_hour : deduced_unit {}; +struct mile_per_hour : deduced_unit {}; + +} +``` + +Please note that thanks to C++20 features we are able to provide all information about the unit +(including text output) in a single line of its type definition. There is no need to specialize +additional type traits or use preprocessor macros. + + +## `Dimension` + +`Dimension` is either a `BaseDimension` or a `DerivedDimension`: ```cpp template -concept Exponent = - detail::is_exp; // exposition only +concept Dimension = BaseDimension || DerivedDimension; ``` -`units::exp` provides an information about a single dimension and its (possibly fractional) -exponent in a derived dimension. +### `BaseDimension` + +According to ISO 80000 a base quantity is a quantity in a conventionally chosen subset of a +given system of quantities, where no quantity in the subset can be expressed in terms of the +other quantities within that subset. They are referred to as being mutually independent since a +base quantity cannot be expressed as a product of powers of the other base quantities. Base unit +is a measurement unit that is adopted by convention for a base quantity in a specific system of +units. + +`base_dimension` represents a dimension of a base quantity and is identified with a pair of +an unique compile-time text describing the dimension symbol and a base unit adopted for this +dimension: ```cpp -template - requires BaseDimension || Dimension +template + requires U::is_named +struct base_dimension { + static constexpr auto symbol = Symbol; + using base_unit = U; +}; +``` + +Pair of symbol and unit template parameters form an unique identifier of the base dimension. +These identifiers provide total ordering of exponents of base dimensions in a derived dimension. + +The SI physical units system defines 7 base dimensions: + +```cpp +namespace units::si { + +struct dim_length : base_dimension<"L", metre> {}; +struct dim_mass : base_dimension<"M", kilogram> {}; +struct dim_time : base_dimension<"T", second> {}; +struct dim_electric_current : base_dimension<"I", ampere> {}; +struct dim_thermodynamic_temperature : base_dimension<"Θ", kelvin> {}; +struct dim_substance : base_dimension<"N", mole> {}; +struct dim_luminous_intensity : base_dimension<"J", candela> {}; + +} +``` + +All other physical dimensions are derived from those. + +There are two reasons why `base_dimension` gets a unit as its template parameter. First, the +base unit is needed for the text output of unnamed derived units. Second, there is more than +one system of physical units. For example CGS definitions look as follows: + +```cpp +namespace units::cgs { + +using si::centimetre; +using si::gram; +using si::second; + +struct dim_length : base_dimension<"L", centimetre> {}; +struct dim_mass : base_dimension<"M", gram> {}; +using si::dim_time; + +} +``` + +Equivalent base dimensions in different systems have the same symbol identifier and get units +from the same hierarchy (with the same reference in `scaled_unit`). Thanks to that we have +the ability to explicitly cast quantities of the same dimension from different systems or +even mix them in one `derived_dimension` definition. + + +### `DerivedDimension` + +According to ISO 80000 a derived quantity is a quantity, in a system of quantities, defined in +terms of the base quantities of that system. Dimension of such quantity is an expression of the +dependence of a quantity on the base quantities of a system of quantities as a product of +powers of factors corresponding to the base quantities, omitting any numerical factors. A power +of a factor is the factor raised to an exponent. Each factor is the dimension of a base +quantity. + +A derived dimension used internally in a library framework is implemented as a type-list like +type that stores an ordered list of exponents of one or more base dimensions: + +```cpp +namespace detail { + +template + requires (BaseDimension && ... && BaseDimension) +struct derived_dimension_base; + +} +``` + +A derived dimension can be formed from multiple exponents (i.e. velocity is represented as +`exp, exp`). It is also possible to form a derived dimension with only one exponent +(i.e. frequency is represented as just `exp`). + +Exponents are implemented with `exp` class template that provides an information about a single +dimension and its (possibly fractional) exponent in a derived dimension. + +```cpp +template struct exp { using dimension = Dim; static constexpr int num = Num; @@ -95,433 +284,236 @@ struct exp { }; ``` -Both a base dimension and a derived dimension can be provided to `units::exp` class template. +In order to be able to perform computations on an arbitrary set of exponents, +`derived_dimension_base` class template have to obey the following rules: +- it contains only base dimensions in the list of exponents, +- base dimensions are not repeated in a list (the exponent of each base dimension is provided + at most once), +- exponents of base dimensions are consistently ordered, +- in case the numerator of the exponent equals zero such base dimension is erased from the list. -`units::base_dimension` represents a base dimension and has assigned a unique compile-time text -describing the dimension name: +Above is needed for the framework to provide dimensional analysis. However, sometimes it is +useful to define derived units in terms of other derived units. To support this both a base +dimension and a derived dimension can be provided to `exp` class template. + +As it was stated above `derived_dimension_base` is a private utility of the framework. In order +to define a new derived dimension the user has to instantiate the following class template: ```cpp -template -struct base_dimension { - using name = Name; - using symbol = Symbol; +template +struct derived_dimension : downcast_child> { + using recipe = exp_list; + using coherent_unit = U; + using base_units_ratio = /* see below */; }; ``` -`units::BaseDimension` is a concept to match all types derived from `base_dimension` instantiations: +There are a few important differences between `detail::derived_dimension_base` and +`derived_dimension`. First, the latter one gets the coherent unit of the derived dimension. + +According to ISO 80000 a coherent unit is a unit that, for a given system of quantities and for +a chosen set of base units, is a product of powers of base units with no other proportionality +factor than one. + +The other difference is that `derived_dimension` allows to provide other derived dimensions in +the list of its exponents. This is called a "recipe" of the dimension and among others is used +to print unnamed coherent units of this dimension. + +In case a derived dimension appears on the list of exponents, such derived dimension will be +unpacked, sorted, and consolidated by a `detail::make_dimension` helper to form a valid list +of exponents of only base dimensions later provided to `detail::derived_dimension_base`. + +Sometimes units of equivalent quantities in different systems of units do not share the same +reference so they cannot be easily converted to each other. An example can be a pressure for +which a coherent unit in SI is pascal and in CGS barye. Those two units are not directly +related with each other with some ratio. As they both are coherent units of their dimensions, +the ratio between them is directly determined by the ratios of base units defined in base +dimensions end their exponents in the derived dimension recipe. To provide interoperability of +such quantities of different systems `base_units_ratio` is being used. The result of the +division of two `base_units_ratio` of two quantities of equivalent dimensions in two different +systems gives a ratio between their coherent units. Alternatively, the user would always have to +directly define a barye in terms of pascal or vice versa. + +Below are a few examples of derived dimension definitions: ```cpp -template -concept BaseDimension = std::is_empty_v && - requires { - typename T::name; - typename T::symbol; - } && - std::derived_from>; +namespace units::si { + +struct dim_velocity : derived_dimension, exp> {}; + +struct dim_acceleration : derived_dimension, exp> {}; + +struct dim_force : derived_dimension, exp> {}; + +struct dim_energy : derived_dimension, exp> {}; + +struct dim_power : derived_dimension, exp> {}; + +} ``` -For example here is a list of SI base dimensions: +If as a result of dimensional computation the library framework will generate a derived +dimension that was not predefined by the user than the instance of +`unknown_dimension`. The coherent unit of such an unknown dimension is +`scaled_unit, unknown_unit>`. -```cpp -struct base_dim_length : base_dimension<"length", "m"> {}; -struct base_dim_mass : base_dimension<"mass", "kg"> {}; -struct base_dim_time : base_dimension<"time", "s"> {}; -struct base_dim_current : base_dimension<"current", "A"> {}; -struct base_dim_temperature : base_dimension<"temperature", "K"> {}; -struct base_dim_substance : base_dimension<"substance", "mol"> {}; -struct base_dim_luminous_intensity : base_dimension<"luminous intensity", "cd"> {}; -``` -In order to be able to perform computations on arbitrary sets of exponents, an important -property of `units::dimension` is that its base dimensions: -- are not repeated in a list (the exponent of each base dimension is provided at most once), -- are consistently ordered, -- exponents with ::num of zero are elided +## `Quantity` -#### `derived_dimension` - -Above design of dimensions is created with the ease of use for end users in mind. Compile-time -errors should provide as short as possible template instantiations strings that should be easy to -understand by every engineer. Also types visible in a debugger should be easy to understand. -That is why `units::dimension` type for derived dimensions always stores information about only -those base dimensions that are used to form that derived dimension. - -However, such an approach have some challenges: - -```cpp -constexpr Velocity auto v1 = 1_m / 1s; -constexpr Velocity auto v2 = 2 / 2s * 1m; - -static_assert(std::same_as); -static_assert(v1 == v2); -``` - -Above code, no matter what is the order of the base dimensions in an expression forming our result, -must produce the same `Velocity` type so that both values can be easily compared. In order to achieve -that, `dimension` class templates should never be instantiated manually but through a `derived_dimension` -helper: - -```cpp -template -struct derived_dimension : downcast_child::type> {}; -``` - -`Child` class template parameter is a part of a CRTP idiom and is used to provide a downcasting facility -described later in this document. - -So for example to create a `velocity` type we have to do: - -```cpp -struct velocity : derived_dimension, exp> {}; -``` - -In order to make `derived_dimension` work as expected it has to provide unique ordering for -contained base dimensions. Beside providing ordering to base dimensions it also has to: -- aggregate two arguments of the same base dimension but different exponents -- eliminate two arguments of the same base dimension and with opposite equal exponents - -`derived_dimension` is also able to form a dimension type based not only on base dimensions but -it can take other derived dimensions as well. In such a case units defined with a -`coherent_derived_unit` and `deduced_derived_unit` helper will get symbols of units for those -derived dimension (if those are named units) rather than system base units. - -For example to form `pressure` user can provide the following two definitions: - -```cpp -struct pressure : derived_dimension, exp, exp> {}; -``` - -or - -```cpp -struct pressure : derived_dimension, exp> {}; -``` - -In the second case `derived_dimension` will extract all derived dimensions into the list of -exponents of base dimensions. Thanks to this both cases will result with exactly the same base -class formed only from the exponents of base units but in the second case the recipe to form -a derived dimension will be remembered and used for `deduced_derived_unit` usage. - -#### `merge_dimension` - -`units::merge_dimension` is a type alias that works similarly to `derived_dimension` but instead -of sorting the whole list of base dimensions from scratch it assumes that provided input `dimension` -types are already sorted as a result of `derived_dimension`. Also contrary to `derived_dimension` -it works only with exponents of bas dimensions (no derived dimensions allowed). - -Typical use case for `merge_dimension` is to produce final `dimension` return type of multiplying -two different dimensions: - -```cpp -template -struct dimension_multiply; - -template -struct dimension_multiply, dimension> { - using type = downcast_traits_t, dimension>>; -}; - -template -using dimension_multiply = dimension_multiply::type; -``` - - -### `Units` - -`units::unit` is a class template that expresses the unit of a specific physical dimension: - -```cpp -template - requires (R::num * R::den > 0) -struct unit : downcast_base> { - using dimension = D; - using ratio = R; -}; -``` - -`units::Unit` is a concept that is satisfied by a type that is empty and publicly -derived from `units::unit` class template: - -```cpp -template -concept Unit = - std::is_empty_v && - detail::is_unit>; // exposition only -``` - -The library provides a bunch of helpers to create the derived unit: -- `named_coherent_derived_unit` -- `coherent_derived_unit` -- `named_scaled_derived_unit` -- `named_deduced_derived_unit` -- `deduced_derived_unit` -- `prefixed_derived_unit` - -Coherent derived units (units with `ratio<1>`) are created with a `named_coherent_derived_unit` -or `coherent_derived_unit` class templates: - -```cpp -template -struct named_coherent_derived_unit : downcast_child>> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; -}; - -template -struct coherent_derived_unit : downcast_child>> { - static constexpr bool is_named = false; - static constexpr auto symbol = /* ... */; - using prefix_type = no_prefix; -}; -``` - -The above exposes public `prefix_type` member type and `symbol` used to print unit symbol -names. `prefix_type` is a tag type used to identify the type of prefixes to be used (i.e. SI, -data) and should satisfy the following concept: - -```cpp -template -concept PrefixType = std::derived_from; -``` - -For example to define the named coherent unit of `length`: - -```cpp -struct metre : named_coherent_derived_unit {}; -``` - -Again, similarly to `derived_dimension`, the first class template parameter is a CRTP idiom used -to provide downcasting facility (described below). - -`coherent_derived_unit` also provides a synthetized unit symbol. If all coherent units of -the recipe provided in a `derived_dimension` are named than the recipe is used to built a -unit symbol. Otherwise, the symbol will be created based on ingredient base units. - -```cpp -struct surface_tension : derived_dimension, exp> {}; -struct newton_per_metre : coherent_derived_unit {}; -``` - -To create scaled unit the following template should be used: - -```cpp -template -struct named_scaled_derived_unit : downcast_child> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; -}; -``` - -For example to define `minute`: - -```cpp -struct minute : named_scaled_derived_unit> {}; -``` - -The `mp-units` library provides also a helper class templates to simplify the above process. -For example to create a prefixed unit the following may be used: - -```cpp -template - requires (!std::same_as) -struct prefixed_derived_unit : downcast_child>> { - static constexpr bool is_named = true; - static constexpr auto symbol = P::symbol + U::symbol; - using prefix_type = P::prefix_type; -}; -``` - -where `Prefix` is a concept requiring the instantiation of the following class template: - -```cpp -template -struct prefix : downcast_child> { - static constexpr auto symbol = Symbol; -}; -``` - -With this to create prefixed units user does not have to specify numeric value of the prefix ratio -or its symbol and just has to do the following: - -```cpp -struct kilometre : prefixed_derived_unit {}; -``` - -SI prefixes are predefined in the library and the user may easily provide his/her own with: - -```cpp -struct data_prefix : units::prefix_type {}; - -struct kibi : units::prefix, "Ki"> {}; -struct mebi : units::prefix, "Mi"> {}; -``` - -For the cases where determining the exact ratio is not trivial another helper can be used: - -```cpp -template -struct named_deduced_derived_unit : downcast_child { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; -}; - -template - requires U::is_named && (Us::is_named && ... && true) -struct deduced_derived_unit : downcast_child { - static constexpr bool is_named = false; - static constexpr auto symbol = /* even more magic to get the correct unit symbol */; - using prefix_type = no_prefix; -}; -``` - -This will deduce the ratio based on the ingredient units and their relation defined in the -dimension. For example to create a deduced velocity unit a user can do: - -```cpp -struct kilometre_per_hour : deduced_derived_unit {}; -``` - -`deduced_derived_unit` has also one more important feature. It is able to synthesize a unit -symbol: -- in case all units on the list are the units of base dimension (i.e. above we have a `kilometre` - that is a unit of a base dimension `length`, and `hour` a unit of base dimension `time`), - the resulting unit symbol will be created using base dimensions (`km/h`). -- if at least one non-base dimension unit exists in a list than the recipe provided in the - `derived_dimension` will be used to create a unit. For example: - - ```cpp - struct surface_tension : derived_dimension, exp> {}; - struct newton_per_centimetre : deduced_derived_unit {}; - ``` - - will result with a symbol `N/cm` for `newton_per_centimetre`. - - -### `Quantities` - -`units::quantity` is a class template that expresses the quantity/amount of a specific dimension +`quantity` is a class template that expresses the quantity/amount of a specific dimension expressed in a specific unit of that dimension: ```cpp -template -class quantity; +template U, Scalar Rep = double> +class quantity ``` -`units::Quantity` is a concept that is satisfied by a type that is an instantiation of -`units::quantity` class template: +`quantity` provides a similar interface to `std::chrono::duration`. The difference is that it +uses `double` as a default representation and has a few additional member types and +functions as below: ```cpp -template -concept Quantity = - detail::is_quantity; // exposition only -``` - -`units::quantity` provides the interface really similar to `std::chrono::duration`. The difference is that -it uses `double` as a default representation and has a few additional member types and functions as below: - -```cpp -template +template U, Scalar Rep = double> class quantity { public: + using dimension = D; using unit = U; using rep = Rep; - using dimension = U::dimension; - - [[nodiscard]] static constexpr quantity one() noexcept { return quantity(quantity_values::one()); } - - template - requires std::same_as> - [[nodiscard]] constexpr Scalar operator*(const quantity& lhs, - const quantity& rhs); - - template - requires (!std::same_as>) && - (treat_as_floating_point || - (std::ratio_multiply::den == 1)) - [[nodiscard]] constexpr Quantity operator*(const quantity& lhs, - const quantity& rhs); - - template - [[nodiscard]] constexpr Quantity operator/(const Rep1& v, - const quantity& q); - - template - requires std::same_as - [[nodiscard]] constexpr Scalar operator/(const quantity& lhs, - const quantity& rhs); - - template - requires (!std::same_as) && - (treat_as_floating_point || - (ratio_divide::den == 1)) - [[nodiscard]] constexpr Quantity operator/(const quantity& lhs, - const quantity& rhs); + [[nodiscard]] static constexpr quantity one() noexcept; // ... }; + +template + requires detail::basic_arithmetic && equivalent_dim> +[[nodiscard]] constexpr Scalar auto operator*(const quantity& lhs, + const quantity& rhs); + +template + requires detail::basic_arithmetic && (!equivalent_dim>) +[[nodiscard]] constexpr Quantity auto operator*(const quantity& lhs, + const quantity& rhs); + +template + requires std::magma +[[nodiscard]] constexpr Quantity auto operator/(const Value& v, + const quantity& q); + +template + requires detail::basic_arithmetic && equivalent_dim +[[nodiscard]] constexpr Scalar auto operator/(const quantity& lhs, + const quantity& rhs); + +template + requires detail::basic_arithmetic && (!equivalent_dim) +[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, + const quantity& rhs); ``` -Additional functions provide the support for operations that result in a different dimension type -than those of their arguments. +Additional functions provide the support for operations that result in a different dimension +type than those of their arguments. `equivalent_dim` constraint requires two dimensions to be +either the same or have convertible units of base dimension (with the same reference unit). Beside adding new elements a few other changes where applied compared to the `std::chrono::duration` class: 1. The `duration` is using `std::common_type_t` to find a common representation - for a calculation result. Such a design was reported as problematic by numerics study group members - as sometimes we want to provide a different type in case of multiplication and different in case of - division. `std::common_type` lacks that additional information. That is why `units::quantity` uses - the resulting type of a concrete operator operation and provides it directly to `units::common_quantity_t` - type trait. + for a calculation result. Such a design was reported as problematic by SG6 (numerics study group) members + as sometimes we want to provide a different type in case of multiplication and different in case of + division. `std::common_type` lacks that additional information. That is why `units::quantity` uses + the resulting type of a concrete operator operation. 2. `operator %` is constrained with `treat_as_floating_point` type trait to limit the types to integral representations only. Also `operator %(Rep)` takes `Rep` as a template argument to limit implicit conversions. +To simplify writing efficient generic code quantities of each dimension have associated: +1. Concept (i.e. `units::Length`) that matches a length dimension of any physical systems. +2. Per-system quantity alias (i.e. `units::si::length` for + `units::quantity`). -#### `quantity_cast` +Also, to help instantiate quantities with compile-time known values every unit in the library +has an associated UDL. For example: + +```cpp +namespace si::inline literals { + +// m +constexpr auto operator"" m(unsigned long long l) { return length(l); } +constexpr auto operator"" m(long double l) { return length(l); } + +// km +constexpr auto operator"" km(unsigned long long l) { return length(l); } +constexpr auto operator"" km(long double l) { return length(l); } + +} +``` + + +### `quantity_cast` To explicitly force truncating conversions `quantity_cast` function is provided which is a direct counterpart of `std::chrono::duration_cast`. As a template argument user can provide here either -a `quantity` type or only its template parameters (`Unit`, `Rep`): +a `quantity` type or only its template parameters (`Dimension`, `Unit`, or `Rep`): ```cpp -template - requires same_dim -[[nodiscard]] constexpr To quantity_cast(const quantity& q); +template + requires QuantityOf && + detail::basic_arithmetic> +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); -template -[[nodiscard]] constexpr quantity quantity_cast(const quantity& q); +template + requires equivalent_dim +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); -template -[[nodiscard]] constexpr quantity quantity_cast(const quantity& q); +template + requires UnitOf +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); -template -[[nodiscard]] constexpr quantity quantity_cast(const quantity& q); +template + requires detail::basic_arithmetic> +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); ``` -#### `operator<<` +## Text output -The library tries its best to print a correct unit of the quantity. This is why it performs a series -of checks: -1. If the user predefined a unit with a `named_XXX_derived_unit` class templates, the symbol provided +### Unit Symbol + +The library tries its best to print a correct unit of the quantity. This is why it performs +a series of checks: +1. If the user predefined a unit with a `named_XXX_unit` class templates, the symbol provided by the user will be used (i.e. `60 W`). -2. If a quantity has an unknown unit for a dimension predefined by the user with `derived_dimension`, - the symbol of a coherent unit of this dimension will be used. Additionally: - - if `Prefix` template parameter of a `coherent_derived_unit` is different than `no_prefix` then - the prefix symbol (i.e. `8 cJ`) defined by the specialization of `units::prefix_symbol` will be - aded wherever possible (`Ratio` matches the prefix ratio), +2. If a unit was created with a `deduced_unit` class template, the symbol of deduced unit is + printed (i.e. `70 km/h`). +3. Otherwise, the library tries to print a prefix and symbol of an unknown unit for this derived + dimension: + - prefix: + - if ratio of the scaled unit is `1`, than no prefix is being printed, + - otherwise, if `PrefixType` template parameter of a reference unit is different than + `no_prefix`, and if the ratio of scaled unit matches the ratio of a prefix of a specified + type, than the symbol of this prefix will be used, - otherwise, non-standard ratio (i.e. `2 [60]Hz`) will be printed. -3. If a quantity has an unknown dimension, the symbols of base dimensions will be used to construct - a unit symbol (i.e. `2 m/kg^2`). In this case no prefix symbols are added. + - symbol: + - if a reference unit has a user-predefined or deduced symbol, than this symbol it is being + printed, + - otherwise, the symbol is constructed from names and exponents of base dimensions + (i.e. `2 m/kg^2`). -#### Text Formatting + +### `operator<<` + +`quantity::operator<<()` provides only a basic support to print a quantity. It prints its count +and a symbol separated with one space character. + + +### Text Formatting `mp-units` supports new C++20 formatting facility (currently provided as a dependency on -[`fmt`](https://github.com/fmtlib/fmt) library). `parse` member functions of facility -formatters interpret the format specification as a `units-format-spec` according to the +[`fmt`](https://github.com/fmtlib/fmt) library). `parse()` member functions of +`fmt::formatter, CharT>` class template partial +specialization interprets the format specification as a `units-format-spec` according to the following syntax: ```text @@ -544,7 +536,7 @@ type: one of The productions `fill-and-align`, `sign`, `width`, and `precision` are described in [Format string](https://wg21.link/format.string.std) chapter of the C++ standard. Giving a `precision` specification in the `units-format-spec` is valid only for `units::quantity` types -where the representation type `Rep` is a floating point type. For all other `Rep` types, an +where the representation type `Rep` is a floating-point type. For all other `Rep` types, an exception of type `format_error` is thrown if the `units-format-spec` contains a precision specification. An `format_error` is also thrown if `sign` is provided with a `conversion-spec` to print quantity unit but not its value. @@ -569,141 +561,95 @@ std::string s = fmt::format("{:=>12}", 120_kmph); // value of s is "====120 km/h ``` -## Strong types instead of aliases, and type downcasting facility +## Improving user's experience -Most of the important design decisions in the library are dictated by the requirement of providing -the best user experience as possible. +Most of the important design decisions in the library are dictated by the requirement of +providing the best user experience as possible. -For example with template aliases usage the following code: +Most of C++ libraries in the world use template aliases to provide a friendly name for a +developer. Unfortunately, such aliases are quickly lost in a compilation process and as a +result the potential error log contains a huge source type rather than a short alias for it. +The same can be observed during debugging of a code using template aliases. + +Let's assume that we want to provide a user friendly name for a capacitance derived dimension. +Other libraries will do it in the following way: ```cpp -const Velocity auto t = 20s; +using dim_capacitance = detail::derived_dimension_base, + exp, + exp, + exp>; ``` -could generate a following compile time error: +The above solution does provide a good developer's experience but a really poor one for the end +user. If we will get a compilation error message containing `dim_capacitance` in most cases +the compiler will print the following type instead of the alias: ```text -\example\example.cpp:39:22: error: deduced initializer does not satisfy placeholder constraints - const Velocity auto t = 20s; - ^~~~ -In file included from \example\example.cpp:23: -/src/include/units/si/velocity.h:41:16: note: within 'template concept const bool units::Velocity [with T = units::quantity >, std::ratio<1> >, long long int>]' - concept Velocity = Quantity && std::same_as; - ^~~~~~~~ -In file included from /src/include/units/bits/tools.h:25, - from /src/include/units/dimension.h:25, - from /src/include/units/si/base_dimensions.h:25, - from /src/include/units/si/velocity.h:25, - from \example\example.cpp:23: -/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::same_as [with T = units::dimension >; U = units::dimension,units::exp >]' - concept same_as = std::is_same_v; - ^~~~ -/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false +units::detail::derived_dimension_base, +units::exp, units::exp, +units::exp > ``` -Time and velocity are not that complicated dimensions and there are much more complicated dimensions -out there, but even for those dimensions +You can notice that even this long syntax was carefully selected to provide quite good user +experience (some other units libraries produce a type that cannot easily fit on one slide) +but it is not questionable less readable than just `dim_capacitance`. -```text -[with T = units::quantity >, std::ratio<1> >, long long int>] -``` +NOTE: To better understand how the framework works and not clutter the text and graphs with +long types in the following examples we will switch from `dim_capacitance` to `dim_area`. +The latter one has much shorter definition but the end result for both will be exactly the same. +User-friendly, short name printed by the compiler and the debugger. -and - -```text -[with T = units::dimension >; U = units::dimension,units::exp >] -``` - -starts to be really hard to analyze or debug. - -That is why it was decided to provide automated downcasting capability when possible. Thanks to this feature the -same code will result with such an error: - -```text -\example\example.cpp:40:22: error: deduced initializer does not satisfy placeholder constraints - const Velocity t = 20s; - ^~~~ -In file included from \example\example.cpp:23: -/src/include/units/si/velocity.h:48:16: note: within 'template concept const bool units::Velocity [with T = units::quantity]' - concept Velocity = Quantity && std::same_as; - ^~~~~~~~ -In file included from /src/include/units/bits/tools.h:25, - from /src/include/units/dimension.h:25, - from /src/include/units/si/base_dimensions.h:25, - from /src/include/units/si/velocity.h:25, - from \example\example.cpp:23: -/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::same_as [with T = units::time; U = units::velocity]' - concept same_as = std::is_same_v; - ^~~~ -/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false -``` - -Now - -```text -[with T = units::quantity] -``` - -and - -```text -[with T = units::time; U = units::velocity] -``` - -are not arguably much easier to understand thus provide better user experience. - -When dealing with simple types, aliases can be easily replaced with inheritance: +To fix it we have to provide a strong type. As we do not have opaque/strong typedefs +in the language we have to use inheritance: ![UML](downcast_1.png) -As a result we get strong types. There are however a few issues with such an approach: -- generic code getting a child class does not easily know the exact template parameters of - the base class -- generic code after computing the instantiation of the class template does not know if - this is a base class in some hierarchy, and in case it is, it does not know how to - replace the base class template instantiation with a derived strong type. +This gives us a nice looking strong type but does not solve the problem of how to switch from +a long instantiation of a `derived_dimension_base` class template that was generated by the +framework as a result of dimensional calculation to a child class assigned by the user for this +instantiation. -Downcasting facility provides such a type substitution mechanism. It connects a specific primary -template class instantiation with a strong type assigned to it by the user. +### Downcasting facility -Here is the overview of resulting class hierarchy for our example: +To support this `mp-units` library introduces a new downcasting facility implemented fully as +a library feature. It creates 1-to-1 link between a long class template instantiation and a +strong type provided by the user. This means that only one child class can be created for a +specific base class template instantiation. + +Downcasting facility is provided by injecting two classes into our hierarchy: ![UML](downcast_2.png) -In the above example `metre` is a downcasting target (child class) and a specific `unit` class -template instantiation is a downcasting source (base class). The downcasting facility provides -1 to 1 type substitution mechanism. Only one child class can be created for a specific base class -template instantiation. - -Downcasting facility is provided through 2 dedicated types, a concept, and a few helper template -aliases. +In the above example `dim_area` is a downcasting target (child class) and a specific +`detail::derived_dimension` class template instantiation is a downcasting source (base class). ```cpp template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; friend auto downcast_guide(downcast_base); // declaration only (no implementation) }; ``` `units::downcast_base` is a class that implements CRTP idiom, marks the base of downcasting -facility with a `base_type` member type, and provides a declaration of downcasting ADL friendly -(Hidden Friend) entry point member function `downcast_guide`. An important design point is that -this function does not return any specific type in its declaration. This non-member function -is going to be defined in a child class template `downcast_child` and will return a target -type of the downcasting operation there. +facility with a `downcast_base_type` member type, and provides a declaration of downcasting ADL +friendly (Hidden Friend) entry point member function `downcast_guide`. An important design point +is that this function does not return any specific type in its declaration. This non-member +function is going to be defined in a child class template `downcast_child` and will return a +target type of the downcasting operation there. ```cpp template concept Downcastable = requires { - typename T::base_type; + typename T::downcast_base_type; } && - std::derived_from>; + std::derived_from>; ``` -`units::Downcastable` is a concepts that verifies if a type implements and can be used in a downcasting -facility. +`units::Downcastable` is a concepts that verifies if a type implements and can be used in a +downcasting facility. ```cpp template @@ -714,27 +660,12 @@ struct downcast_child : T { `units::downcast_child` is another CRTP class template that provides the implementation of a non-member friend function of the `downcast_base` class template which defines the target -type of a downcasting operation. It is used in the following way to define `dimension` and -`unit` types in the library: - -```cpp -template -struct derived_dimension : downcast_child> {}; -``` - -```cpp -template -struct derived_unit : downcast_child>> {}; -``` +type of a downcasting operation. With such CRTP types the only thing the user has to do to register a new type to the downcasting facility is to publicly derive from one of those CRTP types and provide its new child type as the first template parameter of the CRTP type. -```cpp -struct metre : named_derived_unit {}; -``` - Above types are used to define base and target of a downcasting operation. To perform the actual downcasting operation a dedicated template alias is provided: @@ -744,19 +675,9 @@ using downcast = decltype(detail::downcast_target_impl()); ``` `units::downcast` is used to obtain the target type of the downcasting operation registered -for a given instantiation in a base type. - -For example to determine a downcasted type of a quantity multiply operation the following can be done: - -```cpp -using dim = dimension_multiply; -using common_rep = decltype(lhs.count() * rhs.count()); -using ret = quantity>>, common_rep>; -``` - -`detail::downcast_target_impl` checks if a downcasting target is registered for the specific base class. -If yes, it returns the registered type, otherwise it works like a regular identity type returning -a provided base class. +for a given instantiation in a base type. `detail::downcast_target_impl` checks if a downcasting +target is registered for the specific base class. If yes, it returns the registered type, +otherwise it works like a regular identity type returning a provided base class. ```cpp namespace detail { @@ -778,76 +699,159 @@ namespace detail { } ``` -Additionally there is on more simple helper alias provided that is used in the internal +Additionally there is one more simple helper alias provided that is used in the internal library implementation: ```cpp template -using downcast_base_t = T::base_type; +using downcast_base_t = T::downcast_base_type; ``` -## Adding custom dimensions and units +### `unknown_dimension` -In order to extend the library with custom dimensions the user has to: -1. Create a new base dimension if the predefined ones are not enough to form a new derived dimension: +Sometimes dimensional calculation results with a class template instantiation that was not +predefined by the user in the downcasting facility. A typical example of such a case are +temporary results of calculations: + +```cpp +units::Length auto d1 = 123m; +units::Time auto t1 = 10s; +units::Velocity auto v1 = avg_speed(d1, t1); + +auto temp1 = v1 * 50m; // intermediate unknown dimension + +units::Velocity auto v2 = temp1 / 100m; // back to known dimensions again +units::Length auto d2 = v2 * 60s; +``` + +To provide support to form an unknown derived dimension that could be than be converted to a +known one with a correct unit, and also to improve the user experience and clearly state that +it is an unknown dimension the library framework will provide an instance of: + +```cpp +struct unknown_unit : unit {}; + +template +struct unknown_dimension : derived_dimension, + scaled_unit, unknown_unit>, + E, ERest...> { + using coherent_unit = scaled_unit, unknown_unit>; +}; +``` + +with this the error log or a debugger breakpoint involving a `temp1` type will include: + +```text +units::quantity, +units::exp >, units::unknown_unit, long int> +``` + + +## Extensibility + +The library was designed with a simple extensibility in mind. It is easy to add new units, +dimensions, and prefixes. The systems of units are not closed (classes) but open (namespaces) +and can be easily extended, or its content can be partially/fully imported to other systems. + + +### Adding a new system with custom dimensions and units + +A great example of a adding a whole new system can be a `data` system in the library which +adds support for digital information quantities. In summary it adds: +1. New prefix type and its prefixes: ```cpp - struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; - ``` + namespace units::data { -2. Create a new dimension type with the recipe of how to construct it from base dimensions and - register it for a downcasting facility: + struct prefix : prefix_type {}; - ```cpp - struct digital_information : units::derived_dimension> {}; - ``` + struct kibi : units::prefix> {}; + struct mebi : units::prefix> {}; -3. Define a concept that will match a new dimension: - - ```cpp - template - concept DigitalInformation = units::QuantityOf; - ``` - -4. If non-SI prefixes should be applied to the unit symbol, define a new prefix tag and define - new prefixes using this tag and provide their ratio and symbol: - - ```cpp - struct data_prefix; - - struct kibi : units::prefix, "Ki"> {}; - struct mebi : units::prefix, "Mi"> {}; - ``` - -5. Define units and register them to a downcasting facility: - - ```cpp - struct bit : units::named_coherent_derived_unit {}; - struct kilobit : units::prefixed_derived_unit {}; - - struct byte : units::named_derived_unit> {}; - struct kilobyte : units::prefixed_derived_unit {}; - ``` - -6. Provide user-defined literals for the most important units: - - ```cpp - inline namespace literals { - constexpr auto operator""_b(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_b(long double l) { return units::quantity(l); } - - constexpr auto operator""_B(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_B(long double l) { return units::quantity(l); } } ``` -## Adding custom representations +2. New units for `information`: + + ```cpp + namespace units::data { + + struct bit : named_unit {}; + struct kibibit : prefixed_unit {}; + + struct byte : named_scaled_unit, bit> {}; + struct kibibyte : prefixed_unit {}; + + } + ``` + +3. New base dimension, its concept, and quantity alias: + + ```cpp + namespace units::data { + + struct dim_information : base_dimension<"information", bit> {}; + + template + concept Information = QuantityOf; + + template + using information = quantity; + + } + ``` + +4. UDLs for new units + + ```cpp + namespace units::data::inline literals { + + // bits + constexpr auto operator""b(unsigned long long l) { return information(l); } + constexpr auto operator""Kib(unsigned long long l) { return information(l); } + + // bytes + constexpr auto operator""B(unsigned long long l) { return information(l); } + constexpr auto operator""KiB(unsigned long long l) { return information(l); } + + } + ``` + +5. A new `bitrate` derived dimension, its units, concept, quantity helper, and UDLs + + ```cpp + namespace units::data { + + struct bit_per_second : unit {}; + struct dim_bitrate : derived_dimension, exp> {}; + + struct kibibit_per_second : deduced_unit {}; + + template + concept Bitrate = QuantityOf; + + template + using bitrate = quantity; + + inline namespace literals { + + // bits + constexpr auto operator""_bps(unsigned long long l) { return bitrate(l); } + constexpr auto operator""_Kibps(unsigned long long l) { return bitrate(l); } + + } + + } + ``` + + +### Using custom representations In theory `quantity` can take any arithmetic-like type as a `Rep` template parameter. In practice some interface is forced by numeric concepts. -To provide basic library functionality the type should satisfy the following concept: +To provide basic library functionality the type should satisfy the `Scalar` concept: ```cpp template @@ -897,7 +901,6 @@ the equivalent operation for `Rep` type. Here is an additional list of opt-in op - `operator*=(Rep)` - `operator/=(Rep)` - `operator%=(Rep)` -- `operator%=(Rep)` - `operator%(Rep, Rep)` `quantity` also has 4 static functions `zero()`, `one()`, `min()`, and `max()` which can diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 588056bf..6eba2684 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -39,18 +39,18 @@ steps may be done: ``` - add `units` as a dependency to your `conan` file. For example to use testing version of - `0.4.0` of `mp-units` add: + `0.5.0` of `mp-units` add: - `conanfile.txt` ```text [requires] - mp-units/0.4.0@mpusz/testing + mp-units/0.5.0@mpusz/testing ``` - `conanfile.py` ```python - requires = "mp-units/0.4.0@mpusz/testing" + requires = "mp-units/0.5.0@mpusz/testing" ``` - link your `cmake` target with units @@ -93,5 +93,5 @@ conan create . / -s cppstd=20 -b=outdated --all mp-units/0.4.0@/ +conan upload -r --all mp-units/0.5.0@/ ``` diff --git a/doc/design.png b/doc/design.png new file mode 100644 index 00000000..256bbd4a Binary files /dev/null and b/doc/design.png differ diff --git a/doc/downcast_1.png b/doc/downcast_1.png index 22768197..d8eaa468 100644 Binary files a/doc/downcast_1.png and b/doc/downcast_1.png differ diff --git a/doc/downcast_2.png b/doc/downcast_2.png index 7dbfc6c2..8a37faab 100644 Binary files a/doc/downcast_2.png and b/doc/downcast_2.png differ diff --git a/doc/nomnoml.md b/doc/nomnoml.md new file mode 100644 index 00000000..878d4ac9 --- /dev/null +++ b/doc/nomnoml.md @@ -0,0 +1,46 @@ +# nomnoml + +Graphs in the documentation are created with . + +## Concepts + +```text +[Dimension| +[base_dimension]<-[exp] +[derived_dimension]<-[exp] +[exp]<-[derived_dimension] +] + +[Quantity| +[quantity] +] + +[Unit]<-[Dimension] +[Dimension]<-[Quantity] +[Unit]<-[Quantity] +``` + +## Units + +```text +#direction: right + +[scaled_unit]<:-[unit] +[scaled_unit]<:-[named_unit] +[scaled_unit]<:-[named_scaled_unit] +[scaled_unit]<:-[prefixed_unit] +[scaled_unit]<:-[deduced_unit] +``` + +## Downcasting 1 + +```text +[detail::derived_dimension_base>]<:-[dim_area] +``` + +## Downcasting 2 + +```text +[downcast_base>>]<:-[detail::derived_dimension_base>] +[detail::derived_dimension_base>]<:-[downcast_child>>] +[downcast_child>>]<:-[dim_area]``` \ No newline at end of file diff --git a/doc/units.png b/doc/units.png new file mode 100644 index 00000000..311abd6f Binary files /dev/null and b/doc/units.png differ diff --git a/doc/units_uml.png b/doc/units_uml.png deleted file mode 100644 index f0f1e262..00000000 Binary files a/doc/units_uml.png and /dev/null differ diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index fb500daf..d9f229c5 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -20,15 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -# example app -add_executable(example example.cpp) -target_link_libraries(example - PRIVATE - mp::units -) +function(add_example target) + add_executable(${target} ${target}.cpp) + target_link_libraries(${target} PRIVATE mp::units) +endfunction() -add_executable(measurement measurement.cpp) -target_link_libraries(measurement - PRIVATE - mp::units -) +add_example(avg_velocity) +add_example(measurement) +add_example(unknown_dimension) diff --git a/example/avg_velocity.cpp b/example/avg_velocity.cpp new file mode 100644 index 00000000..49d65993 --- /dev/null +++ b/example/avg_velocity.cpp @@ -0,0 +1,186 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#include +#include +#include + +namespace { + +constexpr units::si::velocity +fixed_int_si_avg_speed(units::si::length d, + units::si::time t) +{ + return d / t; +} + +constexpr units::si::velocity +fixed_double_si_avg_speed(units::si::length d, + units::si::time t) +{ + return d / t; +} + +template +constexpr units::Velocity AUTO si_avg_speed(units::si::length d, + units::si::time t) +{ + return d / t; +} + +constexpr units::Velocity AUTO avg_speed(units::Length AUTO d, units::Time AUTO t) +{ + return d / t; +} + +template +void print_result(D distance, T duration, V velocity) +{ + const auto result_in_kmph = units::quantity_cast>(velocity); + std::cout << "Average speed of a car that makes " << distance << " in " + << duration << " is " << result_in_kmph << ".\n"; +} + +void example() +{ + using namespace units; + + // SI (int) + { + using namespace units::si::literals; + constexpr Length AUTO distance = 220km; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "SI units with 'int' as representation\n"; + + print_result(distance, duration, fixed_int_si_avg_speed(distance, duration)); + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); + } + + // SI (double) + { + using namespace units::si::literals; + constexpr Length AUTO distance = 220.km; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "\nSI units with 'double' as representation\n"; + + // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), quantity_cast(duration))); + + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); + } + + // Customary Units (int) + { + using namespace units::si::literals; + constexpr Length AUTO distance = 140mi; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "\nUS Customary Units with 'int' as representation\n"; + + // it is not possible to make a lossless conversion of miles to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); + } + + // Customary Units (double) + { + using namespace units::si::literals; + constexpr Length AUTO distance = 140.mi; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "\nUS Customary Units with 'double' as representation\n"; + + // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed + // also it is not possible to make a lossless conversion of miles to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast>(distance), quantity_cast(duration))); + + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); + } + + // CGS (int) + { + using namespace units::cgs::literals; + constexpr Length AUTO distance = 22'000'000cm; // constructed from a UDL + constexpr cgs::time duration(2); // constructed from a value + + std::cout << "\nCGS units with 'int' as representation\n"; + + // it is not possible to make a lossless conversion of centimeters to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + + // not possible to convert both a dimension and a unit with implicit cast + print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); + + print_result(distance, duration, avg_speed(distance, duration)); + } + + // CGS (double) + { + using namespace units::cgs::literals; + constexpr Length AUTO distance = 22'000'000.cm; // constructed from a UDL + constexpr cgs::time duration(2); // constructed from a value + + std::cout << "\nCGS units with 'double' as representation\n"; + + // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed + // it is not possible to make a lossless conversion of centimeters to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast>(distance), quantity_cast(duration))); + + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + + // not possible to convert both a dimension and a unit with implicit cast + print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); + + print_result(distance, duration, avg_speed(distance, duration)); + } + +} + +} // namespace + +int main() +{ + try { + example(); + } + catch (const std::exception& ex) { + std::cerr << "Unhandled std exception caught: " << ex.what() << '\n'; + } + catch (...) { + std::cerr << "Unhandled unknown exception caught\n"; + } +} diff --git a/example/measurement.cpp b/example/measurement.cpp index 09c6b6e1..f517cbaa 100644 --- a/example/measurement.cpp +++ b/example/measurement.cpp @@ -20,122 +20,142 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include +#include #include namespace { - template typename Trait> - concept Satisfies = Trait::value; - - // root sum of squares - template - T rss(const T& v1, const T& v2) +// root sum of squares +template +T rss(const T& v1, const T& v2) +{ + return std::sqrt(std::pow(v1, 2) + std::pow(v2, 2)); +} + +template +class measurement { +public: + using value_type = T; + + measurement() = default; + + constexpr /* explicit */ measurement(const value_type& val, const value_type& err = {}) : + // cannot be explicit as `magma` concept requires implicit conversions :-( + value_(val), + uncertainty_(std::abs(err)) { - return std::sqrt(std::pow(v1, 2) + std::pow(v2, 2)); } - template - class measurement { - public: - using value_type = T; + constexpr const value_type& value() const { return value_; } + constexpr const value_type& uncertainty() const { return uncertainty_; } - measurement() = default; + constexpr value_type relative_uncertainty() const { return uncertainty() / value(); } + constexpr value_type lower_bound() const { return value() - uncertainty(); } + constexpr value_type upper_bound() const { return value() + uncertainty(); } - constexpr /* explicit */ measurement(const value_type& val, const value_type& err = {}): // cannot be explicit as `magma` concept requires implicit conversions :-( - value_(val), uncertainty_(std::abs(err)) - { - } + [[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); } - constexpr const value_type& value() const { return value_; } - constexpr const value_type& uncertainty() const { return uncertainty_; } + [[nodiscard]] friend constexpr measurement operator+(const measurement& lhs, const measurement& rhs) + { + return measurement(lhs.value() + rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); + } - constexpr value_type relative_uncertainty() const { return uncertainty() / value(); } - constexpr value_type lower_bound() const { return value() - uncertainty(); } - constexpr value_type upper_bound() const { return value() + uncertainty(); } + [[nodiscard]] friend constexpr measurement operator-(const measurement& lhs, const measurement& rhs) + { + return measurement(lhs.value() - rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); + } - [[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); } + [[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const measurement& rhs) + { + const auto val = lhs.value() * rhs.value(); + return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); + } - [[nodiscard]] friend constexpr measurement operator+(const measurement& lhs, const measurement& rhs) - { - return measurement(lhs.value() + rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); - } + [[nodiscard]] friend constexpr measurement operator/(const measurement& lhs, const measurement& rhs) + { + const auto val = lhs.value() / rhs.value(); + return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); + } - [[nodiscard]] friend constexpr measurement operator-(const measurement& lhs, const measurement& rhs) - { - return measurement(lhs.value() - rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); - } +#if __GNUC__ >= 10 - [[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const measurement& rhs) - { - const auto val = lhs.value() * rhs.value(); - return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); - } + [[nodiscard]] friend constexpr auto operator<=>(const measurement& lhs, const measurement& rhs) = default; + [[nodiscard]] friend constexpr bool operator==(const measurement& lhs, const measurement& rhs) = default; - [[nodiscard]] friend constexpr measurement operator/(const measurement& lhs, const measurement& rhs) - { - const auto val = lhs.value() / rhs.value(); - return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); - } +#else - [[nodiscard]] friend constexpr bool operator==(const measurement& lhs, const measurement& rhs) - { - return lhs.value() == rhs.value() && lhs.uncertainty() == rhs.uncertainty(); - } + [[nodiscard]] friend constexpr bool operator==(const measurement& lhs, const measurement& rhs) + { + return lhs.value() == rhs.value() && lhs.uncertainty() == rhs.uncertainty(); + } - [[nodiscard]] friend constexpr bool operator!=(const measurement& lhs, const measurement& rhs) - { - return !(lhs == rhs); - } + [[nodiscard]] friend constexpr bool operator!=(const measurement& lhs, const measurement& rhs) + { + return !(lhs == rhs); + } - [[nodiscard]] friend constexpr bool operator<(const measurement& lhs, const measurement& rhs) - { - return lhs.value() == rhs.value() ? lhs.uncertainty() < rhs.uncertainty() : lhs.value() < rhs.value(); - } + [[nodiscard]] friend constexpr bool operator<(const measurement& lhs, const measurement& rhs) + { + return lhs.value() == rhs.value() ? lhs.uncertainty() < rhs.uncertainty() : lhs.value() < rhs.value(); + } - [[nodiscard]] friend constexpr bool operator>(const measurement& lhs, const measurement& rhs) - { - return rhs < lhs; - } + [[nodiscard]] friend constexpr bool operator>(const measurement& lhs, const measurement& rhs) { return rhs < lhs; } - [[nodiscard]] friend constexpr bool operator<=(const measurement& lhs, const measurement& rhs) - { - return !(rhs < lhs); - } + [[nodiscard]] friend constexpr bool operator<=(const measurement& lhs, const measurement& rhs) + { + return !(rhs < lhs); + } - [[nodiscard]] friend constexpr bool operator>=(const measurement& lhs, const measurement& rhs) - { - return !(lhs < rhs); - } + [[nodiscard]] friend constexpr bool operator>=(const measurement& lhs, const measurement& rhs) + { + return !(lhs < rhs); + } - friend std::ostream& operator<<(std::ostream& os, const measurement& v) - { - return os << v.value() << " ± " << v.uncertainty(); - } +#endif - private: - value_type value_{}; - value_type uncertainty_{}; - }; + friend std::ostream& operator<<(std::ostream& os, const measurement& v) + { + return os << v.value() << " ± " << v.uncertainty(); + } - template - using m_quantity = units::quantity>; +private: + value_type value_{}; + value_type uncertainty_{}; +}; + +static_assert(units::Scalar>); } // namespace template inline constexpr bool units::treat_as_floating_point> = std::is_floating_point_v; +namespace { + +void example() +{ + using namespace units; + + const auto a = si::acceleration>(measurement(9.8, 0.1)); + const auto t = si::time>(measurement(1.2, 0.1)); + + const Velocity AUTO v1 = a * t; + std::cout << a << " * " << t << " = " << v1 << " = " << quantity_cast(v1) << '\n'; + + si::length> length(measurement(123., 1.)); + std::cout << "10 * " << length << " = " << 10 * length << '\n'; +} + +} // namespace int main() { - const auto a = m_quantity(measurement(9.8, 0.1)); - const auto t = m_quantity(measurement(1.2, 0.1)); - - units::Velocity AUTO v1 = a * t; - m_quantity v2(v1); - std::cout << a << " * " << t << " = " << v1 << " = " << v2 << '\n'; - - m_quantity length(measurement(123., 1.)); - std::cout << "10 * " << length << " = " << 10 * length << '\n'; + try { + example(); + } catch (const std::exception& ex) { + std::cerr << "Unhandled std exception caught: " << ex.what() << '\n'; + } catch (...) { + std::cerr << "Unhandled unknown exception caught\n"; + } } diff --git a/example/example.cpp b/example/unknown_dimension.cpp similarity index 65% rename from example/example.cpp rename to example/unknown_dimension.cpp index c1716594..9cd31340 100644 --- a/example/example.cpp +++ b/example/unknown_dimension.cpp @@ -20,43 +20,43 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include namespace { -using namespace units::literals; - template constexpr units::Velocity AUTO avg_speed(D d, T t) { return d / t; } -template -void example_1(V v, T t) +void example() { - const units::Length AUTO distance = v * t; - std::cout << "A car driving " << v << " in a time of " << t << " will pass " - << units::quantity_cast>(distance) << ".\n"; + using namespace units::si::literals; + + units::Length AUTO d1 = 123m; + units::Time AUTO t1 = 10s; + units::Velocity AUTO v1 = avg_speed(d1, t1); + + auto temp1 = v1 * 50m; // produces intermediate unknown dimension with 'unknown_unit' as its 'coherent_unit' + units::Velocity AUTO v2 = temp1 / 100m; // back to known dimensions again + units::Length AUTO d2 = v2 * 60s; + + std::cout << "d1 = " << d1 << '\n'; + std::cout << "t1 = " << t1 << '\n'; + std::cout << "v1 = " << v1 << '\n'; + std::cout << "temp1 = " << temp1 << '\n'; + std::cout << "v2 = " << v2 << '\n'; + std::cout << "d2 = " << d2 << '\n'; } -void example_2(double distance_v, double duration_v) -{ - units::quantity distance(distance_v); - units::quantity duration(duration_v); - const auto kmph = quantity_cast(avg_speed(distance, duration)); - std::cout << "Average speed of a car that makes " << distance << " in " - << duration << " is " << kmph << ".\n"; -} - -} +} // namespace int main() { try { - example_1(60kmph, 10.0min); - example_2(220, 2); + example(); } catch (const std::exception& ex) { std::cerr << "Unhandled std exception caught: " << ex.what() << '\n'; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcf70e5e..2352b3c3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ cmake_minimum_required(VERSION 3.8) #cmake_policy(SET CMP0076 NEW) project(units - VERSION 0.4.0 + VERSION 0.5.0 LANGUAGES CXX ) @@ -53,7 +53,6 @@ add_library(units INTERFACE) target_compile_features(units INTERFACE cxx_std_20) target_link_libraries(units INTERFACE - CONAN_PKG::range-v3 CONAN_PKG::fmt ) target_include_directories(units @@ -67,10 +66,13 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) -Wno-literal-suffix -Wno-non-template-friend -Wno-stringop-overflow -# TODO gcc:92101 - -Wno-pedantic ) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) + target_link_libraries(units + INTERFACE + CONAN_PKG::range-v3 + ) + target_compile_options(units INTERFACE -fconcepts diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h new file mode 100644 index 00000000..5b8d020f --- /dev/null +++ b/src/include/units/base_dimension.h @@ -0,0 +1,66 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units { + +/** + * @brief A dimension of a base quantity + * + * Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity + * in the subset can be expressed in terms of the other quantities within that subset. They are referred to as + * being mutually independent since a base quantity cannot be expressed as a product of powers of the other base + * quantities. + * + * Base unit is a measurement unit that is adopted by convention for a base quantity in a specific system of units. + * + * Pair of Symbol and Unit template parameters form an unique identifier of the base dimension. The same identifiers can + * be multiplied and divided which will result with an adjustment of its factor in an Exponent of a DerivedDimension + * (in case of zero the dimension will be simplified and removed from further analysis of current expresion). In case + * the Symbol is the same but the Unit differs (i.e. mixing SI and CGS length), there is no automatic simplification but + * is possible to force it with a quantity_cast. + * + * @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support + * @tparam U a base unit to be used for this base dimension + */ +template + requires U::is_named +struct base_dimension { + using base_type_workaround = base_dimension; // TODO Replace with is_derived_from_instantiation when fixed + static constexpr auto symbol = Symbol; + using base_unit = U; +}; + +// base_dimension_less +// TODO Remove the below when https://bugs.llvm.org/show_bug.cgi?id=32208 is fixed +// clang-format off +template +struct base_dimension_less : std::bool_constant< + D1::symbol < D2::symbol || (D1::symbol == D2::symbol && D1::base_unit::symbol < D1::base_unit::symbol)> {}; +// clang-format on + +} // namespace units diff --git a/src/include/units/bits/concepts.h b/src/include/units/bits/base_units_ratio.h similarity index 50% rename from src/include/units/bits/concepts.h rename to src/include/units/bits/base_units_ratio.h index 44b07f31..06ef2a5c 100644 --- a/src/include/units/bits/concepts.h +++ b/src/include/units/bits/base_units_ratio.h @@ -22,32 +22,39 @@ #pragma once -#include -#include -#include +#include +#include #include -namespace units { +namespace units::detail { - namespace detail { +template + requires (E::den == 1 || E::den == 2) // TODO provide support for any den +struct exp_ratio { + using base_ratio = E::dimension::base_unit::ratio; + using positive_ratio = conditional, base_ratio>; + static constexpr std::int64_t N = E::num * E::den < 0 ? -E::num : E::num; + using pow = ratio_pow; + using type = conditional, pow>; +}; - template - concept basic_arithmetic = // exposition only - std::magma && - std::magma && - std::magma && - std::magma; +template +struct base_units_ratio_impl; - template - concept safe_convertible = // exposition only - std::convertible_to && - (treat_as_floating_point || (!treat_as_floating_point)); +template +struct base_units_ratio_impl> { + using type = ratio_multiply::type, typename base_units_ratio_impl>::type>; +}; - template - concept safe_divisible = // exposition only - treat_as_floating_point || - ratio_divide::den == 1; +template +struct base_units_ratio_impl> { + using type = exp_ratio::type; +}; - } +/** + * @brief Calculates the common ratio of all the references of base units in the derived dimension + */ +template +using base_units_ratio = base_units_ratio_impl::type; -} // namespace units +} // namespace units::detail diff --git a/src/include/units/bits/common_quantity.h b/src/include/units/bits/common_quantity.h new file mode 100644 index 00000000..0a1b4da0 --- /dev/null +++ b/src/include/units/bits/common_quantity.h @@ -0,0 +1,66 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include + +namespace units { + +template U, Scalar Rep> +class quantity; + +namespace detail { + +template +struct common_quantity_impl; + +template +struct common_quantity_impl, quantity, Rep> { + using type = quantity; +}; + +template +struct common_quantity_impl, quantity, Rep> { + using type = quantity>, Rep>; +}; + +template + requires same_unit_reference, dimension_unit>::value +struct common_quantity_impl, quantity, Rep> { + using type = quantity>, Rep>; +}; + +template +struct common_quantity_impl, quantity, Rep> { + using ratio1 = ratio_multiply; + using ratio2 = ratio_multiply; + using type = quantity>, Rep>; +}; + +} // namespace detail + +template> + requires equivalent_dim +using common_quantity = detail::common_quantity_impl::type; + +} // namespace units diff --git a/src/include/units/bits/customization_points.h b/src/include/units/bits/customization_points.h deleted file mode 100644 index acc33c3d..00000000 --- a/src/include/units/bits/customization_points.h +++ /dev/null @@ -1,115 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include - -namespace units { - - // treat_as_floating_point - - template // TODO Conceptify that - inline constexpr bool treat_as_floating_point = std::is_floating_point_v; - - // isnan - namespace isnan_impl { - - // non-ADL lookup block - void isnan(); // undefined - - template - inline constexpr bool has_customization = false; - - template - requires requires(const T& t) { - { isnan(t) } -> bool; - } - inline constexpr bool has_customization = true; - - struct fn { - template - constexpr bool operator()(const T&) const - { - return false; - } - - template - requires treat_as_floating_point - constexpr bool operator()(const T& value) const - { - return std::isnan(value); - } - - template - requires treat_as_floating_point && has_customization - constexpr bool operator()(const T& value) const - { - return isnan(value); // uses ADL - } - }; - } - - inline constexpr isnan_impl::fn isnan{}; - - // isfinite - namespace isfinite_impl { - - // non-ADL lookup block - void isfinite(); // undefined - - template - inline constexpr bool has_customization = false; - - template - requires requires(const T& t) { - { isfinite(t) } -> bool; - } - inline constexpr bool has_customization = true; - - struct fn { - template - constexpr bool operator()(const T&) const - { - return true; - } - - template - requires treat_as_floating_point - constexpr bool operator()(const T& value) const - { - return std::isfinite(value); - } - - template - requires treat_as_floating_point && has_customization - constexpr bool operator()(const T& value) const - { - return isfinite(value); // uses ADL - } - }; - } - - inline constexpr isfinite_impl::fn isfinite{}; - -} diff --git a/src/include/units/bits/deduced_symbol_text.h b/src/include/units/bits/deduced_symbol_text.h new file mode 100644 index 00000000..05015919 --- /dev/null +++ b/src/include/units/bits/deduced_symbol_text.h @@ -0,0 +1,82 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::detail { + +template +constexpr auto operator_text() +{ + if constexpr(Idx == 0) { + if constexpr(Divide) { + return basic_fixed_string("1/"); + } + else { + return basic_fixed_string(""); + } + } + else { + if constexpr(Divide) { + return basic_fixed_string("/"); + } + else { + return basic_fixed_string("⋅"); + } + } +} + +template +constexpr auto exp_text() +{ + // get calculation operator + symbol + const auto txt = operator_text() + Symbol; + if constexpr(E::den != 1) { + // add root part + return txt + basic_fixed_string("^(") + regular() + basic_fixed_string("/") + regular() + basic_fixed_string(")"); + } + else if constexpr(abs(E::num) != 1) { + // add exponent part + return txt + superscript(); + } + else { + return txt; + } +} + +template +constexpr auto deduced_symbol_text(exp_list, std::index_sequence) +{ + return (exp_text() + ...); +} + +template +constexpr auto deduced_symbol_text() +{ + return deduced_symbol_text(typename Dim::recipe(), std::index_sequence_for()); +} + +} // namespace units::detail diff --git a/src/include/units/bits/deduced_unit.h b/src/include/units/bits/deduced_unit.h new file mode 100644 index 00000000..82a28fc6 --- /dev/null +++ b/src/include/units/bits/deduced_unit.h @@ -0,0 +1,71 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include + +namespace units::detail { + +// same_scaled_units +template +inline constexpr bool same_scaled_units = false; + +template +inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); + +// deduced_unit +template +struct ratio_op; + +template +struct ratio_op { + using ratio = Result; +}; + +template +struct ratio_op { + using calc_ratio = + conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply, ratio_divide>; + static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen); + using ratio = ratio_op::ratio; +}; + +template +struct derived_ratio; + +template +struct derived_ratio, Us...> { + using ratio = ::units::ratio<1>; +}; + +template +struct derived_ratio, U, URest...> { + using rest_ratio = derived_ratio, URest...>::ratio; + using ratio = ratio_op::ratio; +}; + +template +using deduced_unit = + scaled_unit::ratio, typename D::coherent_unit::reference>; + +} // namespace units::detail diff --git a/src/include/units/bits/derived_dimension_base.h b/src/include/units/bits/derived_dimension_base.h new file mode 100644 index 00000000..c58ec217 --- /dev/null +++ b/src/include/units/bits/derived_dimension_base.h @@ -0,0 +1,61 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::detail { + +/** + * @brief A dimension of a derived quantity + * + * Expression of the dependence of a quantity on the base quantities (and their base dimensions) of a system of + * quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factors. + * A power of a factor is the factor raised to an exponent. + * + * A derived dimension can be formed from multiple exponents (i.e. velocity is represented as "exp, exp"). + * It is also possible to form a derived dimension with only one exponent (i.e. frequency is represented as just + * "exp"). + * + * @note This class template is used by the library engine and should not be directly instantiated by the user. + * + * @tparam E a first exponent of a derived dimension + * @tparam ERest zero or more following exponents of a derived dimension + */ +template + requires (BaseDimension && ... && BaseDimension) +struct derived_dimension_base : downcast_base> { + using exponents = exp_list; +}; + +template +struct to_derived_dimension_base; + +template +struct to_derived_dimension_base> { + using type = derived_dimension_base; +}; + +} // namespace units::detail diff --git a/src/include/units/bits/dim_consolidate.h b/src/include/units/bits/dim_consolidate.h new file mode 100644 index 00000000..5a433706 --- /dev/null +++ b/src/include/units/bits/dim_consolidate.h @@ -0,0 +1,67 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include // TODO remove this dependency with #11 + +namespace units::detail { + +/** + * @brief Consolidates contiguous ranges of exponents of the same dimension + * + * If there is more than one exponent with the same dimension they are aggregated into one exponent by adding + * their exponents. If this accumulation will result with 0, such a dimension is removed from the list. + * + * @tparam D derived dimension to consolidate + */ +template +struct dim_consolidate; + +template<> +struct dim_consolidate> { + using type = exp_list<>; +}; + +template +struct dim_consolidate> { + using type = exp_list; +}; + +template +struct dim_consolidate> { + using type = type_list_push_front>::type, E1>; +}; + +template +struct dim_consolidate, exp, ERest...>> { + // TODO: provide custom implementation for ratio_add + using r1 = std::ratio; + using r2 = std::ratio; + using r = std::ratio_add; + using type = conditional>::type, + typename dim_consolidate, ERest...>>::type>; +}; + +} // namespace units::detail diff --git a/src/include/units/dimensions/mass.h b/src/include/units/bits/dim_unpack.h similarity index 51% rename from src/include/units/dimensions/mass.h rename to src/include/units/bits/dim_unpack.h index d5da2587..710ee17b 100644 --- a/src/include/units/dimensions/mass.h +++ b/src/include/units/bits/dim_unpack.h @@ -22,29 +22,38 @@ #pragma once -#include -#include +#include +#include +#include -namespace units { +namespace units::detail { - struct mass : derived_dimension> {}; +/** + * @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions + * + * @tparam Es Exponents of potentially derived dimensions + */ +template +struct dim_unpack; - template - concept Mass = QuantityOf; +template<> +struct dim_unpack<> { + using type = exp_list<>; +}; - struct gram : named_scaled_derived_unit, si_prefix> {}; - struct kilogram : prefixed_derived_unit {}; +template +struct dim_unpack, ERest...> { + using type = type_list_push_front::type, exp>; +}; - inline namespace literals { +template +struct dim_unpack, ERest...> { + using type = dim_unpack, Num, Den>, ERest...>::type; +}; - // g - constexpr auto operator""g(unsigned long long l) { return quantity(l); } - constexpr auto operator""g(long double l) { return quantity(l); } +template +struct dim_unpack, Num, Den>, ERest...> { + using type = type_list_push_front::type, exp_multiply...>; +}; - // kg - constexpr auto operator""kg(unsigned long long l) { return quantity(l); } - constexpr auto operator""kg(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units +} // namespace units::detail diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h new file mode 100644 index 00000000..0405efae --- /dev/null +++ b/src/include/units/bits/dimension_op.h @@ -0,0 +1,266 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include + +namespace units { + +// equivalent_dim +namespace detail { + +template +struct equivalent_dim_impl : std::false_type {}; + +template +struct equivalent_base_dim : std::conjunction, + same_unit_reference> {}; + +template +struct equivalent_dim_impl : std::disjunction, equivalent_base_dim> {}; + +template +struct equivalent_exp : std::false_type {}; + +template +struct equivalent_exp, exp> : equivalent_dim_impl {}; + +template +struct equivalent_derived_dim : std::false_type {}; + +template + requires (sizeof...(Es1) == sizeof...(Es2)) +struct equivalent_derived_dim, derived_dimension_base> : std::conjunction...> {}; + +template +struct equivalent_dim_impl : std::disjunction, equivalent_derived_dim, downcast_base_t>> {}; + +} // namespace detail + +template +inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::value; + +/** + * @brief Unknown dimension + * + * Sometimes a temporary partial result of a complex calculation may not result in a predefined + * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_unit` + * and ratio<1>. + * + * @tparam E the list of exponents of ingredient dimensions + * @tparam ERest the list of exponents of ingredient dimensions + */ +template +struct unknown_dimension : derived_dimension, scaled_unit, unknown_unit>, E, ERest...> { + using coherent_unit = scaled_unit, unknown_unit>; +}; + +namespace detail { + +template +struct check_unknown { + using type = D; +}; + +// downcast did not find a user predefined type +template +struct check_unknown> { + using type = unknown_dimension; +}; + +template +struct downcast_dimension_impl; + +template +struct downcast_dimension_impl { + using type = D; +}; + +template +struct downcast_dimension_impl { + using type = check_unknown>::type; +}; + +} // namespace detail + +template +using downcast_dimension = detail::downcast_dimension_impl::type; + +// dim_invert +namespace detail { + +template +struct dim_invert_impl; + +template +struct dim_invert_impl { + using type = downcast_dimension>>; +}; + +template +struct dim_invert_impl>> { + using type = D; +}; + +template +struct dim_invert_impl> { + using type = downcast_dimension...>>; +}; + +template +struct dim_invert_impl : dim_invert_impl> { +}; + +} // namespace detail + +template +using dim_invert = detail::dim_invert_impl::type; + +// dimension_multiply +namespace detail { + +template +struct to_dimension; + +template +struct to_dimension> { + using type = derived_dimension_base; +}; + +template +struct to_dimension>> { + using type = D; +}; + +/** + * @brief Merges 2 sorted derived dimensions into one units::derived_dimension_base + * + * A result of a dimensional calculation may result with many exponents of the same base dimension orginated + * from different parts of the equation. As the exponents lists of both operands it is enough to merge them + * into one list and consolidate duplicates. Also it is possible that final exponents list will contain only + * one element being a base dimension with exponent 1. In such a case the final dimension should be the base + * dimension itself. + */ +template +using merge_dimension = to_dimension>::type>::type; + +template +struct dimension_multiply_impl; + +template +struct dimension_multiply_impl { + using type = downcast_dimension>, derived_dimension_base>>>; +}; + +template +struct dimension_multiply_impl { + using type = downcast_dimension>, typename D2::downcast_base_type>>; +}; + +template +struct dimension_multiply_impl { + using type = dimension_multiply_impl::type; +}; + +template +struct dimension_multiply_impl { + using type = downcast_dimension>; +}; + +} // namespace detail + +template +using dimension_multiply = detail::dimension_multiply_impl::type; + +template +using dimension_divide = detail::dimension_multiply_impl>::type; + +// dimension_sqrt +namespace detail { + +template +struct dimension_sqrt_impl; + +template +struct dimension_sqrt_impl { + using type = downcast_dimension>>; +}; + +template +struct dimension_sqrt_impl>> { + using type = D; +}; + +template +struct dimension_sqrt_impl { + using type = dimension_sqrt_impl::type; +}; + +template +struct dimension_sqrt_impl> { + using type = downcast_dimension...>>; +}; + +} // namespace detail + +template +using dimension_sqrt = detail::dimension_sqrt_impl::type; + +// dimension_pow +namespace detail { + +template +struct dimension_pow_impl; + +template +struct dimension_pow_impl { + using type = downcast_dimension>>; +}; + +template +struct dimension_pow_impl { + using type = D; +}; + +template +struct dimension_pow_impl>, N> { + using type = D; +}; + +template +struct dimension_pow_impl { + using type = dimension_pow_impl, N>::type; +}; + +template +struct dimension_pow_impl, N> { + using type = downcast_dimension...>>; +}; + +} // namespace detail + +template +using dimension_pow = detail::dimension_pow_impl::type; + +} // namespace units diff --git a/src/include/units/bits/downcasting.h b/src/include/units/bits/external/downcasting.h similarity index 89% rename from src/include/units/bits/downcasting.h rename to src/include/units/bits/external/downcasting.h index 043b1522..535147e9 100644 --- a/src/include/units/bits/downcasting.h +++ b/src/include/units/bits/external/downcasting.h @@ -22,23 +22,23 @@ #pragma once -#include +#include #include namespace units { template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; friend auto downcast_guide(downcast_base); }; template concept Downcastable = requires { - typename T::base_type; + typename T::downcast_base_type; } && - std::derived_from>; + std::derived_from>; template struct downcast_child : T { @@ -67,6 +67,6 @@ namespace units { using downcast = decltype(detail::downcast_impl()); template - using downcast_base_t = T::base_type; + using downcast_base_t = T::downcast_base_type; } // namespace units diff --git a/src/include/units/bits/fixed_string.h b/src/include/units/bits/external/fixed_string.h similarity index 100% rename from src/include/units/bits/fixed_string.h rename to src/include/units/bits/external/fixed_string.h diff --git a/src/include/units/bits/hacks.h b/src/include/units/bits/external/hacks.h similarity index 92% rename from src/include/units/bits/hacks.h rename to src/include/units/bits/external/hacks.h index da702863..c1bb861a 100644 --- a/src/include/units/bits/hacks.h +++ b/src/include/units/bits/external/hacks.h @@ -22,7 +22,6 @@ #pragma once -#include #include #ifdef NDEBUG @@ -32,16 +31,24 @@ #define Expects(cond) assert(cond); #endif -#if __GNUC__ > 9 -#define AUTO auto -#define SAME_AS(T) std::same_as -#else +#if __GNUC__ < 10 + +#include #define AUTO #define SAME_AS(T) T + +#else + +#include +#define AUTO auto +#define SAME_AS(T) std::same_as + #endif namespace std { +#if __GNUC__ < 10 + // concepts using concepts::common_reference_with; using concepts::common_with; @@ -64,4 +71,11 @@ namespace std { template concept regular_invocable = invocable; -} +#else + +template +concept default_constructible = constructible_from; + +#endif + +} // namespace std diff --git a/src/include/units/bits/numeric_concepts.h b/src/include/units/bits/external/numeric_concepts.h similarity index 99% rename from src/include/units/bits/numeric_concepts.h rename to src/include/units/bits/external/numeric_concepts.h index 5ccbe997..abbef656 100644 --- a/src/include/units/bits/numeric_concepts.h +++ b/src/include/units/bits/external/numeric_concepts.h @@ -22,7 +22,7 @@ #pragma once -#include +#include // P1813 - A Concept Design for the Numeric Algorithms diff --git a/src/include/units/bits/external/text_tools.h b/src/include/units/bits/external/text_tools.h new file mode 100644 index 00000000..7e102fe7 --- /dev/null +++ b/src/include/units/bits/external/text_tools.h @@ -0,0 +1,64 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include + +namespace units::detail { + +template + requires (0 <= Value) && (Value < 10) +inline constexpr basic_fixed_string superscript_number = ""; + +template<> inline constexpr basic_fixed_string superscript_number<0> = "\u2070"; +template<> inline constexpr basic_fixed_string superscript_number<1> = "\u00b9"; +template<> inline constexpr basic_fixed_string superscript_number<2> = "\u00b2"; +template<> inline constexpr basic_fixed_string superscript_number<3> = "\u00b3"; +template<> inline constexpr basic_fixed_string superscript_number<4> = "\u2074"; +template<> inline constexpr basic_fixed_string superscript_number<5> = "\u2075"; +template<> inline constexpr basic_fixed_string superscript_number<6> = "\u2076"; +template<> inline constexpr basic_fixed_string superscript_number<7> = "\u2077"; +template<> inline constexpr basic_fixed_string superscript_number<8> = "\u2078"; +template<> inline constexpr basic_fixed_string superscript_number<9> = "\u2079"; + +template + requires (Value >= 0) +constexpr auto superscript() +{ + if constexpr(Value < 10) + return superscript_number; + else + return superscript() + superscript(); +} + +template + requires (Value >= 0) +constexpr auto regular() +{ + if constexpr(Value < 10) + return basic_fixed_string(static_cast('0' + Value)); + else + return regular() + regular(); +} + +} // namespace units::detail diff --git a/src/include/units/bits/type_list.h b/src/include/units/bits/external/type_list.h similarity index 99% rename from src/include/units/bits/type_list.h rename to src/include/units/bits/external/type_list.h index a4b51d6e..422bb00f 100644 --- a/src/include/units/bits/type_list.h +++ b/src/include/units/bits/external/type_list.h @@ -22,7 +22,7 @@ #pragma once -#include +#include namespace units { diff --git a/src/include/units/bits/external/type_traits.h b/src/include/units/bits/external/type_traits.h new file mode 100644 index 00000000..831fad2b --- /dev/null +++ b/src/include/units/bits/external/type_traits.h @@ -0,0 +1,78 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include + +namespace units { + +// conditional +namespace detail { + +template +struct conditional_impl { + template + using type = F; +}; + +template<> +struct conditional_impl { + template + using type = T; +}; + +} // namespace detail + +template +using conditional = detail::conditional_impl::template type; + +// is_instantiation +namespace detail { + +template typename Type> +inline constexpr bool is_instantiation_impl = false; + +template typename Type> +inline constexpr bool is_instantiation_impl, Type> = true; + +} // namespace detail + +template typename Type> +inline constexpr bool is_instantiation = detail::is_instantiation_impl; + +// is_derived_from_instantiation +namespace detail { + +template typename Type> +struct is_derived_from_instantiation_impl { + template + static constexpr std::true_type check_base(const Type&); + static constexpr std::true_type check_base(...); +}; + +} // namespace detail + +template typename Type> +inline constexpr bool is_derived_from_instantiation = decltype(detail::is_derived_from_instantiation_impl::check_base(std::declval()))::value; + +} // namespace units diff --git a/src/include/units/bits/unit_text.h b/src/include/units/bits/unit_text.h new file mode 100644 index 00000000..f1fc8152 --- /dev/null +++ b/src/include/units/bits/unit_text.h @@ -0,0 +1,128 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include + +namespace units::detail { + +template +constexpr auto ratio_text() +{ + if constexpr(Ratio::num != 1 || Ratio::den != 1) { + auto txt = basic_fixed_string("[") + regular(); + if constexpr(Ratio::den == 1) { + return txt + basic_fixed_string("]"); + } + else { + return txt + basic_fixed_string("/") + regular() + basic_fixed_string("]"); + } + } + else { + return basic_fixed_string(""); + } +} + +template +constexpr auto prefix_or_ratio_text() +{ + if constexpr(Ratio::num == 1 && Ratio::den == 1) { + // no ratio/prefix + return basic_fixed_string(""); + } + else { + if constexpr (!std::same_as) { + // try to form a prefix + using prefix = downcast>; + + if constexpr(!std::same_as>) { + // print as a prefixed unit + return prefix::symbol; + } + else { + // print as a ratio of the coherent unit + return ratio_text(); + } + } + else { + // print as a ratio of the coherent unit + return ratio_text(); + } + } +} + +template +constexpr auto derived_dimension_unit_text(exp_list, std::index_sequence) +{ + return (exp_text::symbol, Idxs>() + ...); +} + +template +constexpr auto derived_dimension_unit_text(exp_list list) +{ + return derived_dimension_unit_text(list, std::index_sequence_for()); +} + +template +constexpr bool all_named(exp_list) +{ + return (dimension_unit::is_named && ...); +} + +template +constexpr auto derived_dimension_unit_text() +{ + using recipe = typename Dim::recipe; + if constexpr(all_named(recipe())) + return derived_dimension_unit_text(recipe()); + else + return derived_dimension_unit_text(typename Dim::exponents()); +} + +// TODO Inline below concept when switched to gcc-10 +template +concept has_symbol = requires{ T::symbol; }; + +template +constexpr auto unit_text() +{ + if constexpr(has_symbol) { + // already has a symbol so print it + return U::symbol; + } + else { + // print as a prefix or ratio of a reference unit + auto prefix_txt = prefix_or_ratio_text(); + + if constexpr(has_symbol) { + // use predefined reference unit symbol + return prefix_txt + U::reference::symbol; + } + else { + // use derived dimension ingredients to create a unit symbol + return prefix_txt + derived_dimension_unit_text(); + } + } +} + +} // namespace units::detail diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h new file mode 100644 index 00000000..76f8c654 --- /dev/null +++ b/src/include/units/concepts.h @@ -0,0 +1,187 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace units { + +namespace detail { + +template +concept basic_arithmetic = // exposition only + std::magma && + std::magma && + std::magma && + std::magma; + +} // namespace detail + +// PrefixType +struct prefix_type; + +template +concept PrefixType = std::derived_from; + +// Prefix +// TODO gcc:92150 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92150 +// namespace detail { + +// template +// inline constexpr bool is_prefix = false; + +// template +// inline constexpr bool is_prefix> = true; + +// } // namespace detail + +template +// concept Prefix = detail::is_prefix; +concept Prefix = true; + + +namespace detail { + +template +inline constexpr bool is_ratio = false; + +} // namespace detail + +template +concept Ratio = detail::is_ratio; + + +// UnitRatio +template +concept UnitRatio = Ratio && (R::num * R::den > 0); + +// Unit +template +struct scaled_unit; + +template +concept Unit = is_derived_from_instantiation; + +// BaseDimension +template + requires U::is_named +struct base_dimension; + +namespace detail { + +#if __GNUC__ == 9 && __GNUC_MINOR__ < 2 + +template +inline constexpr bool is_base_dimension = true; + +#else + +template +inline constexpr bool is_base_dimension = false; + +template +inline constexpr bool is_base_dimension> = true; + +#endif + +} // namespace detail + +template +concept BaseDimension = detail::is_base_dimension; + +// Exponent +namespace detail { + +template +inline constexpr bool is_exp = false; + +} // namespace detail + +template +concept Exponent = detail::is_exp; + +// DerivedDimension +namespace detail { + +template + requires (BaseDimension && ... && BaseDimension) +struct derived_dimension_base; + +} // namespace detail + +template +concept DerivedDimension = is_instantiation, detail::derived_dimension_base>; + +// Dimension +template +concept Dimension = BaseDimension || DerivedDimension; + +// UnitOf +namespace detail { + +template +struct dimension_unit_impl; + +template +struct dimension_unit_impl { + using type = D::base_unit; +}; + +template +struct dimension_unit_impl { + using type = D::coherent_unit; +}; + +} // namespace detail + +template +using dimension_unit = detail::dimension_unit_impl::type; + +template +concept UnitOf = + Unit && + Dimension && + std::same_as::reference>; + +// Quantity +namespace detail { + +template +inline constexpr bool is_quantity = false; + +} // namespace detail + +template +concept Quantity = detail::is_quantity; + +// Scalar +template +concept Scalar = (!Quantity) && std::regular && std::totally_ordered && detail::basic_arithmetic; + +} // namespace units diff --git a/src/include/units/customization_points.h b/src/include/units/customization_points.h new file mode 100644 index 00000000..0e8b70bd --- /dev/null +++ b/src/include/units/customization_points.h @@ -0,0 +1,141 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include + +namespace units { + +/** + * @brief Specifies if a value of a type should be treated as a floating-point value + * + * This type trait should be specialized for a custom representation type to specify + * that values fo this type should be treated by the library as a floating-point ones + * which will enable implicit conversions between quantities. + * + * @tparam Rep a representation type for which a type trait is defined + */ +template +inline constexpr bool treat_as_floating_point = std::is_floating_point_v; + +/** + * @brief A type trait that defines zero, one, min, and max for a representation type + * + * The zero, one, min, and max member functions in units::quantity forward their work to + * these methods. This type can be specialized if the representation Rep requires a specific + * implementation to return these quantity objects. + * + * @tparam Rep a representation type for which a type trait is defined + */ +template +struct quantity_values { + static constexpr Rep zero() noexcept { return Rep(0); } + static constexpr Rep one() noexcept { return Rep(1); } + static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } + static constexpr Rep max() noexcept { return std::numeric_limits::max(); } +}; + + + +// // isnan +// namespace isnan_impl { + +// // non-ADL lookup block +// void isnan(); // undefined + +// template +// inline constexpr bool has_customization = false; + +// template +// requires requires(const T& t) { +// { isnan(t) } -> bool; +// } +// inline constexpr bool has_customization = true; + +// struct fn { +// template +// constexpr bool operator()(const T&) const +// { +// return false; +// } + +// template +// requires treat_as_floating_point +// constexpr bool operator()(const T& value) const +// { +// return std::isnan(value); +// } + +// template +// requires treat_as_floating_point && has_customization +// constexpr bool operator()(const T& value) const +// { +// return isnan(value); // uses ADL +// } +// }; +// } + +// inline constexpr isnan_impl::fn isnan{}; + +// // isfinite +// namespace isfinite_impl { + +// // non-ADL lookup block +// void isfinite(); // undefined + +// template +// inline constexpr bool has_customization = false; + +// template +// requires requires(const T& t) { +// { isfinite(t) } -> bool; +// } +// inline constexpr bool has_customization = true; + +// struct fn { +// template +// constexpr bool operator()(const T&) const +// { +// return true; +// } + +// template +// requires treat_as_floating_point +// constexpr bool operator()(const T& value) const +// { +// return std::isfinite(value); +// } + +// template +// requires treat_as_floating_point && has_customization +// constexpr bool operator()(const T& value) const +// { +// return isfinite(value); // uses ADL +// } +// }; +// } + +// inline constexpr isfinite_impl::fn isfinite{}; + +} // namespace units diff --git a/src/include/units/data/bitrate.h b/src/include/units/data/bitrate.h new file mode 100644 index 00000000..711e0fff --- /dev/null +++ b/src/include/units/data/bitrate.h @@ -0,0 +1,59 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::data { + +struct bit_per_second : unit {}; +struct dim_bitrate : derived_dimension, exp> {}; + +struct kibibit_per_second : deduced_unit {}; +struct mebibit_per_second : deduced_unit {}; +struct gibibit_per_second : deduced_unit {}; +struct tebibit_per_second : deduced_unit {}; +struct pebibit_per_second : deduced_unit {}; + +template +concept Bitrate = QuantityOf; + +template +using bitrate = quantity; + +inline namespace literals { + +// bits +constexpr auto operator""_bps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Kibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Mibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Gibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Tibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Pibps(unsigned long long l) { return bitrate(l); } + +} // namespace literals + +} // namespace units::data diff --git a/src/include/units/data/information.h b/src/include/units/data/information.h new file mode 100644 index 00000000..4d28f47b --- /dev/null +++ b/src/include/units/data/information.h @@ -0,0 +1,74 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::data { + +struct bit : named_unit {}; +struct kibibit : prefixed_unit {}; +struct mebibit : prefixed_unit {}; +struct gibibit : prefixed_unit {}; +struct tebibit : prefixed_unit {}; +struct pebibit : prefixed_unit {}; + +struct byte : named_scaled_unit, bit> {}; +struct kibibyte : prefixed_unit {}; +struct mebibyte : prefixed_unit {}; +struct gibibyte : prefixed_unit {}; +struct tebibyte : prefixed_unit {}; +struct pebibyte : prefixed_unit {}; + +struct dim_information : base_dimension<"information", bit> {}; + +template +concept Information = QuantityOf; + +template +using information = quantity; + +inline namespace literals { + +// bits +constexpr auto operator""b(unsigned long long l) { return information(l); } +constexpr auto operator""Kib(unsigned long long l) { return information(l); } +constexpr auto operator""Mib(unsigned long long l) { return information(l); } +constexpr auto operator""Gib(unsigned long long l) { return information(l); } +constexpr auto operator""Tib(unsigned long long l) { return information(l); } +constexpr auto operator""Pib(unsigned long long l) { return information(l); } + +// bytes +constexpr auto operator""B(unsigned long long l) { return information(l); } +constexpr auto operator""KiB(unsigned long long l) { return information(l); } +constexpr auto operator""MiB(unsigned long long l) { return information(l); } +constexpr auto operator""GiB(unsigned long long l) { return information(l); } +constexpr auto operator""TiB(unsigned long long l) { return information(l); } +constexpr auto operator""PiB(unsigned long long l) { return information(l); } + +} // namespace literals + +} // namespace units::data diff --git a/src/include/units/dimensions/acceleration.h b/src/include/units/data/prefixes.h similarity index 64% rename from src/include/units/dimensions/acceleration.h rename to src/include/units/data/prefixes.h index 891ad786..c03ecf83 100644 --- a/src/include/units/dimensions/acceleration.h +++ b/src/include/units/data/prefixes.h @@ -22,23 +22,18 @@ #pragma once -#include +#include +#include -namespace units { +namespace units::data { - struct acceleration : derived_dimension, exp> {}; +struct prefix : prefix_type {}; - template - concept Acceleration = QuantityOf; +struct kibi : units::prefix> {}; +struct mebi : units::prefix> {}; +struct gibi : units::prefix> {}; +struct tebi : units::prefix> {}; +struct pebi : units::prefix> {}; +struct exbi : units::prefix> {}; - struct metre_per_second_sq : coherent_derived_unit {}; - - inline namespace literals { - - // mps_sq - constexpr auto operator""mps_sq(unsigned long long l) { return quantity(l); } - constexpr auto operator""mps_sq(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units +} // namespace units::si diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h new file mode 100644 index 00000000..753c31a4 --- /dev/null +++ b/src/include/units/derived_dimension.h @@ -0,0 +1,78 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace units { + +namespace detail { + +/** + * @brief Converts user provided derived dimension specification into a valid units::derived_dimension_base definition + * + * User provided definition of a derived dimension may contain the same base dimension repeated more than once on the + * list possibly hidden in other derived units provided by the user. The process here should: + * 1. Extract derived dimensions into exponents of base dimensions. + * 2. Sort the exponents so the same dimensions are placed next to each other. + * 3. Consolidate contiguous range of exponents of the same base dimensions to a one (or possibly zero) exponent for + * this base dimension. + */ +template +using make_dimension = to_derived_dimension_base::type, exp_less>>::type>::type; + +} // namespace detail + +/** + * @brief The list of exponents of dimensions (both base and derived) provided by the user + * + * This is the user's interface to create derived dimensions. Exponents list can contain powers of factors of both + * base and derived dimensions. This is called a "recipe" of the dimension and among others is used to print + * unnamed coherent units of this dimension. + * + * Coherent unit is a unit that, for a given system of quantities and for a chosen set of base units, is a product + * of powers of base units with no other proportionality factor than one. + * + * The implementation is responsible for unpacking all of the dimensions into a list containing only base dimensions + * and their factors and putting them to derived_dimension_base class template. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam U a coherent unit of a derived dimension + * @tparam E the list of exponents of ingredient dimensions + * @tparam ERest the list of exponents of ingredient dimensions + */ +template +struct derived_dimension : downcast_child> { + using recipe = exp_list; + using coherent_unit = U; + using base_units_ratio = detail::base_units_ratio; +}; + +} // namespace units diff --git a/src/include/units/dimension.h b/src/include/units/dimension.h deleted file mode 100644 index 2e795d14..00000000 --- a/src/include/units/dimension.h +++ /dev/null @@ -1,292 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include -#include -#include - -namespace units { - - template - struct base_dimension { - static constexpr auto name = Name; - static constexpr auto symbol = Symbol; - }; - - template - concept BaseDimension = std::is_empty_v && - requires { - T::name; - T::symbol; - };// && // TODO file a bug for this gcc issue -// std::derived_from>; - - // base_dimension_less - - template - struct base_dimension_less : std::bool_constant { - }; - - // is_exp - namespace detail { - - template - inline constexpr bool is_exp = false; - - // partial specialization for an exp type provided below - - } // namespace detail - - template - concept Exponent = detail::is_exp; - - template - struct dimension; - - // is_dimension - namespace detail { - - template - inline constexpr bool is_dimension = false; - - template - inline constexpr bool is_dimension> = true; - - } // namespace detail - - template - concept Dimension = - std::is_empty_v && - detail::is_dimension>; - - // exp - template - requires BaseDimension || Dimension - struct exp { - using dimension = BaseOrDim; - static constexpr int num = Num; - static constexpr int den = Den; - }; - - // is_exp - namespace detail { - - template - inline constexpr bool is_exp> = true; - - } // namespace detail - - // exp_less - template - struct exp_less : base_dimension_less { - }; - - // exp_invert - namespace detail { - - template - struct exp_invert_impl; - - template - struct exp_invert_impl> { - using type = exp; - }; - - } - - template - using exp_invert = detail::exp_invert_impl::type; - - // exp_multiply - namespace detail { - - template - struct exp_multiply_impl { - using r1 = ratio; - using r2 = ratio; - using r = ratio_multiply; - using type = exp; - }; - - } - - template - using exp_multiply = detail::exp_multiply_impl::type; - - // dimension - template - struct dimension : downcast_base> {}; - - // same_dim - template - requires BaseDimension || Dimension - inline constexpr bool same_dim = std::is_same_v; - - template - inline constexpr bool same_dim = std::is_same_v>, typename D2::base_type>; - - // dim_invert - namespace detail { - - template - struct dim_invert_impl; - - template - struct dim_invert_impl> : std::type_identity...>>> {}; - - } - - template - using dim_invert = detail::dim_invert_impl>::type; - - // make_dimension - namespace detail { - - template - struct dim_consolidate; - - template<> - struct dim_consolidate> { - using type = dimension<>; - }; - - template - struct dim_consolidate> { - using type = dimension; - }; - - template - struct dim_consolidate> { - using type = type_list_push_front>::type, E1>; - }; - - template - struct dim_consolidate, exp, ERest...>> { - // TODO: provide custom implementation for ratio_add - using r1 = std::ratio; - using r2 = std::ratio; - using r = std::ratio_add; - using type = conditional>::type, - typename dim_consolidate, ERest...>>::type>; - }; - - template - struct extract; - - template<> - struct extract<> { - using type = dimension<>; - }; - - template - struct extract, ERest...> { - using type = type_list_push_front::type, exp>; - }; - - template - struct extract, Num, Den>, ERest...> { - using type = type_list_push_front::type, exp_multiply...>; - }; - - template - struct extract, ERest...> { - using type = extract, Num, Den>, ERest...>::type; - }; - - template - using make_dimension = dim_consolidate::type, exp_less>>::type; - - } // namespace detail - - // derived_dimension - template - struct derived_dimension : downcast_child> { - using recipe = dimension; - }; - - // merge_dimension - template - using merge_dimension = detail::dim_consolidate>::type; - - // dimension_multiply - namespace detail { - - template - struct dimension_multiply_impl; - - template - struct dimension_multiply_impl, dimension> : std::type_identity, dimension>>> {}; - - } - - template - using dimension_multiply = detail::dimension_multiply_impl::type; - - // dimension_divide - namespace detail { - - template - struct dimension_divide_impl; - - template - struct dimension_divide_impl, dimension> - : dimension_multiply_impl, dimension...>> { - }; - - } - - template - using dimension_divide = detail::dimension_divide_impl::type; - - // dimension_sqrt - namespace detail { - - template - struct dimension_sqrt_impl; - - template - struct dimension_sqrt_impl> : std::type_identity...>>> {}; - - } - - template - using dimension_sqrt = detail::dimension_sqrt_impl::type; - - // dimension_pow - namespace detail { - - template - struct dimension_pow_impl; - - template - struct dimension_pow_impl, N> : std::type_identity...>>> {}; - - } - - template - using dimension_pow = detail::dimension_pow_impl::type; - -} // namespace units diff --git a/src/include/units/dimensions/area.h b/src/include/units/dimensions/area.h deleted file mode 100644 index 1a8e8830..00000000 --- a/src/include/units/dimensions/area.h +++ /dev/null @@ -1,64 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include - -namespace units { - - struct area : derived_dimension> {}; - - template - concept Area = QuantityOf; - - struct square_metre : coherent_derived_unit {}; - struct square_millimetre : deduced_derived_unit {}; - struct square_centimetre : deduced_derived_unit {}; - struct square_kilometre : deduced_derived_unit {}; - struct square_foot : deduced_derived_unit {}; - - inline namespace literals { - - // sq_m - constexpr auto operator""sq_m(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_m(long double l) { return quantity(l); } - - // sq_mm - constexpr auto operator""sq_mm(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_mm(long double l) { return quantity(l); } - - // sq_cm - constexpr auto operator""sq_cm(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_cm(long double l) { return quantity(l); } - - // sq_km - constexpr auto operator""sq_km(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_km(long double l) { return quantity(l); } - - // sq_ft - constexpr auto operator""sq_ft(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_ft(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/capacitance.h b/src/include/units/dimensions/capacitance.h deleted file mode 100644 index 736fc9a6..00000000 --- a/src/include/units/dimensions/capacitance.h +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct capacitance : derived_dimension, exp> {}; - - template - concept Capacitance = QuantityOf; - - struct farad : named_coherent_derived_unit {}; - - inline namespace literals { - - // F - constexpr auto operator""F(unsigned long long l) { return quantity(l); } - constexpr auto operator""_F(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/electric_charge.h b/src/include/units/dimensions/electric_charge.h deleted file mode 100644 index 31cd410f..00000000 --- a/src/include/units/dimensions/electric_charge.h +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct electric_charge : derived_dimension, exp> {}; - - template - concept ElectricCharge = QuantityOf; - - struct coulomb : named_coherent_derived_unit {}; - - inline namespace literals { - - // C - constexpr auto operator""C(unsigned long long l) { return quantity(l); } - constexpr auto operator""C(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/energy.h b/src/include/units/dimensions/energy.h deleted file mode 100644 index 709a1b14..00000000 --- a/src/include/units/dimensions/energy.h +++ /dev/null @@ -1,67 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include -#include - -namespace units { - - struct energy : derived_dimension, exp> {}; - - template - concept Energy = QuantityOf; - - struct joule : named_coherent_derived_unit {}; - struct millijoule : prefixed_derived_unit {}; - struct kilojoule : prefixed_derived_unit {}; - struct megajoule : prefixed_derived_unit {}; - struct gigajoule : prefixed_derived_unit {}; - - inline namespace literals { - - // J - constexpr auto operator""_J(unsigned long long l) { return quantity(l); } - constexpr auto operator""_J(long double l) { return quantity(l); } - - // mJ - constexpr auto operator""mJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""mJ(long double l) { return quantity(l); } - - // kJ - constexpr auto operator""kJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""kJ(long double l) { return quantity(l); } - - // MJ - constexpr auto operator""MJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""MJ(long double l) { return quantity(l); } - - // GJ - constexpr auto operator""GJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""GJ(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/force.h b/src/include/units/dimensions/force.h deleted file mode 100644 index 5ec11f3d..00000000 --- a/src/include/units/dimensions/force.h +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct force : derived_dimension, exp> {}; - - template - concept Force = QuantityOf; - - struct newton : named_coherent_derived_unit {}; - - inline namespace literals { - - // N - constexpr auto operator""N(unsigned long long l) { return quantity(l); } - constexpr auto operator""N(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/frequency.h b/src/include/units/dimensions/frequency.h deleted file mode 100644 index 3594a22b..00000000 --- a/src/include/units/dimensions/frequency.h +++ /dev/null @@ -1,71 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct frequency : derived_dimension> {}; - - template - concept Frequency = QuantityOf; - - struct hertz : named_coherent_derived_unit {}; - struct millihertz : prefixed_derived_unit {}; - struct kilohertz : prefixed_derived_unit {}; - struct megahertz : prefixed_derived_unit {}; - struct gigahertz : prefixed_derived_unit {}; - struct terahertz : prefixed_derived_unit {}; - - inline namespace literals { - - // Hz - constexpr auto operator""Hz(unsigned long long l) { return quantity(l); } - constexpr auto operator""Hz(long double l) { return quantity(l); } - - // mHz - constexpr auto operator""mHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""mHz(long double l) { return quantity(l); } - - // kHz - constexpr auto operator""kHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""kHz(long double l) { return quantity(l); } - - // MHz - constexpr auto operator""MHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""MHz(long double l) { return quantity(l); } - - // GHz - constexpr auto operator""GHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""GHz(long double l) { return quantity(l); } - - // THz - constexpr auto operator""THz(unsigned long long l) { return quantity(l); } - constexpr auto operator""THz(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/length.h b/src/include/units/dimensions/length.h deleted file mode 100644 index d5ebb34a..00000000 --- a/src/include/units/dimensions/length.h +++ /dev/null @@ -1,88 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct length : derived_dimension> {}; - - template - concept Length = QuantityOf; - - // SI units - struct metre : named_coherent_derived_unit {}; - struct millimetre : prefixed_derived_unit {}; - struct centimetre : prefixed_derived_unit {}; - struct kilometre : prefixed_derived_unit {}; - - inline namespace literals { - - // m - constexpr auto operator""m(unsigned long long l) { return quantity(l); } - constexpr auto operator""m(long double l) { return quantity(l); } - - // mm - constexpr auto operator""mm(unsigned long long l) { return quantity(l); } - constexpr auto operator""mm(long double l) { return quantity(l); } - - // cm - constexpr auto operator""cm(unsigned long long l) { return quantity(l); } - constexpr auto operator""cm(long double l) { return quantity(l); } - - // km - constexpr auto operator""km(unsigned long long l) { return quantity(l); } - constexpr auto operator""km(long double l) { return quantity(l); } - - } // namespace literals - - // US customary units - struct yard : named_scaled_derived_unit> {}; - struct foot : named_scaled_derived_unit>> {}; - struct inch : named_scaled_derived_unit>> {}; - struct mile : named_scaled_derived_unit, yard::ratio>> {}; - - inline namespace literals { - - // yd - constexpr auto operator""yd(unsigned long long l) { return quantity(l); } - constexpr auto operator""yd(long double l) { return quantity(l); } - - // ft - constexpr auto operator""ft(unsigned long long l) { return quantity(l); } - constexpr auto operator""ft(long double l) { return quantity(l); } - - // in - constexpr auto operator""in(unsigned long long l) { return quantity(l); } - constexpr auto operator""in(long double l) { return quantity(l); } - - // mi - constexpr auto operator""mi(unsigned long long l) { return quantity(l); } - constexpr auto operator""mi(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/power.h b/src/include/units/dimensions/power.h deleted file mode 100644 index afffb2d2..00000000 --- a/src/include/units/dimensions/power.h +++ /dev/null @@ -1,66 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct power : derived_dimension, exp> {}; - - template - concept Power = QuantityOf; - - struct watt : named_coherent_derived_unit {}; - struct milliwatt : prefixed_derived_unit {}; - struct kilowatt : prefixed_derived_unit {}; - struct megawatt : prefixed_derived_unit {}; - struct gigawatt : prefixed_derived_unit {}; - - inline namespace literals { - - // W - constexpr auto operator""W(unsigned long long l) { return quantity(l); } - constexpr auto operator""_W(long double l) { return quantity(l); } - - // mW - constexpr auto operator""mW(unsigned long long l) { return quantity(l); } - constexpr auto operator""mW(long double l) { return quantity(l); } - - // kW - constexpr auto operator""kW(unsigned long long l) { return quantity(l); } - constexpr auto operator""kW(long double l) { return quantity(l); } - - // MW - constexpr auto operator""MW(unsigned long long l) { return quantity(l); } - constexpr auto operator""MW(long double l) { return quantity(l); } - - // GW - constexpr auto operator""GW(unsigned long long l) { return quantity(l); } - constexpr auto operator""GW(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/pressure.h b/src/include/units/dimensions/pressure.h deleted file mode 100644 index 4302ed26..00000000 --- a/src/include/units/dimensions/pressure.h +++ /dev/null @@ -1,46 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct pressure : derived_dimension, exp> {}; - - template - concept Pressure = QuantityOf; - - struct pascal : named_coherent_derived_unit {}; - - inline namespace literals { - - // Pa - constexpr auto operator""Pa(unsigned long long l) { return quantity(l); } - constexpr auto operator""Pa(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/si_prefixes.h b/src/include/units/dimensions/si_prefixes.h deleted file mode 100644 index a2d5bc5c..00000000 --- a/src/include/units/dimensions/si_prefixes.h +++ /dev/null @@ -1,52 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include - -namespace units { - - // prefix tags - struct si_prefix : prefix_type {}; - - // SI prefixes - - struct atto : prefix, "a"> {}; - struct femto : prefix, "f"> {}; - struct pico : prefix, "p"> {}; - struct nano : prefix, "n"> {}; - struct micro : prefix, "\u00b5"> {}; - struct milli : prefix, "m"> {}; - struct centi : prefix, "c"> {}; - struct deci : prefix, "d"> {}; - struct deca : prefix, "da"> {}; - struct hecto : prefix, "h"> {}; - struct kilo : prefix, "k"> {}; - struct mega : prefix, "M"> {}; - struct giga : prefix, "G"> {}; - struct tera : prefix, "T"> {}; - struct peta : prefix, "P"> {}; - struct exa : prefix, "E"> {}; - -} diff --git a/src/include/units/dimensions/surface_tension.h b/src/include/units/dimensions/surface_tension.h deleted file mode 100644 index 8a106301..00000000 --- a/src/include/units/dimensions/surface_tension.h +++ /dev/null @@ -1,45 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include - -namespace units { - - struct surface_tension : derived_dimension, exp> {}; - - template - concept SurfaceTension = QuantityOf; - - struct newton_per_metre : coherent_derived_unit {}; - - inline namespace literals { - - // Nm - constexpr auto operator""Npm(unsigned long long l) { return quantity(l); } - constexpr auto operator""Npm(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/time.h b/src/include/units/dimensions/time.h deleted file mode 100644 index 3c60e384..00000000 --- a/src/include/units/dimensions/time.h +++ /dev/null @@ -1,71 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include - -namespace units { - - struct time : derived_dimension> {}; - - template - concept Time = QuantityOf; - - struct second : named_coherent_derived_unit {}; - struct nanosecond : prefixed_derived_unit {}; - struct microsecond : prefixed_derived_unit {}; - struct millisecond : prefixed_derived_unit {}; - struct minute : named_scaled_derived_unit> {}; - struct hour : named_scaled_derived_unit> {}; - - inline namespace literals { - - // ns - constexpr auto operator""ns(unsigned long long l) { return quantity(l); } - constexpr auto operator""ns(long double l) { return quantity(l); } - - // us - constexpr auto operator""us(unsigned long long l) { return quantity(l); } - constexpr auto operator""us(long double l) { return quantity(l); } - - // ms - constexpr auto operator""ms(unsigned long long l) { return quantity(l); } - constexpr auto operator""ms(long double l) { return quantity(l); } - - // s - constexpr auto operator""s(unsigned long long l) { return quantity(l); } - constexpr auto operator""s(long double l) { return quantity(l); } - - // min - constexpr auto operator""min(unsigned long long l) { return quantity(l); } - constexpr auto operator""min(long double l) { return quantity(l); } - - // h - constexpr auto operator""h(unsigned long long l) { return quantity(l); } - constexpr auto operator""h(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/velocity.h b/src/include/units/dimensions/velocity.h deleted file mode 100644 index e583fba9..00000000 --- a/src/include/units/dimensions/velocity.h +++ /dev/null @@ -1,55 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include - -namespace units { - - struct velocity : derived_dimension, exp> {}; - - template - concept Velocity = QuantityOf; - - struct metre_per_second : coherent_derived_unit {}; - struct kilometre_per_hour : deduced_derived_unit {}; - struct mile_per_hour : deduced_derived_unit {}; - - inline namespace literals { - - // mps - constexpr auto operator""mps(unsigned long long l) { return quantity(l); } - constexpr auto operator""mps(long double l) { return quantity(l); } - - // kmph - constexpr auto operator""kmph(unsigned long long l) { return quantity(l); } - constexpr auto operator""kmph(long double l) { return quantity(l); } - - // mph - constexpr auto operator""mph(unsigned long long l) { return quantity(l); } - constexpr auto operator""mph(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/voltage.h b/src/include/units/dimensions/voltage.h deleted file mode 100644 index f862a873..00000000 --- a/src/include/units/dimensions/voltage.h +++ /dev/null @@ -1,48 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include -#include -#include -#include -#include - -namespace units { - - struct voltage : derived_dimension, exp> {}; - - template - concept Voltage = QuantityOf; - - struct volt : named_coherent_derived_unit {}; - - inline namespace literals { - - // V - constexpr auto operator""V(unsigned long long l) { return quantity(l); } - constexpr auto operator""V(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/volume.h b/src/include/units/dimensions/volume.h deleted file mode 100644 index 9cf72b53..00000000 --- a/src/include/units/dimensions/volume.h +++ /dev/null @@ -1,64 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#pragma once - -#include - -namespace units { - - struct volume : derived_dimension> {}; - - template - concept Volume = QuantityOf; - - struct cubic_metre : coherent_derived_unit {}; - struct cubic_millimetre : deduced_derived_unit {}; - struct cubic_centimetre : deduced_derived_unit {}; - struct cubic_kilometre : deduced_derived_unit {}; - struct cubic_foot : deduced_derived_unit {}; - - inline namespace literals { - - // cub_mm - constexpr auto operator""cub_mm(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_mm(long double l) { return quantity(l); } - - // cub_cm - constexpr auto operator""cub_cm(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_cm(long double l) { return quantity(l); } - - // cub_m - constexpr auto operator""cub_m(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_m(long double l) { return quantity(l); } - - // cub_km - constexpr auto operator""cub_km(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_km(long double l) { return quantity(l); } - - // cub_ft - constexpr auto operator""cub_ft(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_ft(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/exp.h b/src/include/units/exp.h new file mode 100644 index 00000000..b2d71d67 --- /dev/null +++ b/src/include/units/exp.h @@ -0,0 +1,87 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include + +namespace units { + +/** + * @brief A power of factor corresponding to the dimension of a quantity + * + * @tparam Dim component dimension of a derived quantity + * @tparam Num numinator of the factor + * @tparam Den denominator of the factor + */ +template +struct exp { + using dimension = Dim; + static constexpr int num = Num; + static constexpr int den = Den; +}; + +// is_exp +namespace detail { + +template +inline constexpr bool is_exp> = true; + +} // namespace detail + +// exp_less +template + requires BaseDimension && BaseDimension +struct exp_less : base_dimension_less {}; + +// exp_invert +namespace detail { + +template +constexpr exp exp_invert_impl(exp); + +} // namespace detail + +template +using exp_invert = decltype(detail::exp_invert_impl(E())); + +// exp_multiply +namespace detail { + +template +struct exp_multiply_impl { + using r1 = ratio; + using r2 = ratio; + using r = ratio_multiply; + using type = exp; +}; + +} // namespace detail + +template +using exp_multiply = detail::exp_multiply_impl::type; + +template +struct exp_list {}; + +} // namespace units diff --git a/src/include/units/format.h b/src/include/units/format.h index 1bbea90d..4b361a02 100644 --- a/src/include/units/format.h +++ b/src/include/units/format.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include #include #include @@ -116,20 +116,20 @@ namespace units { return format_to(out, treat_as_floating_point ? "{:" + sign_text + "g}" : "{:" + sign_text + "}", val); } - template + template inline static OutputIt format_units_quantity_unit(OutputIt out) { - return format_to(out, "{}", unit_text().c_str()); + return format_to(out, "{}", unit_text().c_str()); } - template + template struct units_formatter { OutputIt out; Rep val; fmt::sign_t sign; int precision; - explicit units_formatter(OutputIt o, quantity q, fmt::sign_t s, int prec): + explicit units_formatter(OutputIt o, quantity q, fmt::sign_t s, int prec): out(o), val(q.count()), sign(s), precision(prec) { } @@ -147,7 +147,7 @@ namespace units { void on_quantity_unit() { - out = format_units_quantity_unit(out); + out = format_units_quantity_unit(out); } }; @@ -155,11 +155,11 @@ namespace units { } // namespace units -template -struct fmt::formatter, CharT> { +template +struct fmt::formatter, CharT> { private: - using quantity = units::quantity; - using iterator = fmt::basic_parse_context::iterator; + using quantity = units::quantity; + using iterator = fmt::basic_format_parse_context::iterator; using arg_ref_type = fmt::internal::arg_ref; fmt::basic_format_specs specs; @@ -172,7 +172,7 @@ private: struct spec_handler { formatter& f; - fmt::basic_parse_context& context; + fmt::basic_format_parse_context& context; fmt::basic_string_view format_str; template @@ -185,8 +185,7 @@ private: constexpr arg_ref_type make_arg_ref(fmt::basic_string_view arg_id) { context.check_arg_id(arg_id); - const auto str_val = fmt::internal::string_view_metadata(format_str, arg_id); - return arg_ref_type(str_val); + return arg_ref_type(arg_id); } constexpr arg_ref_type make_arg_ref(fmt::internal::auto_id) @@ -226,7 +225,7 @@ private: iterator end; }; - constexpr parse_range do_parse(fmt::basic_parse_context& ctx) + constexpr parse_range do_parse(fmt::basic_format_parse_context& ctx) { auto begin = ctx.begin(), end = ctx.end(); if(begin == end || *begin == '}') @@ -285,7 +284,7 @@ private: } public: - constexpr auto parse(fmt::basic_parse_context& ctx) + constexpr auto parse(fmt::basic_format_parse_context& ctx) { auto range = do_parse(ctx); format_str = fmt::basic_string_view(&*range.begin, fmt::internal::to_unsigned(range.end - range.begin)); @@ -293,7 +292,7 @@ public: } template - auto format(const units::quantity& q, FormatContext& ctx) + auto format(const units::quantity& q, FormatContext& ctx) { auto begin = format_str.begin(), end = format_str.end(); @@ -302,15 +301,15 @@ public: auto out = std::back_inserter(buf); // process dynamic width and precision - fmt::internal::handle_dynamic_spec(specs.width, width_ref, ctx, format_str.begin()); - fmt::internal::handle_dynamic_spec(precision, precision_ref, ctx, format_str.begin()); + fmt::internal::handle_dynamic_spec(specs.width, width_ref, ctx); + fmt::internal::handle_dynamic_spec(precision, precision_ref, ctx); // deal with quantity content if(begin == end || *begin == '}') { // default format should print value followed by the unit separeted with 1 space out = units::detail::format_units_quantity_value(out, q.count(), specs.sign, precision); *out++ = CharT(' '); - units::detail::format_units_quantity_unit(out); + units::detail::format_units_quantity_unit(out); } else { // user provided format diff --git a/src/include/units/math.h b/src/include/units/math.h index 1fd79d17..b9782c3f 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -27,27 +27,29 @@ namespace units { - template - requires N == 0 - inline Rep AUTO pow(const quantity&) noexcept + template + requires (N == 0) + inline Rep pow(const quantity&) noexcept { return 1; } - template - inline Quantity AUTO pow(const quantity& q) noexcept + template + inline Quantity AUTO pow(const quantity& q) noexcept { - using dim = dimension_pow; - using r = ratio_pow; - return quantity>, Rep>(static_cast(std::pow(q.count(), N))); + using dim = dimension_pow; + using ratio = ratio_pow; + using unit = downcast_unit; + return quantity(static_cast(std::pow(q.count(), N))); } - template - inline Quantity AUTO sqrt(const quantity& q) noexcept + template + inline Quantity AUTO sqrt(const quantity& q) noexcept { - using dim = dimension_sqrt; - using r = ratio_sqrt; - return quantity>, Rep>(static_cast(std::sqrt(q.count()))); + using dim = dimension_sqrt; + using ratio = ratio_sqrt; + using unit = downcast_unit; + return quantity(static_cast(std::sqrt(q.count()))); } } // namespace units diff --git a/src/include/units/physical/cgs/acceleration.h b/src/include/units/physical/cgs/acceleration.h new file mode 100644 index 00000000..0c2b488a --- /dev/null +++ b/src/include/units/physical/cgs/acceleration.h @@ -0,0 +1,45 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::cgs { + +struct gal : named_unit {}; +struct dim_acceleration : physical::dim_acceleration {}; + +template +using acceleration = quantity; + +inline namespace literals { + +// Gal +constexpr auto operator""Gal(unsigned long long l) { return acceleration(l); } +constexpr auto operator""Gal(long double l) { return acceleration(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/area.h b/src/include/units/physical/cgs/area.h new file mode 100644 index 00000000..c9618a53 --- /dev/null +++ b/src/include/units/physical/cgs/area.h @@ -0,0 +1,47 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::cgs { + +using si::square_centimetre; + +struct dim_area : physical::dim_area {}; + +template +using area = quantity; + +inline namespace literals { + +// sq_cm +constexpr auto operator"" sq_cm(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_cm(long double l) { return area(l); } + +} + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/energy.h b/src/include/units/physical/cgs/energy.h new file mode 100644 index 00000000..413eb773 --- /dev/null +++ b/src/include/units/physical/cgs/energy.h @@ -0,0 +1,47 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::cgs { + +struct erg : named_unit {}; + +struct dim_energy : physical::dim_energy {}; + +template +using energy = quantity; + +inline namespace literals { + +// erg +constexpr auto operator""_erg(unsigned long long l) { return energy(l); } +constexpr auto operator""_erg(long double l) { return energy(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/force.h b/src/include/units/physical/cgs/force.h new file mode 100644 index 00000000..d9f9a1a7 --- /dev/null +++ b/src/include/units/physical/cgs/force.h @@ -0,0 +1,48 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +namespace units::cgs { + +struct dyne : named_unit {}; + +struct dim_force : physical::dim_force {}; + +template +using force = quantity; + +inline namespace literals { + +// dyn +constexpr auto operator""dyn(unsigned long long l) { return force(l); } +constexpr auto operator""dyn(long double l) { return force(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/dimensions/current.h b/src/include/units/physical/cgs/length.h similarity index 68% rename from src/include/units/dimensions/current.h rename to src/include/units/physical/cgs/length.h index 072f9a8e..a49a4cb8 100644 --- a/src/include/units/dimensions/current.h +++ b/src/include/units/physical/cgs/length.h @@ -22,24 +22,25 @@ #pragma once -#include +#include +#include #include -namespace units { +namespace units::cgs { - struct current : derived_dimension> {}; +using si::centimetre; - template - concept Current = QuantityOf; +struct dim_length : physical::dim_length {}; - struct ampere : named_coherent_derived_unit {}; +template +using length = quantity; - inline namespace literals { +inline namespace literals { - // A - constexpr auto operator""A(unsigned long long l) { return quantity(l); } - constexpr auto operator""A(long double l) { return quantity(l); } +// cm +constexpr auto operator"" cm(unsigned long long l) { return length(l); } +constexpr auto operator"" cm(long double l) { return length(l); } - } +} -} // namespace units +} // namespace units::cgs diff --git a/src/include/units/dimensions/si_base_dimensions.h b/src/include/units/physical/cgs/mass.h similarity index 68% rename from src/include/units/dimensions/si_base_dimensions.h rename to src/include/units/physical/cgs/mass.h index ef2ea824..d809eb32 100644 --- a/src/include/units/dimensions/si_base_dimensions.h +++ b/src/include/units/physical/cgs/mass.h @@ -22,16 +22,25 @@ #pragma once -#include +#include +#include +#include -namespace units { +namespace units::cgs { - struct base_dim_length : base_dimension<"length", "m"> {}; - struct base_dim_mass : base_dimension<"mass", "kg"> {}; - struct base_dim_time : base_dimension<"time", "s"> {}; - struct base_dim_current : base_dimension<"current", "A"> {}; - struct base_dim_temperature : base_dimension<"temperature", "K"> {}; - struct base_dim_substance : base_dimension<"substance", "mol"> {}; - struct base_dim_luminous_intensity : base_dimension<"luminous intensity", "cd"> {}; +using si::gram; -} // namespace units +struct dim_mass : physical::dim_mass {}; + +template +using mass = quantity; + +inline namespace literals { + +// g +constexpr auto operator""g(unsigned long long l) { return mass(l); } +constexpr auto operator""g(long double l) { return mass(l); } + +} + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/power.h b/src/include/units/physical/cgs/power.h new file mode 100644 index 00000000..5a813cb2 --- /dev/null +++ b/src/include/units/physical/cgs/power.h @@ -0,0 +1,47 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::cgs { + +struct erg_per_second : unit {}; + +struct dim_power : physical::dim_power {}; + +template +using power = quantity; + +inline namespace literals { + +// ergps +constexpr auto operator""_ergps(unsigned long long l) { return power(l); } +constexpr auto operator""_ergps(long double l) { return power(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/pressure.h b/src/include/units/physical/cgs/pressure.h new file mode 100644 index 00000000..0e3b98d4 --- /dev/null +++ b/src/include/units/physical/cgs/pressure.h @@ -0,0 +1,48 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +namespace units::cgs { + +struct barye : named_unit {}; + +struct dim_pressure : physical::dim_pressure {}; + +template +using pressure = quantity; + +inline namespace literals { + +// Ba +constexpr auto operator""Ba(unsigned long long l) { return pressure(l); } +constexpr auto operator""Ba(long double l) { return pressure(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/bits/type_traits.h b/src/include/units/physical/cgs/time.h similarity index 73% rename from src/include/units/bits/type_traits.h rename to src/include/units/physical/cgs/time.h index 4a9c8a50..c678bc79 100644 --- a/src/include/units/bits/type_traits.h +++ b/src/include/units/physical/cgs/time.h @@ -22,27 +22,21 @@ #pragma once -#include +#include +#include +#include -namespace units { +namespace units::cgs { - namespace detail { +using si::second; - template - struct conditional_impl { - template - using type = F; - }; +using si::dim_time; +using si::time; - template<> - struct conditional_impl { - template - using type = T; - }; +inline namespace literals { - } - - template - using conditional = detail::conditional_impl::template type; +using si::literals::operator"" s; } + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/velocity.h b/src/include/units/physical/cgs/velocity.h new file mode 100644 index 00000000..8adbaa58 --- /dev/null +++ b/src/include/units/physical/cgs/velocity.h @@ -0,0 +1,46 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::cgs { + +struct centimetre_per_second : unit {}; +struct dim_velocity : physical::dim_velocity {}; + +template +using velocity = quantity; + +inline namespace literals { + +// cmps +constexpr auto operator"" cmps(unsigned long long l) { return velocity(l); } +constexpr auto operator"" cmps(long double l) { return velocity(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/dimensions.h b/src/include/units/physical/dimensions.h new file mode 100644 index 00000000..ea8ceb51 --- /dev/null +++ b/src/include/units/physical/dimensions.h @@ -0,0 +1,166 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units { + +namespace physical { + +template typename DimTemplate> +concept DimensionOf = (Dimension || BaseDimension) && is_derived_from_instantiation; + +template typename DimTemplate> +concept QuantityOf = Quantity && is_derived_from_instantiation; + +// ------------------------ base dimensions ----------------------------- + +template +struct dim_length : base_dimension<"L", U> {}; + +template +struct dim_mass : base_dimension<"M", U> {}; + +template +struct dim_time : base_dimension<"T", U> {}; + +template +struct dim_electric_current : base_dimension<"I", U> {}; + +template +struct dim_thermodynamic_temperature : base_dimension<"Θ", U> {}; + +template +struct dim_substance : base_dimension<"N", U> {}; + +template +struct dim_luminous_intensity : base_dimension<"J", U> {}; + +// ------------------------ derived dimensions ----------------------------- + +template T> +struct dim_frequency : derived_dimension> {}; + +template L> +struct dim_area : derived_dimension> {}; + +template L> +struct dim_volume : derived_dimension> {}; + +template L, DimensionOf T> +struct dim_velocity : derived_dimension, exp> {}; + +template L, DimensionOf T> +struct dim_acceleration : derived_dimension, exp> {}; + +template M, DimensionOf A> +struct dim_force : derived_dimension, exp> {}; + +template F, DimensionOf L> +struct dim_energy : derived_dimension, exp> {}; + +template E, DimensionOf T> +struct dim_power : derived_dimension, exp> {}; + +template P, DimensionOf C> +struct dim_voltage : derived_dimension, exp> {}; + +template T, DimensionOf C> +struct dim_electric_charge : derived_dimension, exp> {}; + +template C, DimensionOf V> +struct dim_capacitance : derived_dimension, exp> {}; + +template F, DimensionOf L> +struct dim_surface_tension : derived_dimension, exp> {}; + +template F, DimensionOf A> +struct dim_pressure : derived_dimension, exp> {}; + +} // namespace physical + +template +concept Length = physical::QuantityOf; + +template +concept Mass = physical::QuantityOf; + +template +concept Time = physical::QuantityOf; + +template +concept Current = physical::QuantityOf; + +template +concept Temperature = physical::QuantityOf; + +template +concept Substance = physical::QuantityOf; + +template +concept LuminousIntensity = physical::QuantityOf; + +template +concept Frequency = physical::QuantityOf; + +template +concept Area = physical::QuantityOf; + +template +concept Volume = physical::QuantityOf; + +template +concept Velocity = physical::QuantityOf; + +template +concept Acceleration = physical::QuantityOf; + +template +concept Force = physical::QuantityOf; + +template +concept Energy = physical::QuantityOf; + +template +concept Power = physical::QuantityOf; + +template +concept Voltage = physical::QuantityOf; + +template +concept ElectricCharge = physical::QuantityOf; + +template +concept Capacitance = physical::QuantityOf; + +template +concept SurfaceTension = physical::QuantityOf; + +template +concept Pressure = physical::QuantityOf; + +} // namespace units diff --git a/src/include/units/physical/si/acceleration.h b/src/include/units/physical/si/acceleration.h new file mode 100644 index 00000000..b7d34653 --- /dev/null +++ b/src/include/units/physical/si/acceleration.h @@ -0,0 +1,45 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct metre_per_second_sq : unit {}; +struct dim_acceleration : physical::dim_acceleration {}; + +template +using acceleration = quantity; + +inline namespace literals { + +// mps_sq +constexpr auto operator""mps_sq(unsigned long long l) { return acceleration(l); } +constexpr auto operator""mps_sq(long double l) { return acceleration(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/area.h b/src/include/units/physical/si/area.h new file mode 100644 index 00000000..4a53594d --- /dev/null +++ b/src/include/units/physical/si/area.h @@ -0,0 +1,66 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct square_metre : unit {}; +struct dim_area : physical::dim_area {}; + +struct square_millimetre : deduced_unit {}; +struct square_centimetre : deduced_unit {}; +struct square_kilometre : deduced_unit {}; +struct square_foot : deduced_unit {}; + +template +using area = quantity; + +inline namespace literals { + +// sq_m +constexpr auto operator"" sq_m(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_m(long double l) { return area(l); } + +// sq_mm +constexpr auto operator"" sq_mm(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_mm(long double l) { return area(l); } + +// sq_cm +constexpr auto operator"" sq_cm(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_cm(long double l) { return area(l); } + +// sq_km +constexpr auto operator"" sq_km(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_km(long double l) { return area(l); } + +// sq_ft +constexpr auto operator"" sq_ft(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_ft(long double l) { return area(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/capacitance.h b/src/include/units/physical/si/capacitance.h new file mode 100644 index 00000000..eb71780f --- /dev/null +++ b/src/include/units/physical/si/capacitance.h @@ -0,0 +1,48 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +namespace units::si { + +struct farad : named_unit {}; + +struct dim_capacitance : physical::dim_capacitance {}; + +template +using capacitance = quantity; + +inline namespace literals { + +// F +constexpr auto operator""F(unsigned long long l) { return capacitance(l); } +constexpr auto operator""_F(long double l) { return capacitance(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/current.h b/src/include/units/physical/si/current.h new file mode 100644 index 00000000..26b5143b --- /dev/null +++ b/src/include/units/physical/si/current.h @@ -0,0 +1,46 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct ampere : named_unit {}; + +struct dim_electric_current : physical::dim_electric_current {}; + +template +using current = quantity; + +inline namespace literals { + +// A +constexpr auto operator""A(unsigned long long l) { return current(l); } +constexpr auto operator""A(long double l) { return current(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/electric_charge.h b/src/include/units/physical/si/electric_charge.h new file mode 100644 index 00000000..c2059c98 --- /dev/null +++ b/src/include/units/physical/si/electric_charge.h @@ -0,0 +1,47 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::si { + +struct coulomb : named_unit {}; + +struct dim_electric_charge : physical::dim_electric_charge {}; + +template +using electric_charge = quantity; + +inline namespace literals { + +// C +constexpr auto operator""C(unsigned long long l) { return electric_charge(l); } +constexpr auto operator""C(long double l) { return electric_charge(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/energy.h b/src/include/units/physical/si/energy.h new file mode 100644 index 00000000..719d4539 --- /dev/null +++ b/src/include/units/physical/si/energy.h @@ -0,0 +1,67 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::si { + +struct joule : named_unit {}; +struct millijoule : prefixed_unit {}; +struct kilojoule : prefixed_unit {}; +struct megajoule : prefixed_unit {}; +struct gigajoule : prefixed_unit {}; + +struct dim_energy : physical::dim_energy {}; + +template +using energy = quantity; + +inline namespace literals { + +// J +constexpr auto operator""_J(unsigned long long l) { return energy(l); } +constexpr auto operator""_J(long double l) { return energy(l); } + +// mJ +constexpr auto operator""mJ(unsigned long long l) { return energy(l); } +constexpr auto operator""mJ(long double l) { return energy(l); } + +// kJ +constexpr auto operator""kJ(unsigned long long l) { return energy(l); } +constexpr auto operator""kJ(long double l) { return energy(l); } + +// MJ +constexpr auto operator""MJ(unsigned long long l) { return energy(l); } +constexpr auto operator""MJ(long double l) { return energy(l); } + +// GJ +constexpr auto operator""GJ(unsigned long long l) { return energy(l); } +constexpr auto operator""GJ(long double l) { return energy(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/force.h b/src/include/units/physical/si/force.h new file mode 100644 index 00000000..e3f7cf19 --- /dev/null +++ b/src/include/units/physical/si/force.h @@ -0,0 +1,48 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +namespace units::si { + +struct newton : named_unit {}; + +struct dim_force : physical::dim_force {}; + +template +using force = quantity; + +inline namespace literals { + +// N +constexpr auto operator""N(unsigned long long l) { return force(l); } +constexpr auto operator""N(long double l) { return force(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/frequency.h b/src/include/units/physical/si/frequency.h new file mode 100644 index 00000000..be896849 --- /dev/null +++ b/src/include/units/physical/si/frequency.h @@ -0,0 +1,71 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct hertz : named_unit {}; +struct millihertz : prefixed_unit {}; +struct kilohertz : prefixed_unit {}; +struct megahertz : prefixed_unit {}; +struct gigahertz : prefixed_unit {}; +struct terahertz : prefixed_unit {}; + +struct dim_frequency : physical::dim_frequency {}; + +template +using frequency = quantity; + +inline namespace literals { + +// Hz +constexpr auto operator"" Hz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" Hz(long double l) { return frequency(l); } + +// mHz +constexpr auto operator"" mHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" mHz(long double l) { return frequency(l); } + +// kHz +constexpr auto operator"" kHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" kHz(long double l) { return frequency(l); } + +// MHz +constexpr auto operator"" MHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" MHz(long double l) { return frequency(l); } + +// GHz +constexpr auto operator"" GHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" GHz(long double l) { return frequency(l); } + +// THz +constexpr auto operator"" THz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" THz(long double l) { return frequency(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/length.h b/src/include/units/physical/si/length.h new file mode 100644 index 00000000..6bf17294 --- /dev/null +++ b/src/include/units/physical/si/length.h @@ -0,0 +1,87 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct metre : named_unit {}; +struct millimetre : prefixed_unit {}; +struct centimetre : prefixed_unit {}; +struct kilometre : prefixed_unit {}; + +struct dim_length : physical::dim_length {}; + +template +using length = quantity; + +inline namespace literals { + +// m +constexpr auto operator"" m(unsigned long long l) { return length(l); } +constexpr auto operator"" m(long double l) { return length(l); } + +// mm +constexpr auto operator"" mm(unsigned long long l) { return length(l); } +constexpr auto operator"" mm(long double l) { return length(l); } + +// cm +constexpr auto operator"" cm(unsigned long long l) { return length(l); } +constexpr auto operator"" cm(long double l) { return length(l); } + +// km +constexpr auto operator"" km(unsigned long long l) { return length(l); } +constexpr auto operator"" km(long double l) { return length(l); } + +} // namespace literals + +// US customary units +struct yard : named_scaled_unit, metre> {}; +struct foot : named_scaled_unit, yard> {}; +struct inch : named_scaled_unit, foot> {}; +struct mile : named_scaled_unit, yard> {}; + +inline namespace literals { + +// yd +constexpr auto operator"" yd(unsigned long long l) { return length(l); } +constexpr auto operator"" yd(long double l) { return length(l); } + +// ft +constexpr auto operator"" ft(unsigned long long l) { return length(l); } +constexpr auto operator"" ft(long double l) { return length(l); } + +// in +constexpr auto operator"" in(unsigned long long l) { return length(l); } +constexpr auto operator"" in(long double l) { return length(l); } + +// mi +constexpr auto operator"" mi(unsigned long long l) { return length(l); } +constexpr auto operator"" mi(long double l) { return length(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/dimensions/luminous_intensity.h b/src/include/units/physical/si/luminous_intensity.h similarity index 64% rename from src/include/units/dimensions/luminous_intensity.h rename to src/include/units/physical/si/luminous_intensity.h index 736da752..d3bcd5e2 100644 --- a/src/include/units/dimensions/luminous_intensity.h +++ b/src/include/units/physical/si/luminous_intensity.h @@ -22,24 +22,25 @@ #pragma once -#include +#include +#include #include -namespace units { +namespace units::si { - struct luminous_intensity : derived_dimension> {}; +struct candela : named_unit {}; - template - concept LuminousIntensity = QuantityOf; +struct dim_luminous_intensity : physical::dim_luminous_intensity {}; - struct candela : named_coherent_derived_unit {}; +template +using luminous_intensity = quantity; - inline namespace literals { +inline namespace literals { - // cd - constexpr auto operator""cd(unsigned long long l) { return quantity(l); } - constexpr auto operator""cd(long double l) { return quantity(l); } +// cd +constexpr auto operator""cd(unsigned long long l) { return luminous_intensity(l); } +constexpr auto operator""cd(long double l) { return luminous_intensity(l); } - } // namespace literals +} // namespace literals -} // namespace units +} // namespace units::si diff --git a/src/include/units/physical/si/mass.h b/src/include/units/physical/si/mass.h new file mode 100644 index 00000000..7af3df31 --- /dev/null +++ b/src/include/units/physical/si/mass.h @@ -0,0 +1,51 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct gram : named_unit {}; +struct kilogram : prefixed_unit {}; + +struct dim_mass : physical::dim_mass {}; + +template +using mass = quantity; + +inline namespace literals { + +// g +constexpr auto operator""g(unsigned long long l) { return mass(l); } +constexpr auto operator""g(long double l) { return mass(l); } + +// kg +constexpr auto operator""kg(unsigned long long l) { return mass(l); } +constexpr auto operator""kg(long double l) { return mass(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/power.h b/src/include/units/physical/si/power.h new file mode 100644 index 00000000..072ede21 --- /dev/null +++ b/src/include/units/physical/si/power.h @@ -0,0 +1,67 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::si { + +struct watt : named_unit {}; +struct milliwatt : prefixed_unit {}; +struct kilowatt : prefixed_unit {}; +struct megawatt : prefixed_unit {}; +struct gigawatt : prefixed_unit {}; + +struct dim_power : physical::dim_power {}; + +template +using power = quantity; + +inline namespace literals { + +// W +constexpr auto operator""W(unsigned long long l) { return power(l); } +constexpr auto operator""_W(long double l) { return power(l); } + +// mW +constexpr auto operator""mW(unsigned long long l) { return power(l); } +constexpr auto operator""mW(long double l) { return power(l); } + +// kW +constexpr auto operator""kW(unsigned long long l) { return power(l); } +constexpr auto operator""kW(long double l) { return power(l); } + +// MW +constexpr auto operator""MW(unsigned long long l) { return power(l); } +constexpr auto operator""MW(long double l) { return power(l); } + +// GW +constexpr auto operator""GW(unsigned long long l) { return power(l); } +constexpr auto operator""GW(long double l) { return power(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/prefixes.h b/src/include/units/physical/si/prefixes.h new file mode 100644 index 00000000..6c09b90a --- /dev/null +++ b/src/include/units/physical/si/prefixes.h @@ -0,0 +1,51 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include + +namespace units::si { + +struct prefix : prefix_type {}; + +// TODO Remove dependency on std::ratio + +struct atto : units::prefix> {}; +struct femto : units::prefix> {}; +struct pico : units::prefix> {}; +struct nano : units::prefix> {}; +struct micro : units::prefix> {}; +struct milli : units::prefix> {}; +struct centi : units::prefix> {}; +struct deci : units::prefix> {}; +struct deca : units::prefix> {}; +struct hecto : units::prefix> {}; +struct kilo : units::prefix> {}; +struct mega : units::prefix> {}; +struct giga : units::prefix> {}; +struct tera : units::prefix> {}; +struct peta : units::prefix> {}; +struct exa : units::prefix> {}; + +} // namespace units::si diff --git a/src/include/units/physical/si/pressure.h b/src/include/units/physical/si/pressure.h new file mode 100644 index 00000000..a4256305 --- /dev/null +++ b/src/include/units/physical/si/pressure.h @@ -0,0 +1,48 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +namespace units::si { + +struct pascal : named_unit {}; + +struct dim_pressure : physical::dim_pressure {}; + +template +using pressure = quantity; + +inline namespace literals { + +// Pa +constexpr auto operator""Pa(unsigned long long l) { return pressure(l); } +constexpr auto operator""Pa(long double l) { return pressure(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/dimensions/substance.h b/src/include/units/physical/si/substance.h similarity index 67% rename from src/include/units/dimensions/substance.h rename to src/include/units/physical/si/substance.h index 54b15fa4..285fc33f 100644 --- a/src/include/units/dimensions/substance.h +++ b/src/include/units/physical/si/substance.h @@ -22,24 +22,25 @@ #pragma once -#include +#include +#include #include -namespace units { +namespace units::si { - struct substance : derived_dimension> {}; +struct mole : named_unit {}; - template - concept Substance = QuantityOf; +struct dim_substance : physical::dim_substance {}; - struct mole : named_coherent_derived_unit {}; +template +using substance = quantity; - inline namespace literals { +inline namespace literals { - // mol - constexpr auto operator""mol(unsigned long long l) { return quantity(l); } - constexpr auto operator""mol(long double l) { return quantity(l); } +// mol +constexpr auto operator"" mol(unsigned long long l) { return substance(l); } +constexpr auto operator"" mol(long double l) { return substance(l); } - } // namespace literals +} // namespace literals -} // namespace units +} // namespace units::si diff --git a/src/include/units/physical/si/surface_tension.h b/src/include/units/physical/si/surface_tension.h new file mode 100644 index 00000000..3e4b01b2 --- /dev/null +++ b/src/include/units/physical/si/surface_tension.h @@ -0,0 +1,46 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct newton_per_metre : unit {}; + +struct dim_surface_tension : physical::dim_surface_tension {}; + +template +using surface_tension = quantity; + +inline namespace literals { + + // Nm + constexpr auto operator""Npm(unsigned long long l) { return surface_tension(l); } + constexpr auto operator""Npm(long double l) { return surface_tension(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/dimensions/temperature.h b/src/include/units/physical/si/temperature.h similarity index 64% rename from src/include/units/dimensions/temperature.h rename to src/include/units/physical/si/temperature.h index 5a26a56a..a00ef3b1 100644 --- a/src/include/units/dimensions/temperature.h +++ b/src/include/units/physical/si/temperature.h @@ -22,24 +22,24 @@ #pragma once -#include +#include #include -namespace units { +namespace units::si { - struct temperature : derived_dimension> {}; +struct kelvin : named_unit {}; - template - concept ThermodynamicTemperature = QuantityOf; +struct dim_thermodynamic_temperature : physical::dim_thermodynamic_temperature {}; - struct kelvin : named_coherent_derived_unit {}; +template +using temperature = quantity; - inline namespace literals { +inline namespace literals { - // K - constexpr auto operator""K(unsigned long long l) { return quantity(l); } - constexpr auto operator""_K(long double l) { return quantity(l); } // TODO: conflicts with gcc GNU extension +// K +constexpr auto operator""K(unsigned long long l) { return temperature(l); } +constexpr auto operator""_K(long double l) { return temperature(l); } // TODO: conflicts with gcc GNU extension - } // namespace literals +} // namespace literals -} // namespace units +} // namespace units::si diff --git a/src/include/units/physical/si/time.h b/src/include/units/physical/si/time.h new file mode 100644 index 00000000..9bd60783 --- /dev/null +++ b/src/include/units/physical/si/time.h @@ -0,0 +1,71 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct second : named_unit {}; +struct nanosecond : prefixed_unit {}; +struct microsecond : prefixed_unit {}; +struct millisecond : prefixed_unit {}; +struct minute : named_scaled_unit, second> {}; +struct hour : named_scaled_unit, second> {}; + +struct dim_time : physical::dim_time {}; + +template +using time = quantity; + +inline namespace literals { + +// ns +constexpr auto operator""ns(unsigned long long l) { return time(l); } +constexpr auto operator""ns(long double l) { return time(l); } + +// us +constexpr auto operator""us(unsigned long long l) { return time(l); } +constexpr auto operator""us(long double l) { return time(l); } + +// ms +constexpr auto operator""ms(unsigned long long l) { return time(l); } +constexpr auto operator""ms(long double l) { return time(l); } + +// s +constexpr auto operator""s(unsigned long long l) { return time(l); } +constexpr auto operator""s(long double l) { return time(l); } + +// min +constexpr auto operator""min(unsigned long long l) { return time(l); } +constexpr auto operator""min(long double l) { return time(l); } + +// h +constexpr auto operator""h(unsigned long long l) { return time(l); } +constexpr auto operator""h(long double l) { return time(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/velocity.h b/src/include/units/physical/si/velocity.h new file mode 100644 index 00000000..bcd168bd --- /dev/null +++ b/src/include/units/physical/si/velocity.h @@ -0,0 +1,57 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include + +namespace units::si { + +struct metre_per_second : unit {}; +struct dim_velocity : physical::dim_velocity {}; + +struct kilometre_per_hour : deduced_unit {}; +struct mile_per_hour : deduced_unit {}; + +template +using velocity = quantity; + +inline namespace literals { + +// mps +constexpr auto operator"" mps(unsigned long long l) { return velocity(l); } +constexpr auto operator"" mps(long double l) { return velocity(l); } + +// kmph +constexpr auto operator"" kmph(unsigned long long l) { return velocity(l); } +constexpr auto operator"" kmph(long double l) { return velocity(l); } + +// mph +constexpr auto operator"" mph(unsigned long long l) { return velocity(l); } +constexpr auto operator"" mph(long double l) { return velocity(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/voltage.h b/src/include/units/physical/si/voltage.h new file mode 100644 index 00000000..7138e728 --- /dev/null +++ b/src/include/units/physical/si/voltage.h @@ -0,0 +1,48 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +namespace units::si { + +struct volt : named_unit {}; + +struct dim_voltage : physical::dim_voltage {}; + +template +using voltage = quantity; + +inline namespace literals { + +// V +constexpr auto operator""V(unsigned long long l) { return voltage(l); } +constexpr auto operator""V(long double l) { return voltage(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/volume.h b/src/include/units/physical/si/volume.h new file mode 100644 index 00000000..be80331a --- /dev/null +++ b/src/include/units/physical/si/volume.h @@ -0,0 +1,66 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include +#include + +namespace units::si { + +struct cubic_metre : unit {}; +struct dim_volume : physical::dim_volume {}; + +struct cubic_millimetre : deduced_unit {}; +struct cubic_centimetre : deduced_unit {}; +struct cubic_kilometre : deduced_unit {}; +struct cubic_foot : deduced_unit {}; + +template +using volume = quantity; + +inline namespace literals { + +// cub_mm +constexpr auto operator""cub_mm(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_mm(long double l) { return volume(l); } + +// cub_cm +constexpr auto operator""cub_cm(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_cm(long double l) { return volume(l); } + +// cub_m +constexpr auto operator""cub_m(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_m(long double l) { return volume(l); } + +// cub_km +constexpr auto operator""cub_km(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_km(long double l) { return volume(l); } + +// cub_ft +constexpr auto operator""cub_ft(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_ft(long double l) { return volume(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index 1677644b..eaded432 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -22,49 +22,56 @@ #pragma once -#include -#include +#include +#include #include namespace units { - struct prefix_type {}; +/** + * @brief The base for all prefix types + * + * Every prefix type should inherit from this type to satisfy PrefixType concept. + */ +struct prefix_type {}; - template - concept PrefixType = std::derived_from; +/** + * @brief No prefix possible for the unit + * + * This is a special prefix type tag specifying that the unit can not be scaled with any kind + * of the prefix. + */ +struct no_prefix : prefix_type {}; - namespace detail { +namespace detail { - template - struct prefix_base : downcast_base> { - using prefix_type = PT; - using ratio = R; - }; +template +struct prefix_base : downcast_base> { + using prefix_type = PT; + using ratio = R; +}; - } +} // namespace detail - template - struct prefix : downcast_child> { - static constexpr auto symbol = Symbol; - }; - - - // TODO gcc:92150 - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92150 - // namespace detail { - - // template - // inline constexpr bool is_prefix = false; - - // template - // inline constexpr bool is_prefix> = true; - - // } // namespace detail - - template -// concept Prefix = detail::is_prefix; - concept Prefix = true; - - struct no_prefix : prefix_type {}; +/** + * @brief A prefix used to scale units + * + * Data from a prefix class is used in two cases: + * - when defining a prefixed_unit its ratio is used to scale the reference unit and its + * symbol is used to prepend to the symbol of referenced unit + * - when printing the symbol of a scaled unit that was not predefined by the user but its + * factor matches ratio of a prefix from the specified prefix family, its symbol will be + * prepended to the symbol of the unit + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam PT a type of prefix family + * @tparam Symbol a text representation of the prefix + * @tparam R factor to be used to scale a unit + */ +template + requires (!std::same_as) +struct prefix : downcast_child>> { + static constexpr auto symbol = Symbol; +}; } // namespace units diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index b80ecc83..acbc276b 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -22,518 +22,430 @@ #pragma once -#include -#include -#include +#include +#include +#include +#include + +#if __GNUC__ >= 10 +#include +#endif + #include namespace units { - // is_quantity - namespace detail { +namespace detail { - template - inline constexpr bool is_quantity = false; +template +concept safe_convertible = // exposition only + std::convertible_to && + (treat_as_floating_point || (!treat_as_floating_point)); - // partial specialization below after the first quantity forward declaration +template +concept safe_divisible = // exposition only + treat_as_floating_point || + ratio_divide::den == 1; - } // namespace detail +} // namespace detail - template - concept Quantity = detail::is_quantity; +/** + * @brief A quantity + * + * Property of a phenomenon, body, or substance, where the property has a magnitude that can be + * expressed by means of a number and a measurement unit. + * + * @tparam D a dimension of the quantity (can be either a BaseDimension or a DerivedDimension) + * @tparam U a measurement unit of the quantity + * @tparam Rep a type to be used to represent values of a quantity + */ +template U, Scalar Rep = double> +class quantity { + Rep value_; - template - concept QuantityOf = Quantity && Dimension && same_dim; +public: + using dimension = D; + using unit = U; + using rep = Rep; - // Scalar - template - concept Scalar = - (!Quantity) && - std::regular && - std::totally_ordered && - detail::basic_arithmetic; + quantity() = default; + quantity(const quantity&) = default; + quantity(quantity&&) = default; - template - class quantity; + template + requires detail::safe_convertible + constexpr explicit quantity(const Value& v) : value_{static_cast(v)} {} - namespace detail { + template + requires equivalent_dim && + detail::safe_convertible && + detail::safe_divisible + constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} {} - template - inline constexpr bool is_quantity> = true; + quantity& operator=(const quantity&) = default; + quantity& operator=(quantity&&) = default; - } // namespace detail + [[nodiscard]] constexpr rep count() const noexcept { return value_; } - // common_quantity - namespace detail { - - template - struct common_quantity_impl; - - template - struct common_quantity_impl, quantity, Rep> { - using type = quantity; - }; - - template - requires same_dim - struct common_quantity_impl, quantity, Rep> { - using type = - quantity>>, - Rep>; - }; - - } // namespace detail - - template> - using common_quantity = detail::common_quantity_impl::type; - - // quantity_cast - - namespace detail { - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - if constexpr(treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * - (static_cast(CRatio::num) / static_cast(CRatio::den)))); - } - else { - return To( - static_cast(static_cast(q.count()) * static_cast(CRatio::num) / static_cast(CRatio::den))); - } - } - }; - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - return To(static_cast(q.count())); - } - }; - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - if constexpr(treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * (CRep{1} / static_cast(CRatio::den)))); - } - else { - return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); - } - } - }; - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); - } - }; - - } // namespace detail - - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim && - detail::basic_arithmetic> + template + [[nodiscard]] static constexpr quantity zero() noexcept + requires requires { quantity_values::zero(); } + // requires requires { quantity_values::zero(); } // TODO gated by gcc-9 (fixed in gcc-10) { - using c_ratio = ratio_divide; - using c_rep = std::common_type_t; - using ret_dim = downcast; - using ret_unit = downcast>; - using ret = quantity; - using cast = detail::quantity_cast_impl; - return cast::cast(q); + return quantity(quantity_values::zero()); } - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim && - detail::basic_arithmetic> + template + [[nodiscard]] static constexpr quantity one() noexcept + requires requires { quantity_values::one(); } + // requires requires { quantity_values::one(); } // TODO gated by gcc-9 (fixed in gcc-10) { - return quantity_cast>(q); + return quantity(quantity_values::one()); } - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim + template + [[nodiscard]] static constexpr quantity min() noexcept + requires requires { quantity_values::min(); } + // requires requires { quantity_values::min(); } // TODO gated by gcc-9 (fixed in gcc-10) { - return quantity_cast>(q); + return quantity(quantity_values::min()); } - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires detail::basic_arithmetic> + template + [[nodiscard]] static constexpr quantity max() noexcept + requires requires { quantity_values::max(); } + // requires requires { quantity_values::max(); } // TODO gated by gcc-9 (fixed in gcc-10) { - return quantity_cast>(q); + return quantity(quantity_values::max()); } - // quantity_values + [[nodiscard]] constexpr quantity operator+() const { return *this; } - template - struct quantity_values { - static constexpr Rep zero() noexcept { return Rep(0); } - static constexpr Rep one() noexcept { return Rep(1); } - static constexpr Rep max() noexcept { return std::numeric_limits::max(); } - static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } - }; - - - // quantity - - template - class quantity { - Rep value_; - - public: - using unit = U; - using rep = Rep; - using dimension = U::dimension; - - quantity() = default; - quantity(const quantity&) = default; - quantity(quantity&&) = default; - - template - requires detail::safe_convertible - constexpr explicit quantity(const Value& v): value_{static_cast(v)} - { - } - - template - requires same_dim && - detail::safe_convertible && - detail::safe_divisible - constexpr quantity(const Q2& q): value_{quantity_cast(q).count()} - { - } - - quantity& operator=(const quantity&) = default; - quantity& operator=(quantity&&) = default; - - [[nodiscard]] constexpr rep count() const noexcept { return value_; } - - template - [[nodiscard]] static constexpr quantity zero() noexcept - requires requires { quantity_values::zero(); } - // requires requires { quantity_values::zero(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::zero()); - } - - template - [[nodiscard]] static constexpr quantity one() noexcept - requires requires { quantity_values::one(); } -// requires requires { quantity_values::one(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::one()); - } - - template - [[nodiscard]] static constexpr quantity min() noexcept - requires requires { quantity_values::min(); } -// requires requires { quantity_values::min(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::min()); - } - - template - [[nodiscard]] static constexpr quantity max() noexcept - requires requires { quantity_values::max(); } -// requires requires { quantity_values::max(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::max()); - } - - [[nodiscard]] constexpr quantity operator+() const { return *this; } - - template - [[nodiscard]] constexpr quantity operator-() const - requires std::regular_invocable -// requires std::regular_invocable // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(-count()); - } - - template - constexpr quantity& operator++() - requires requires(T v) { { ++v } -> SAME_AS(T&); } -// requires requires(rep v) { { ++v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - ++value_; - return *this; - } - - template - requires requires(T v) { { v++ } -> SAME_AS(T); } - constexpr quantity operator++(int) - // requires requires(rep v) { { v++ } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { return quantity(value_++); } - - template - requires requires(T v) { { --v } -> SAME_AS(T&); } - constexpr quantity& operator--() - // requires requires(rep v) { { --v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - --value_; - return *this; - } - - template - requires requires(T v) { { v-- } -> SAME_AS(T); } - constexpr quantity operator--(int) - // requires requires(rep v) { { v-- } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { return quantity(value_--); } - - template - requires requires(T v1, T v2) { { v1 += v2 } -> SAME_AS(T&); } - constexpr quantity& operator+=(const quantity& q) - // requires requires(rep v1, rep v2) { { v1 += v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ += q.count(); - return *this; - } - - template - requires requires(T v1, T v2) { { v1 -= v2 } -> SAME_AS(T&); } - constexpr quantity& operator-=(const quantity& q) - // requires requires(rep v1, rep v2) { { v1 -= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ -= q.count(); - return *this; - } - - template - requires requires(T v1, T v2) { { v1 *= v2 } -> SAME_AS(T&); } - constexpr quantity& operator*=(const rep& rhs) - // requires requires(rep v1, rep v2) { { v1 *= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ *= rhs; - return *this; - } - - template - requires requires(T v1, T v2) { { v1 /= v2 } -> SAME_AS(T&); } - constexpr quantity& operator/=(const rep& rhs) - // requires requires(rep v1, rep v2) { { v1 /= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ /= rhs; - return *this; - } - - template - constexpr quantity& operator%=(const Value& rhs) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - requires(T v1, Value v2) { { v1 %= v2 } -> SAME_AS(T&); } - // requires(rep v1, Value v2) { { v1 %= v2 } -> SAME_AS(rep&); } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ %= rhs; - return *this; - } - - template - constexpr quantity& operator%=(const quantity& q) - requires (!treat_as_floating_point) && - requires(T v1, T v2) { { v1 %= v2 } -> SAME_AS(T&); } - // requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ %= q.count(); - return *this; - } - - template - friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) - { - return os << q.count() << " " << detail::unit_text(); - } - }; - - template - [[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) - requires same_dim && detail::basic_arithmetic + template + [[nodiscard]] constexpr quantity operator-() const + requires std::regular_invocable + // requires std::regular_invocable // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(lhs.count() + rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() + ret(rhs).count()); + return quantity(-count()); } - template - [[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic + template + constexpr quantity& operator++() + requires requires(T v) { { ++v } -> SAME_AS(T&); } + // requires requires(rep v) { { ++v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(lhs.count() - rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() - ret(rhs).count()); + ++value_; + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) - requires std::magma + template + requires requires(T v) { { v++ } -> SAME_AS(T); } + constexpr quantity operator++(int) + // requires requires(rep v) { { v++ } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(q.count() * v); - using ret = quantity; - return ret(q.count() * v); + return quantity(value_++); } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) - requires std::magma + template + requires requires(T v) { { --v } -> SAME_AS(T&); } + constexpr quantity& operator--() + // requires requires(rep v) { { --v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - return q * v; + --value_; + return *this; } - template - [[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) - requires same_dim> && - detail::basic_arithmetic + template + requires requires(T v) { { v-- } -> SAME_AS(T); } + constexpr quantity operator--(int) + // requires requires(rep v) { { v-- } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(lhs.count() * rhs.count()); - using ratio = ratio_multiply; - return common_rep(lhs.count()) * common_rep(rhs.count()) * common_rep(ratio::num) / common_rep(ratio::den); + return quantity(value_--); } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) - requires (!same_dim>) && - (treat_as_floating_point || - (std::ratio_multiply::den == 1)) && - detail::basic_arithmetic + template + requires requires(T v1, T v2) { { v1 += v2 } -> SAME_AS(T&); } + constexpr quantity& operator+=(const quantity& q) + // requires requires(rep v1, rep v2) { { v1 += v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using dim = dimension_multiply; - using common_rep = decltype(lhs.count() * rhs.count()); - using ret = quantity>>, common_rep>; - return ret(lhs.count() * rhs.count()); + value_ += q.count(); + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) - requires std::magma + template + requires requires(T v1, T v2) { { v1 -= v2 } -> SAME_AS(T&); } + constexpr quantity& operator-=(const quantity& q) + // requires requires(rep v1, rep v2) { { v1 -= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(q != std::remove_cvref_t(0)); - - using dim = dim_invert; - using common_rep = decltype(v / q.count()); - using ret = quantity>>, common_rep>; - return ret(v / q.count()); + value_ -= q.count(); + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& q, const Value& v) - requires std::magma + template + requires requires(T v1, T v2) { { v1 *= v2 } -> SAME_AS(T&); } + constexpr quantity& operator*=(const rep& rhs) + // requires requires(rep v1, rep v2) { { v1 *= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(v != Value{0}); - - using common_rep = decltype(q.count() / v); - using ret = quantity; - return ret(q.count() / v); + value_ *= rhs; + return *this; } - template - [[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic + template + requires requires(T v1, T v2) { { v1 /= v2 } -> SAME_AS(T&); } + constexpr quantity& operator/=(const rep& rhs) + // requires requires(rep v1, rep v2) { { v1 /= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(rhs != std::remove_cvref_t(0)); - - using common_rep = decltype(lhs.count() / rhs.count()); - using cq = common_quantity, quantity, common_rep>; - return cq(lhs).count() / cq(rhs).count(); + value_ /= rhs; + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) - requires (!same_dim) && - (treat_as_floating_point || - (ratio_divide::den == 1)) && - detail::basic_arithmetic + template + constexpr quantity& operator%=(const Value& rhs) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + requires(T v1, Value v2) { { v1 %= v2 } -> SAME_AS(T&); } + // requires(rep v1, Value v2) { { v1 %= v2 } -> SAME_AS(rep&); } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(rhs != std::remove_cvref_t(0)); - - using common_rep = decltype(lhs.count() / rhs.count()); - using dim = dimension_divide; - using ret = quantity>>, common_rep>; - return ret(lhs.count() / rhs.count()); + value_ %= rhs; + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) - requires (!treat_as_floating_point) && (!treat_as_floating_point) && - std::magma + template + constexpr quantity& operator%=(const quantity& q) + requires (!treat_as_floating_point) && + requires(T v1, T v2) { { v1 %= v2 } -> SAME_AS(T&); } + // requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(q.count() % v); - using ret = quantity; - return ret(q.count() % v); + value_ %= q.count(); + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) - requires (!treat_as_floating_point) && (!treat_as_floating_point) && - std::magma +#if __GNUC__ >= 10 + + template + [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with { - using common_rep = decltype(lhs.count() % rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() % ret(rhs).count()); + using cq = common_quantity>; + return cq(lhs).count() <=> cq(rhs).count(); } - template - [[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::equality_comparable_with + // TODO op== not needed (gcc bug) + template + [[nodiscard]] friend constexpr auto operator==(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::equality_comparable_with { - using cq = common_quantity, quantity>; + using cq = common_quantity>; return cq(lhs).count() == cq(rhs).count(); } - template - [[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::equality_comparable_with +#else + + template + [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::equality_comparable_with + { + using cq = common_quantity>; + return cq(lhs).count() == cq(rhs).count(); + } + + template + [[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::equality_comparable_with { return !(lhs == rhs); } - template - [[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with + template + [[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with { - using cq = common_quantity, quantity>; + using cq = common_quantity>; return cq(lhs).count() < cq(rhs).count(); } - template - [[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with + template + [[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with { return !(rhs < lhs); } - template - [[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with + template + [[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with { return rhs < lhs; } - template - [[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with + template + [[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with { return !(lhs < rhs); } +#endif + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) + { + return os << q.count() << " " << detail::unit_text(); + } +}; + +template +[[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic +{ + using common_rep = decltype(lhs.count() + rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() + ret(rhs).count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic +{ + using common_rep = decltype(lhs.count() - rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() - ret(rhs).count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) + requires std::magma +{ + using common_rep = decltype(q.count() * v); + using ret = quantity; + return ret(q.count() * v); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) + requires std::magma +{ + return q * v; +} + +template +[[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && equivalent_dim> +{ + using common_rep = decltype(lhs.count() * rhs.count()); + using ratio = ratio_multiply; + return common_rep(lhs.count()) * common_rep(rhs.count()) * common_rep(ratio::num) / common_rep(ratio::den); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && (!equivalent_dim>) // TODO equivalent_derived_dim? +{ + using dim = dimension_multiply; + using ratio1 = ratio_divide::ratio>; + using ratio2 = ratio_divide::ratio>; + using ratio = ratio_multiply, typename dimension_unit::ratio>; + using unit = downcast_unit; + using common_rep = decltype(lhs.count() * rhs.count()); + using ret = quantity; + return ret(lhs.count() * rhs.count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) + requires std::magma +{ + Expects(q.count() != 0); + + using dim = dim_invert; + using ratio = ratio; + using unit = downcast_unit; + using common_rep = decltype(v / q.count()); + using ret = quantity; + return ret(v / q.count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& q, const Value& v) + requires std::magma +{ + Expects(v != Value{0}); + + using common_rep = decltype(q.count() / v); + using ret = quantity; + return ret(q.count() / v); +} + +template +[[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && equivalent_dim +{ + Expects(rhs.count() != 0); + + using common_rep = decltype(lhs.count() / rhs.count()); + using cq = common_quantity, quantity, common_rep>; + return cq(lhs).count() / cq(rhs).count(); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && (!equivalent_dim) // TODO equivalent_derived_dim? +{ + Expects(rhs.count() != 0); + + using common_rep = decltype(lhs.count() / rhs.count()); + using dim = dimension_divide; + using ratio1 = ratio_divide::ratio>; + using ratio2 = ratio_divide::ratio>; + using ratio = ratio_multiply, typename dimension_unit::ratio>; + using unit = downcast_unit; + using ret = quantity; + return ret(lhs.count() / rhs.count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma +{ + using common_rep = decltype(q.count() % v); + using ret = quantity; + return ret(q.count() % v); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma +{ + using common_rep = decltype(lhs.count() % rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() % ret(rhs).count()); +} + +namespace detail { + +template +inline constexpr bool is_quantity> = true; + +} // namespace detail + } // namespace units diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h new file mode 100644 index 00000000..4906995b --- /dev/null +++ b/src/include/units/quantity_cast.h @@ -0,0 +1,188 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include + +namespace units { + +// QuantityOf +template +concept QuantityOf = Quantity && Dimension && equivalent_dim; + +// quantity_cast +namespace detail { + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + if constexpr (treat_as_floating_point) { + return To(static_cast(static_cast(q.count()) * + (static_cast(CRatio::num) / static_cast(CRatio::den)))); + } else { + return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) / + static_cast(CRatio::den))); + } + } +}; + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + return To(static_cast(q.count())); + } +}; + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + if constexpr (treat_as_floating_point) { + return To(static_cast(static_cast(q.count()) * (CRep{1} / static_cast(CRatio::den)))); + } else { + return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); + } + } +}; + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); + } +}; + +template +struct cast_ratio; + +template +struct cast_ratio { + using type = ratio_divide; +}; + +template + requires same_unit_reference::value +struct cast_ratio { + using type = ratio_divide; +}; + +template +struct cast_ratio { + using from_ratio = ratio_multiply; + using to_ratio = ratio_multiply; + using type = ratio_divide; +}; + +} // namespace detail + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets the target quantity type to cast to. For example: + * + * auto q1 = units::quantity_cast>(1ms); + * + * @tparam To a target quantity type to cast to + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires QuantityOf && + detail::basic_arithmetic> +{ + using c_ratio = detail::cast_ratio::type; + using c_rep = std::common_type_t; + using ret_unit = downcast_unit; + using ret = quantity; + using cast = detail::quantity_cast_impl; + return cast::cast(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only the target dimension to cast to. For example: + * + * auto q1 = units::quantity_cast(200Gal); + * + * @tparam ToD a dimension type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires equivalent_dim +{ + return quantity_cast, Rep>>(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only the target unit to cast to. For example: + * + * auto q1 = units::quantity_cast(1ms); + * + * @tparam ToU a unit type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires UnitOf +{ + return quantity_cast>(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only representation to cast to. For example: + * + * auto q1 = units::quantity_cast(1ms); + * + * @tparam ToRep a representation type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires detail::basic_arithmetic> +{ + return quantity_cast>(q); +} + +} // namespace units diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h index 1092ca35..6ad15cef 100644 --- a/src/include/units/ratio.h +++ b/src/include/units/ratio.h @@ -22,178 +22,180 @@ #pragma once -#include -#include -#include +#include +#include #include +#include +#include namespace units { - namespace detail { +namespace detail { - template - [[nodiscard]] constexpr T abs(T v) noexcept { return v < 0 ? -v : v; } +template +[[nodiscard]] constexpr T abs(T v) noexcept +{ + return v < 0 ? -v : v; +} - } +} // namespace detail - template - requires (Den != 0) - struct ratio { - static_assert(-INTMAX_MAX <= Num, "numerator too negative"); - static_assert(-INTMAX_MAX <= Den, "denominator too negative"); +template + requires(Den != 0) +struct ratio { + static_assert(-INTMAX_MAX <= Num, "numerator too negative"); + static_assert(-INTMAX_MAX <= Den, "denominator too negative"); - static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); - static constexpr std::intmax_t den = detail::abs(Den) / std::gcd(Num, Den); + static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); + static constexpr std::intmax_t den = detail::abs(Den) / std::gcd(Num, Den); - using type = ratio; - }; + using type = ratio; +}; - // is_ratio +namespace detail { - namespace detail { +template +inline constexpr bool is_ratio> = true; - template - inline constexpr bool is_ratio = false; - - template - inline constexpr bool is_ratio> = true; - - } // namespace detail - - template - concept Ratio = detail::is_ratio; - - // ratio_multiply - - namespace detail { - - static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) - { - constexpr std::uintmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); - - const std::uintmax_t a0 = detail::abs(lhs) % c; - const std::uintmax_t a1 = detail::abs(lhs) / c; - const std::uintmax_t b0 = detail::abs(rhs) % c; - const std::uintmax_t b1 = detail::abs(rhs) / c; - - Expects(a1 == 0 || b1 == 0); // overflow in multiplication - Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication - Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication - Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication - - return lhs * rhs; - } - - template - struct ratio_multiply_impl { - private: - static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); - static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); - - public: - using type = ratio; - static constexpr std::intmax_t num = type::num; - static constexpr std::intmax_t den = type::den; - }; - - } - - template - using ratio_multiply = detail::ratio_multiply_impl::type; - - // ratio_divide - - namespace detail { - - template - struct ratio_divide_impl { - static_assert(R2::num != 0, "division by 0"); - using type = ratio_multiply>; - static constexpr std::intmax_t num = type::num; - static constexpr std::intmax_t den = type::den; - }; - - } - - template - using ratio_divide = detail::ratio_divide_impl::type; - - // ratio_pow - - namespace detail { - - template - struct ratio_pow_impl { - using type = ratio_multiply::type, R>; - }; - - template - struct ratio_pow_impl { - using type = R; - }; - - template - struct ratio_pow_impl { - using type = ratio<1>; - }; - - } - - template - using ratio_pow = detail::ratio_pow_impl::type; - - // ratio_sqrt - - namespace detail { - - constexpr std::intmax_t sqrt_impl(std::intmax_t v, std::intmax_t l, std::intmax_t r) - { - if(l == r) - return r; - - const auto mid = (r + l) / 2; - if(mid * mid >= v) - return sqrt_impl(v, l, mid); - else - return sqrt_impl(v, mid + 1, r); - } - - static constexpr std::intmax_t sqrt_impl(std::intmax_t v) - { - return sqrt_impl(v, 1, v); - } - - template - struct ratio_sqrt_impl { - using type = ratio; - }; - - template - struct ratio_sqrt_impl> { - using type = ratio<0>; - }; - - } - - template - using ratio_sqrt = detail::ratio_sqrt_impl::type; +} // namespace detail - // common_ratio +// ratio_add +// TODO implement ratio_add +// template +// using ratio_add = detail::ratio_add_impl::type; - namespace detail { +// ratio_subtract +// TODO implement ratio_subtract +// template +// using ratio_subtract = detail::ratio_subtract_impl::type; - // TODO: simplified - template - struct common_ratio_impl { - static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); - static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); - using type = ratio; - }; +// ratio_multiply - } +namespace detail { - template - using common_ratio = detail::common_ratio_impl::type; +static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) +{ + constexpr std::uintmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); + + const std::uintmax_t a0 = detail::abs(lhs) % c; + const std::uintmax_t a1 = detail::abs(lhs) / c; + const std::uintmax_t b0 = detail::abs(rhs) % c; + const std::uintmax_t b1 = detail::abs(rhs) / c; + + Expects(a1 == 0 || b1 == 0); // overflow in multiplication + Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication + Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication + Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication + + return lhs * rhs; +} + +template +struct ratio_multiply_impl { +private: + static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); + static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); + +public: + using type = ratio; + static constexpr std::intmax_t num = type::num; + static constexpr std::intmax_t den = type::den; +}; + +} // namespace detail + +template +using ratio_multiply = detail::ratio_multiply_impl::type; + +// ratio_divide + +namespace detail { + +template +struct ratio_divide_impl { + static_assert(R2::num != 0, "division by 0"); + using type = ratio_multiply>; + static constexpr std::intmax_t num = type::num; + static constexpr std::intmax_t den = type::den; +}; + +} // namespace detail + +template +using ratio_divide = detail::ratio_divide_impl::type; + +// ratio_pow + +namespace detail { + +template +struct ratio_pow_impl { + using type = ratio_multiply::type, R>; +}; + +template +struct ratio_pow_impl { + using type = R; +}; + +template +struct ratio_pow_impl { + using type = ratio<1>; +}; + +} // namespace detail + +template +using ratio_pow = detail::ratio_pow_impl::type; + +// ratio_sqrt + +namespace detail { + +constexpr std::intmax_t sqrt_impl(std::intmax_t v, std::intmax_t l, std::intmax_t r) +{ + if (l == r) return r; + + const auto mid = (r + l) / 2; + if (mid * mid >= v) + return sqrt_impl(v, l, mid); + else + return sqrt_impl(v, mid + 1, r); +} + +static constexpr std::intmax_t sqrt_impl(std::intmax_t v) { return sqrt_impl(v, 1, v); } + +template +struct ratio_sqrt_impl { + using type = ratio; +}; + +template +struct ratio_sqrt_impl> { + using type = ratio<0>; +}; + +} // namespace detail + +template +using ratio_sqrt = detail::ratio_sqrt_impl::type; + +// common_ratio + +namespace detail { + +// TODO: simplified +template +struct common_ratio_impl { + static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); + static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); + using type = ratio; +}; + +} // namespace detail + +template +using common_ratio = detail::common_ratio_impl::type; } // namespace units diff --git a/src/include/units/unit.h b/src/include/units/unit.h index ec7b3563..7ce63081 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -22,353 +22,157 @@ #pragma once -#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include namespace units { - template - requires (R::num * R::den > 0) - struct unit : downcast_base> { - using dimension = D; - using ratio = R; - }; +/** + * @brief A common point for a hierarchy of units + * + * A unit is an entity defined and adopted by convention, with which any other quantity of + * the same kind can be compared to express the ratio of the second quantity to the first + * one as a number. + * + * All units of the same dimension can be convereted between each other. To allow this all of + * them are expressed as different ratios of the same one proprietary chosen reference unit + * (i.e. all length units are expressed in terms of meter, all mass units are expressed in + * terms of gram, ...) + * + * @tparam U a unit to use as a reference for this dimension + * @tparam R a ratio of a reference unit + */ +template +struct scaled_unit : downcast_base> { + using ratio = R; + using reference = U; +}; - // is_unit +template +using downcast_unit = downcast::reference>>; - namespace detail { +template +struct same_unit_reference : std::is_same {}; - template - inline constexpr bool is_unit = false; +/** + * @brief An unnamed unit + * + * Defines a new unnamed (in most cases coherent) derived unit of a specific derived dimension + * and it should be passed in this dimension's definition. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + */ +template +struct unit : downcast_child, Child>> { + static constexpr bool is_named = false; + using prefix_type = no_prefix; +}; - template - inline constexpr bool is_unit> = true; +/** + * @brief Unknown unit + * + * Used as a coherent unit of an unknown dimension. + */ +struct unknown_unit : unit {}; - } +/** + * @brief A named unit + * + * Defines a named (in most cases coherent) unit that is then passed to a dimension definition. + * A named unit may be used by other units defined with the prefix of the same type, unless + * no_prefix is provided for PT template parameter (in such a case it is impossible to define + * a prefix unit based on this one). + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam Symbol a short text representation of the unit + * @tparam PT no_prefix or a type of prefix family + */ +template +struct named_unit : downcast_child, Child>> { + static constexpr bool is_named = true; + static constexpr auto symbol = Symbol; + using prefix_type = PT; +}; - template - concept Unit = - std::is_empty_v && - detail::is_unit>; +/** + * @brief A scaled unit + * + * Defines a new named unit that is a scaled version of another unit. Such unit can be used by + * other units defined with the prefix of the same type, unless no_prefix is provided for PT + * template parameter (in such a case it is impossible to define a prefix unit based on this + * one). + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam Symbol a short text representation of the unit + * @tparam PT no_prefix or a type of prefix family + * @tparam R a scale to apply to U + * @tparam U a reference unit to scale + */ +template +struct named_scaled_unit : downcast_child, typename U::reference>> { + static constexpr bool is_named = true; + static constexpr auto symbol = Symbol; + using prefix_type = PT; +}; - // deduced_derived_unit +/** + * @brief A prefixed unit + * + * Defines a new unit that is a scaled version of another unit by the provided prefix. It is + * only possible to create such a unit if the given prefix type matches the one defined in a + * reference unit. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam P prefix to be appied to the reference unit + * @tparam U reference unit + */ +template + requires std::same_as +// TODO replace with the below code when gcc will stop to crash on it ;-) +// struct prefixed_unit : named_scaled_unit, +// typename U::reference> {}; +struct prefixed_unit : + downcast_child, typename U::reference>> { + static constexpr bool is_named = true; + static constexpr auto symbol = P::symbol + U::symbol; + using prefix_type = P::prefix_type; +}; - namespace detail { +/** + * @brief A unit with a deduced ratio and symbol + * + * Defines a new unit with a deduced ratio and symbol based on the recipe from the provided + * derived dimension. The number and order of provided units should match the recipe of the + * derived dimension. All of the units provided should also be a named ones so it is possible + * to create a deduced symbol text. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam Dim a derived dimension recipe to use for deduction + * @tparam U the unit of the first composite dimension from provided derived dimension's recipe + * @tparam URest the units for the rest of dimensions from the recipe + */ +template + requires detail::same_scaled_units && + (U::is_named && (URest::is_named && ... && true)) +struct deduced_unit : downcast_child> { + static constexpr bool is_named = false; + static constexpr auto symbol = detail::deduced_symbol_text(); + using prefix_type = no_prefix; +}; - template - struct get_unit_base_dim; - - template - struct get_unit_base_dim> { - static_assert(sizeof...(Rest) == 0, "Base unit expected"); - using dimension = E::dimension; - }; - - template - struct ratio_op; - - template - struct ratio_op { - using ratio = Result; - }; - - template - struct ratio_op { - using calc_ratio = conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply, - ratio_divide>; - static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen); - using ratio = ratio_op::ratio; - }; - - template - struct derived_ratio; - - template - struct derived_ratio, Us...> { - using ratio = ::units::ratio<1>; - }; - - template - struct derived_ratio, U, URest...> { - static_assert(same_dim, "The order and number of units in `deduced_derived_unit` should match dimensions provided in a `derived_dimension<>`"); - static_assert(sizeof...(ERest) == sizeof...(URest), "The number of `deduced_derived_unit` units should match the number of exponents provided to `derived_dimension<>`"); - using rest_ratio = derived_ratio, URest...>::ratio; - using ratio = ratio_op::ratio; - }; - - template - constexpr auto exp_count(dimension) - { - return sizeof...(Es); - } - - template - inline constexpr bool is_unit_of_base_dimension = (exp_count(typename U::dimension::base_type()) == 1); - - template - inline constexpr bool are_units_of_base_dimension = (is_unit_of_base_dimension && ...); - - template - using deduced_derived_unit = - unit, - typename D::base_type, typename D::recipe>, Us...>::ratio>; - - template - requires (0 <= Value) && (Value < 10) - inline constexpr basic_fixed_string superscript_number = "\u2070"; - -// template<> inline constexpr basic_fixed_string superscript_number<0> = "\u2070"; - template<> inline constexpr basic_fixed_string superscript_number<1> = "\u00b9"; - template<> inline constexpr basic_fixed_string superscript_number<2> = "\u00b2"; - template<> inline constexpr basic_fixed_string superscript_number<3> = "\u00b3"; - template<> inline constexpr basic_fixed_string superscript_number<4> = "\u2074"; - template<> inline constexpr basic_fixed_string superscript_number<5> = "\u2075"; - template<> inline constexpr basic_fixed_string superscript_number<6> = "\u2076"; - template<> inline constexpr basic_fixed_string superscript_number<7> = "\u2077"; - template<> inline constexpr basic_fixed_string superscript_number<8> = "\u2078"; - template<> inline constexpr basic_fixed_string superscript_number<9> = "\u2079"; - - template - requires (Value >= 0) - constexpr auto superscript() - { - if constexpr(Value < 10) - return superscript_number; - else - return superscript() + superscript(); - } - - template - requires (Value >= 0) - constexpr auto regular() - { - if constexpr(Value < 10) - return basic_fixed_string(static_cast('0' + Value)); - else - return regular() + regular(); - } - - - template - constexpr auto ratio_text() - { - if constexpr(Ratio::num != 1 || Ratio::den != 1) { - auto txt = basic_fixed_string("[") + regular(); - if constexpr(Ratio::den == 1) { - return txt + basic_fixed_string("]"); - } - else { - return txt + basic_fixed_string("/") + regular() + basic_fixed_string("]"); - } - } - else { - return basic_fixed_string(""); - } - } - - template - constexpr auto prefix_or_ratio_text() - { - if constexpr(Ratio::num != 1 || Ratio::den != 1) { - if constexpr (!std::same_as) { - using prefix = downcast>; - - if constexpr(!std::same_as>) { - // print as a prefixed unit - return prefix::symbol; - } - else { - // print as a ratio of the coherent unit - return ratio_text(); - } - } - else { - // print as a ratio of the coherent unit - return ratio_text(); - } - } - } - - - template - constexpr auto operator_text() - { - if constexpr(Idx == 0) { - if constexpr(Divide) { - return basic_fixed_string("1/"); - } - else { - return basic_fixed_string(""); - } - } - else { - if constexpr(Divide) { - return basic_fixed_string("/"); - } - else { - return basic_fixed_string("⋅"); - } - } - } - - template - constexpr auto exp_text() - { - // get calculation operator + symbol - const auto txt = operator_text() + Symbol; - if constexpr(E::den != 1) { - // add root part - return txt + basic_fixed_string("^(") + regular() + basic_fixed_string("/") + regular() + basic_fixed_string(")"); - } - else if constexpr(abs(E::num) != 1) { - // add exponent part - return txt + superscript(); - } - else { - return txt; - } - } - - template - constexpr auto dimension_symbol() - { - if constexpr(BaseDimension) - return Dim::symbol; - else - // coherent derived unit - return downcast>>::symbol; - } - - template - constexpr auto base_symbol_text_impl(dimension, std::index_sequence) - { - return (exp_text(), Idxs>() + ...); - } - - template - constexpr auto base_symbol_text(dimension d) - { - return base_symbol_text_impl(d, std::index_sequence_for()); - } - - template - constexpr bool all_named(dimension) - { - return (downcast>>::is_named && ...); - } - - template - constexpr auto base_symbol_text() - { - using recipe = typename Dim::recipe; - if constexpr(all_named(recipe())) - return base_symbol_text(recipe()); - else - return base_symbol_text(Dim()); - } - - template - constexpr auto exp_validate_and_text() - { - static_assert(same_dim, "The order and number of units in `deduced_derived_unit` should match dimensions provided in a `derived_dimension<>`"); - return exp_text(); - } - - template - constexpr auto deduced_symbol_text_impl(dimension, std::index_sequence) - { - return (exp_validate_and_text() + ...); - } - - template - constexpr auto deduced_symbol_text(dimension d) - { - static_assert(sizeof...(Es) == sizeof...(Us), "The number of `deduced_derived_unit` units should match the number of exponents provided to `derived_dimension<>`"); - return deduced_symbol_text_impl(d, std::index_sequence_for()); - } - - template - constexpr auto deduced_symbol_text() - { - if constexpr(are_units_of_base_dimension) - return deduced_symbol_text(typename Dim::base_type()); - else - return deduced_symbol_text(typename Dim::recipe()); - } - - template - constexpr auto unit_text() - { - if constexpr(!is_unit) { - // Unit is a downcasted derived unit child class already so just print defined symbol immediately - return Unit::symbol; - } - else { - // we are dealing with a non-user-defined unit here - using ratio = Unit::ratio; - using dim = Unit::dimension; - if constexpr(!is_dimension) { - // downcasted user-defined dimension - // print as a prefix or ratio of a coherent unit symbol defined by the user - using coherent_unit = downcast>>; - return prefix_or_ratio_text() + coherent_unit::symbol; - } - else { - // print as a ratio of a coherent unit + coherent unit dimensions and their exponents - return ratio_text() + base_symbol_text(dim{}); - } - } - } - - } // namespace detail - - - // derived_unit - - template - struct named_coherent_derived_unit : downcast_child>> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; - }; - - template - struct coherent_derived_unit : downcast_child>> { - static constexpr bool is_named = false; - static constexpr auto symbol = detail::base_symbol_text(); - using prefix_type = no_prefix; - }; - - template - struct named_scaled_derived_unit : downcast_child> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; - }; - - template - struct named_deduced_derived_unit : downcast_child> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; - }; - - template - requires U::is_named && (Us::is_named && ... && true) - struct deduced_derived_unit : downcast_child> { - static constexpr bool is_named = false; - static constexpr auto symbol = detail::deduced_symbol_text(); - using prefix_type = no_prefix; - }; - - template - requires (!std::same_as) - struct prefixed_derived_unit : downcast_child>> { - static constexpr bool is_named = true; - static constexpr auto symbol = P::symbol + U::symbol; - using prefix_type = P::prefix_type; - }; +// template +// struct named_deduced_derived_unit : downcast_child> { +// static constexpr bool is_named = true; +// static constexpr auto symbol = Symbol; +// using prefix_type = PT; +// }; } // namespace units diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a68376b5..67a1d088 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,4 +22,4 @@ add_subdirectory(unit_test/runtime) add_subdirectory(unit_test/static) -add_subdirectory(metabench) +#add_subdirectory(metabench) diff --git a/test/metabench/make_dimension/dimension_concepts_all.h b/test/metabench/make_dimension/dimension_concepts_all.h index f3293ccd..755da1e1 100644 --- a/test/metabench/make_dimension/dimension_concepts_all.h +++ b/test/metabench/make_dimension/dimension_concepts_all.h @@ -131,7 +131,7 @@ namespace units { struct dim_invert> : std::type_identity...>>> {}; template - using dim_invert_t = dim_invert::type; + using dim_invert_t = dim_invert::type; // make_dimension @@ -196,7 +196,7 @@ namespace units { struct dimension_multiply, dimension> : std::type_identity, dimension>>> {}; template - using dimension_multiply_t = dimension_multiply::type; + using dimension_multiply_t = dimension_multiply::type; // dimension_divide @@ -209,6 +209,6 @@ namespace units { }; template - using dimension_divide_t = dimension_divide::type; + using dimension_divide_t = dimension_divide::type; } // namespace units diff --git a/test/metabench/make_dimension/dimension_concepts_iface.h b/test/metabench/make_dimension/dimension_concepts_iface.h index d262e562..ca104c0d 100644 --- a/test/metabench/make_dimension/dimension_concepts_iface.h +++ b/test/metabench/make_dimension/dimension_concepts_iface.h @@ -131,7 +131,7 @@ namespace units { struct dim_invert> : std::type_identity...>>> {}; template - using dim_invert_t = dim_invert::type; + using dim_invert_t = dim_invert::type; // make_dimension @@ -196,7 +196,7 @@ namespace units { struct dimension_multiply, dimension> : std::type_identity, dimension>>> {}; template - using dimension_multiply_t = dimension_multiply::type; + using dimension_multiply_t = dimension_multiply::type; // dimension_divide @@ -209,6 +209,6 @@ namespace units { }; template - using dimension_divide_t = dimension_divide::type; + using dimension_divide_t = dimension_divide::type; } // namespace units diff --git a/test/metabench/make_dimension/dimension_no_concepts.h b/test/metabench/make_dimension/dimension_no_concepts.h index 188f92f8..ac8f8688 100644 --- a/test/metabench/make_dimension/dimension_no_concepts.h +++ b/test/metabench/make_dimension/dimension_no_concepts.h @@ -102,7 +102,7 @@ namespace units { struct dim_invert> : std::type_identity...>>> {}; template - using dim_invert_t = dim_invert::type; + using dim_invert_t = dim_invert::type; // make_dimension @@ -167,7 +167,7 @@ namespace units { struct dimension_multiply, dimension> : std::type_identity, dimension>>> {}; template - using dimension_multiply_t = dimension_multiply::type; + using dimension_multiply_t = dimension_multiply::type; // dimension_divide @@ -180,6 +180,6 @@ namespace units { }; template - using dimension_divide_t = dimension_divide::type; + using dimension_divide_t = dimension_divide::type; } // namespace units diff --git a/test/metabench/make_dimension/downcasting_concepts_all.h b/test/metabench/make_dimension/downcasting_concepts_all.h index db0c3b3b..68bd5230 100644 --- a/test/metabench/make_dimension/downcasting_concepts_all.h +++ b/test/metabench/make_dimension/downcasting_concepts_all.h @@ -29,18 +29,18 @@ namespace units { template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; }; template concept Downcastable = requires { - typename T::base_type; + typename T::downcast_base_type; } && - std::derived_from>; + std::derived_from>; template - using downcast_base_t = T::base_type; + using downcast_base_t = T::downcast_base_type; template struct downcast_traits : std::type_identity {}; diff --git a/test/metabench/make_dimension/downcasting_no_concepts.h b/test/metabench/make_dimension/downcasting_no_concepts.h index 519c469d..051b0ff5 100644 --- a/test/metabench/make_dimension/downcasting_no_concepts.h +++ b/test/metabench/make_dimension/downcasting_no_concepts.h @@ -29,11 +29,11 @@ namespace units { template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; }; template - using downcast_base_t = T::base_type; + using downcast_base_t = T::downcast_base_type; template struct downcast_traits : std::type_identity {}; diff --git a/test/unit_test/runtime/digital_info_test.cpp b/test/unit_test/runtime/digital_info_test.cpp index 600c17e6..0b032a09 100644 --- a/test/unit_test/runtime/digital_info_test.cpp +++ b/test/unit_test/runtime/digital_info_test.cpp @@ -20,43 +20,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include #include -namespace data { +using namespace units::data; - struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; - - struct digital_information : units::derived_dimension> {}; - - template - concept DigitalInformation = units::QuantityOf; - - struct data_prefix : units::prefix_type {}; - - struct kibi : units::prefix, "Ki"> {}; - struct mebi : units::prefix, "Mi"> {}; - - struct bit : units::named_coherent_derived_unit {}; - struct kilobit : units::prefixed_derived_unit {}; - struct byte : units::named_scaled_derived_unit, data_prefix> {}; - struct kilobyte : units::prefixed_derived_unit {}; - - inline namespace literals { - - constexpr auto operator""_b(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_Kib(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_B(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_KiB(unsigned long long l) { return units::quantity(l); } - - } - -} - -using namespace data; - -TEST_CASE("operator<< on a custom quantity", "[text][ostream]") +TEST_CASE("operator<< on a data quantity", "[text][ostream]") { std::stringstream stream; @@ -64,25 +34,25 @@ TEST_CASE("operator<< on a custom quantity", "[text][ostream]") { SECTION("named unit") { - stream << 64_B; + stream << 64B; REQUIRE(stream.str() == "64 B"); } SECTION("prefixed coherent unit") { - stream << 256_Kib; + stream << 256Kib; REQUIRE(stream.str() == "256 Kib"); } SECTION("prefixed non-coherent unit") { - stream << 1024_KiB; + stream << 1024KiB; REQUIRE(stream.str() == "1024 KiB"); } SECTION("other unit matching prefix") { - stream << 8_Kib * 8_Kib / 2_b; + stream << 8Kib * 8Kib / 2b; REQUIRE(stream.str() == "32 Mib"); } } diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index a214bb6f..c4c3aa03 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -20,18 +20,20 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/area.h" -#include "units/dimensions/frequency.h" -#include "units/dimensions/power.h" -#include "units/dimensions/velocity.h" -#include "units/dimensions/volume.h" -#include "units/dimensions/surface_tension.h" +#include "units/physical/si/area.h" +#include "units/physical/si/frequency.h" +#include "units/physical/si/power.h" +#include "units/physical/si/pressure.h" +#include "units/physical/si/velocity.h" +#include "units/physical/si/volume.h" +#include "units/physical/si/surface_tension.h" #include "units/format.h" #include "units/math.h" #include #include using namespace units; +using namespace units::si; using namespace Catch::Matchers; TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") @@ -108,7 +110,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const quantity>> q(123); + const length, metre>> q(123); stream << q; SECTION("iostream") @@ -129,7 +131,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const quantity>> q(60); + const energy, joule>> q(60); stream << q; SECTION("iostream") @@ -242,8 +244,8 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("surface tension") { - struct newton_per_centimetre : deduced_derived_unit {}; - const quantity q(123); + struct newton_per_centimetre : deduced_unit {}; + const surface_tension q(123); stream << q; SECTION("iostream") @@ -636,17 +638,17 @@ TEST_CASE("format string with only %Q should print quantity value only", "[text] SECTION("nan") { - CHECK(fmt::format("{:%Q}", quantity(std::numeric_limits::quiet_NaN())) == "nan"); + CHECK(fmt::format("{:%Q}", length(std::numeric_limits::quiet_NaN())) == "nan"); } SECTION("inf") { - CHECK(fmt::format("{:%Q}", quantity(std::numeric_limits::infinity())) == "inf"); + CHECK(fmt::format("{:%Q}", length(std::numeric_limits::infinity())) == "inf"); } SECTION("-inf") { - CHECK(fmt::format("{:%Q}", quantity(-std::numeric_limits::infinity())) == "-inf"); + CHECK(fmt::format("{:%Q}", length(-std::numeric_limits::infinity())) == "-inf"); } } } @@ -742,8 +744,8 @@ TEST_CASE("fill and align specification", "[text][fmt]") TEST_CASE("sign specification", "[text][fmt]") { - quantity inf(std::numeric_limits::infinity()); - quantity nan(std::numeric_limits::quiet_NaN()); + length inf(std::numeric_limits::infinity()); + length nan(std::numeric_limits::quiet_NaN()); SECTION("default format {} on a quantity") { diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 390f8cb7..1e721528 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -20,16 +20,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/area.h" -#include "units/dimensions/frequency.h" -#include "units/dimensions/power.h" -#include "units/dimensions/velocity.h" -#include "units/dimensions/volume.h" -#include "units/dimensions/surface_tension.h" +#include "units/physical/si/area.h" +#include "units/physical/si/frequency.h" +#include "units/physical/si/power.h" +#include "units/physical/si/velocity.h" +#include "units/physical/si/volume.h" +#include "units/physical/si/surface_tension.h" #include "units/format.h" #include -using namespace units; +using namespace units::si; TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") { diff --git a/test/unit_test/runtime/math_test.cpp b/test/unit_test/runtime/math_test.cpp index 4e72e26b..b147e839 100644 --- a/test/unit_test/runtime/math_test.cpp +++ b/test/unit_test/runtime/math_test.cpp @@ -21,73 +21,35 @@ // SOFTWARE. #include "units/math.h" -#include "units/dimensions/area.h" -#include "units/dimensions/volume.h" +#include "units/physical/si/area.h" +#include "units/physical/si/volume.h" #include using namespace units; +using namespace units::si; // classical -TEST_CASE("pow() on quantity changes the value and the dimension accordingly", "[math][pow]") +TEST_CASE("'pow()' on quantity changes the value and the dimension accordingly", "[math][pow]") { - CHECK(pow<0>(2m) == 1); - CHECK(pow<1>(2m) == 2m); - CHECK(pow<2>(2m) == 4sq_m); - CHECK(pow<3>(2m) == 8cub_m); + SECTION ("'pow<0>(q)' returns '1'") { + CHECK(pow<0>(2m) == 1); + } + + SECTION ("'pow<1>(q)' returns 'q'") { + CHECK(pow<1>(2m) == 2m); + } + + SECTION ("'pow<2>(q)' squares both the value and a dimension") { + CHECK(pow<2>(2m) == 4sq_m); + } + + SECTION ("'pow<3>(q)' cubes both the value and a dimension") { + CHECK(pow<3>(2m) == 8cub_m); + } } -TEST_CASE("sqrt() on quantity changes the value and the dimension accordingly", "[math][sqrt]") +TEST_CASE("'sqrt()' on quantity changes the value and the dimension accordingly", "[math][sqrt]") { REQUIRE(sqrt(4sq_m) == 2m); } - -// BDD style - -SCENARIO("quantities should work with pow()", "[math][pow]") -{ - GIVEN("A quantity q") { - auto q = 2m; - - REQUIRE(q.count() == 2); - - WHEN("pow<1>(q) is called") { - auto res = pow<1>(q); - - THEN("the same quantity is received") { - REQUIRE(res == q); - } - } - WHEN("pow<2>(q) is called") { - auto res = pow<2>(q); - - THEN("both the value and dimension is raised to the exponent 2") { - REQUIRE(res == 4sq_m); - } - } - WHEN("pow<3>(q) is called") { - auto res = pow<3>(q); - - THEN("both the value and dimension is raised to the exponent 3") { - REQUIRE(res == 8cub_m); - } - } - } -} - -SCENARIO("quantities should work with sqrt()", "[math][sqrt]") -{ - GIVEN("A quantity q") { - auto q = 4sq_m; - - REQUIRE(q.count() == 4); - - WHEN("sqrt(q) is called") { - auto res = sqrt(q); - - THEN("both the value and dimension are square rooted") { - REQUIRE(res == 2m); - } - } - } -} diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 7aa64210..790ee380 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -23,10 +23,14 @@ add_library(unit_tests_static cgs_test.cpp custom_unit_test.cpp - dimension_test.cpp + data_test.cpp + dimension_op_test.cpp +# fixed_string_test.cpp math_test.cpp quantity_test.cpp ratio_test.cpp + si_test.cpp + si_cgs_test.cpp type_list_test.cpp unit_test.cpp ) diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 341126f2..e58bb4d4 100644 --- a/test/unit_test/static/cgs_test.cpp +++ b/test/unit_test/static/cgs_test.cpp @@ -20,56 +20,84 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include -#include -namespace cgs { - - using units::centimetre; - using units::gram; - using units::second; - struct centimetre_per_second : units::deduced_derived_unit {}; - struct gal : units::named_deduced_derived_unit {}; - struct dyne : units::named_deduced_derived_unit {}; - struct erg : units::named_deduced_derived_unit {}; - struct ergps : units::named_deduced_derived_unit {}; // TODO make it work for erg and non-named - struct barye : units::named_deduced_derived_unit {}; - - - inline namespace literals { - - using namespace units::literals; - - constexpr auto operator""cmps(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""cmps(long double l) { return units::quantity(l); } - constexpr auto operator""Gal(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""Gal(long double l) { return units::quantity(l); } - constexpr auto operator""dyn(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""dyn(long double l) { return units::quantity(l); } - constexpr auto operator""_erg(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_erg(long double l) { return units::quantity(l); } - constexpr auto operator""_ergps(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_ergps(long double l) { return units::quantity(l); } - constexpr auto operator""Ba(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""Ba(long double l) { return units::quantity(l); } - - } // namespace literals - -} +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace { - using namespace cgs::literals; +using namespace units; +using namespace units::cgs; - static_assert(100cm == 1m); - static_assert(1'000g == 1kg); - static_assert(100cmps == 1mps); - static_assert(100Gal == 1mps_sq); - static_assert(100'000dyn == 1N); - static_assert(10'000'000_erg == 1_J); - static_assert(10'000'000_ergps == 1W); - static_assert(10Ba == 1Pa); +/* ************** BASE DIMENSIONS **************** */ -} \ No newline at end of file +// length + +static_assert(centimetre::symbol == "cm"); + +// mass + +// time + +/* ************** DERIVED DIMENSIONS IN TERMS OF BASE UNITS **************** */ + +// velocity + +static_assert(10cm / 5s == 2cmps); +static_assert(10cm / 2cmps == 5s); +static_assert(10cm == 2cmps * 5s); + +static_assert(detail::unit_text() == "cm/s"); + +// area +static_assert(std::is_same_v::ratio>, ratio<1>>); + +static_assert(1cm * 1cm == 1sq_cm); +static_assert(100sq_cm / 10cm == 10cm); + +static_assert(detail::unit_text() == "cm²"); + +/* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ + +// acceleration + +static_assert(10cmps / 10s == 1Gal); +static_assert(10cmps / 1Gal == 10s); +static_assert(1Gal * 10s == 10cmps); + +// force + +static_assert(10g * 10Gal == 100dyn); +static_assert(100dyn / 10g == 10Gal); +static_assert(100dyn / 10Gal == 10g); + +// pressure + +static_assert(10dyn / 10sq_cm == 1Ba); +static_assert(10dyn / 1Ba == 10sq_cm); +static_assert(1Ba * 10sq_cm == 10dyn); + +// energy + +static_assert(10dyn * 10cm == 100_erg); +static_assert(100_erg / 10cm == 10dyn); +static_assert(100_erg / 10dyn == 10cm); + +/* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ + +// power + +static_assert(10_erg / 10s == 1_ergps); +static_assert(1_ergps * 10s == 10_erg); +static_assert(10_erg / 1_ergps == 10s); + +static_assert(detail::unit_text() == "erg/s"); + +} diff --git a/test/unit_test/static/custom_unit_test.cpp b/test/unit_test/static/custom_unit_test.cpp index 2df947e8..54c0c056 100644 --- a/test/unit_test/static/custom_unit_test.cpp +++ b/test/unit_test/static/custom_unit_test.cpp @@ -20,70 +20,37 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include +#include +#include #include -/* ************** DERIVED DIMENSIONS THAT INCLUDE UNITS WITH SPECIAL NAMES **************** */ - namespace { - struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; +using namespace units; +using namespace units::si; - struct digital_information : units::derived_dimension> {}; +// power spectral density +struct sq_volt_per_hertz : unit {}; +struct dim_power_spectral_density : derived_dimension, units::exp> {}; - template - concept DigitalInformation = units::QuantityOf; +template +using power_spectral_density = quantity; - struct data_prefix : units::prefix_type {}; +// amplitude spectral density +struct volt_per_sqrt_hertz : unit {}; +struct dim_amplitude_spectral_density : derived_dimension, units::exp> {}; - struct kibi : units::prefix, "Ki"> {}; - - struct bit : units::named_coherent_derived_unit {}; - struct kilobit : units::prefixed_derived_unit {}; - - struct byte : units::named_scaled_derived_unit, data_prefix> {}; - struct kilobyte : units::prefixed_derived_unit {}; - - inline namespace literals { - - constexpr auto operator""_b(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_Kib(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_B(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_KiB(unsigned long long l) { return units::quantity(l); } - - } -} - -namespace { - - static_assert(1_B == 8_b); - static_assert(1024_b == 1_Kib); - static_assert(1024_B == 1_KiB); - static_assert(8 * 1024_b == 1_KiB); - static_assert(8 * 1_Kib == 1_KiB); +template +using amplitude_spectral_density = quantity; } namespace { - using namespace units; +static_assert(std::is_same_v, dim_amplitude_spectral_density>); +static_assert(std::is_same_v, dim_power_spectral_density>); - // power spectral density - struct power_spectral_density : derived_dimension, units::exp> {}; - struct sq_volt_per_hertz : coherent_derived_unit {}; - - // amplitude spectral density - struct amplitude_spectral_density : derived_dimension, units::exp> {}; - struct volt_per_sqrt_hertz : coherent_derived_unit {}; -} - -namespace { - - static_assert(std::is_same_v, amplitude_spectral_density>); - static_assert(std::is_same_v, power_spectral_density>); - - static_assert(std::is_same_v(quantity(4))), decltype(quantity(16))>); - static_assert(std::is_same_v(16))), decltype(quantity(4))>); +static_assert(std::is_same_v(amplitude_spectral_density(4))), decltype(power_spectral_density(16))>); +static_assert(std::is_same_v(16))), decltype(amplitude_spectral_density(4))>); } diff --git a/test/unit_test/static/data_test.cpp b/test/unit_test/static/data_test.cpp new file mode 100644 index 00000000..14b483b2 --- /dev/null +++ b/test/unit_test/static/data_test.cpp @@ -0,0 +1,48 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#include +#include + +/* ************** DERIVED DIMENSIONS THAT INCLUDE UNITS WITH SPECIAL NAMES **************** */ + +namespace { + +using namespace units::data; + +// information + +static_assert(1B == 8b); +static_assert(1024b == 1Kib); +static_assert(1024B == 1KiB); +static_assert(8 * 1024b == 1KiB); +static_assert(8 * 1Kib == 1KiB); + +static_assert(1Kib == 1024b); +static_assert(1Mib == 1024Kib); +static_assert(1Gib == 1024Mib); +static_assert(1Tib == 1024Gib); +static_assert(1Pib == 1024Tib); + +// bitrate + +} diff --git a/test/unit_test/static/dimension_op_test.cpp b/test/unit_test/static/dimension_op_test.cpp new file mode 100644 index 00000000..4ac7fa61 --- /dev/null +++ b/test/unit_test/static/dimension_op_test.cpp @@ -0,0 +1,119 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#include "units/bits/dimension_op.h" +#include "units/unit.h" +#include + +using namespace units; + +namespace { + +struct u0 : named_unit {}; +struct d0 : base_dimension<"d0", u0> {}; +struct u1 : named_unit {}; +struct d1 : base_dimension<"d1", u1> {}; +struct u2 : named_unit {}; +struct d2 : base_dimension<"d2", u2> {}; +struct u3 : named_unit {}; +struct d3 : base_dimension<"d3", u3> {}; + +// exp_invert + +static_assert(std::is_same_v>, exp>); +static_assert(std::is_same_v>, exp>); + +// dim_unpack + +template +struct typeinfo; + +template +using dim_unpack = detail::dim_unpack::type; + +template +using derived_dim = detail::derived_dimension_base; + +static_assert(std::is_same_v, exp_list<>>); +static_assert(std::is_same_v>, exp_list>>); +static_assert(std::is_same_v, exp>, exp_list, exp>>); +using dim1 = derived_dim>; +using dim2 = derived_dim, exp>; +static_assert(std::is_same_v, exp>, exp_list, exp>>); +static_assert(std::is_same_v, exp, exp>, + exp_list, exp, exp, exp>>); + +// dim_invert +static_assert(std::is_same_v>>, d0>); +static_assert(std::is_same_v>>, unknown_dimension>>); +static_assert( + std::is_same_v, exp>>, unknown_dimension, exp>>); + +// make_dimension + +template +using make_dimension = detail::make_dimension; + +static_assert(std::is_same_v>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim, exp>>); +static_assert(std::is_same_v, exp>, derived_dim, exp>>); +static_assert(std::is_same_v, exp>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim>>); + +static_assert(std::is_same_v, exp, exp, exp>, + derived_dim, exp>>); +static_assert(std::is_same_v, exp, exp, exp>, + derived_dim, exp>>); + +static_assert(std::is_same_v, exp, exp>, derived_dim>>); +static_assert(std::is_same_v, exp, exp>, derived_dim>>); +static_assert(std::is_same_v, exp, exp>, derived_dim>>); + +// dimension_multiply + +static_assert(std::is_same_v>, derived_dim>>, + unknown_dimension, exp>>); +static_assert( + std::is_same_v>, d1>, unknown_dimension, exp>>); +static_assert( + std::is_same_v>>, unknown_dimension, exp>>); +static_assert(std::is_same_v, unknown_dimension, exp>>); +static_assert(std::is_same_v< + dimension_multiply, exp, exp>, derived_dim>>, + unknown_dimension, exp, exp, exp>>); +static_assert(std::is_same_v< + dimension_multiply, exp, exp>, derived_dim>>, + unknown_dimension, exp, exp>>); +static_assert(std::is_same_v< + dimension_multiply, exp, exp>, derived_dim>>, + unknown_dimension, exp>>); +static_assert(std::is_same_v>, derived_dim>>, d0>); + +// dimension_divide + +static_assert(std::is_same_v>, derived_dim>>, + unknown_dimension, exp>>); +static_assert(std::is_same_v>, unknown_dimension>>, d0>); + +} // namespace diff --git a/test/unit_test/static/dimension_test.cpp b/test/unit_test/static/dimension_test.cpp deleted file mode 100644 index c476ce04..00000000 --- a/test/unit_test/static/dimension_test.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. - -#include "units/dimension.h" -#include - -using namespace units; - -namespace { - - struct d0 : base_dimension<"d0", ""> {}; - struct d1 : base_dimension<"d1", ""> {}; - struct d2 : base_dimension<"d2", ""> {}; - struct d3 : base_dimension<"d3", ""> {}; - - // exp_invert - - static_assert(std::is_same_v>, exp>); - static_assert(std::is_same_v>, exp>); - - // extract - - template - struct typeinfo; - - template - using extract = detail::extract::type; - - static_assert(std::is_same_v, dimension<>>); - static_assert(std::is_same_v>, dimension>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - using dim0 = dimension<>; - using dim1 = dimension>; - using dim2 = dimension, exp>; - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension, exp, exp, exp>>); - - // make_dimension - - template - using make_dimension = detail::make_dimension; - - static_assert(std::is_same_v>, dimension>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension<>>); - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension>>); - - static_assert(std::is_same_v, exp, exp, exp>, dimension, exp>>); - static_assert( - std::is_same_v, exp, exp, exp>, dimension, exp>>); - - static_assert(std::is_same_v, exp, exp>, dimension>>); - static_assert(std::is_same_v, exp, exp>, dimension>>); - static_assert(std::is_same_v, exp, exp>, dimension>>); - static_assert(std::is_same_v, exp, exp, exp>, dimension<>>); - - // dimension_multiply - - static_assert( - std::is_same_v>, dimension>>, dimension, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension>>, - dimension, exp, exp, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension>>, - dimension, exp, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension>>, - dimension, exp>>); - - // dimension_divide - - static_assert( - std::is_same_v>, dimension>>, dimension, exp>>); - static_assert(std::is_same_v>, dimension>>, dimension<>>); - -} // namespace diff --git a/test/unit_test/static/math_test.cpp b/test/unit_test/static/math_test.cpp index 09a4759c..752c58b7 100644 --- a/test/unit_test/static/math_test.cpp +++ b/test/unit_test/static/math_test.cpp @@ -20,13 +20,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/area.h" +#include "units/physical/si/area.h" #include "units/math.h" -using namespace units; - namespace { + using namespace units::si::literals; + static_assert(std::is_same_v(2m)), std::int64_t>); static_assert(std::is_same_v(2m)), decltype(2m)>); static_assert(std::is_same_v(2m)), decltype(4sq_m)>); diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index a0d55d13..7dd965f3 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -20,299 +20,331 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/velocity.h" -#include "units/dimensions/frequency.h" -#include "units/dimensions/area.h" #include "units/math.h" -#include +#include "units/physical/si/area.h" +#include "units/physical/si/frequency.h" +#include "units/physical/si/velocity.h" #include +#include using namespace units; namespace { - template - class my_value { - T value_{}; - public: - my_value() = default; - constexpr my_value(T v) : value_(std::move(v)) {} - [[nodiscard]] constexpr my_value operator-() const { return my_value(-value_); } +template +class my_value { + T value_{}; - [[nodiscard]] friend constexpr my_value operator+(my_value lhs, my_value rhs) { return my_value(lhs.value_ + rhs.value_); } - [[nodiscard]] friend constexpr my_value operator-(my_value lhs, my_value rhs) { return my_value(lhs.value_ - rhs.value_); } - [[nodiscard]] friend constexpr my_value operator*(my_value lhs, my_value rhs) { return my_value(lhs.value_ * rhs.value_); } - [[nodiscard]] friend constexpr my_value operator/(my_value lhs, my_value rhs) { return my_value(lhs.value_ / rhs.value_); } +public: + my_value() = default; + constexpr my_value(T v) : value_(std::move(v)) {} - [[nodiscard]] friend constexpr bool operator==(my_value lhs, my_value rhs) { return lhs.value_ == rhs.value_; } - [[nodiscard]] friend constexpr bool operator!=(my_value lhs, my_value rhs) { return !(lhs == rhs); } - [[nodiscard]] friend constexpr bool operator<(my_value lhs, my_value rhs) { return lhs.value_ < rhs.value_; } - [[nodiscard]] friend constexpr bool operator>(my_value lhs, my_value rhs) { return rhs < lhs; } - [[nodiscard]] friend constexpr bool operator<=(my_value lhs, my_value rhs) { return !(rhs < lhs); } - [[nodiscard]] friend constexpr bool operator>=(my_value lhs, my_value rhs) { return !(lhs < rhs); } + // constexpr my_value& operator+=(my_value other) { value_ += other.value_; return *this; } + // constexpr my_value& operator-=(my_value other) { value_ -= other.value_; return *this; } + // constexpr my_value& operator*=(my_value other) { value_ *= other.value_; return *this; } + // constexpr my_value& operator/=(my_value other) { value_ /= other.value_; return *this; } - constexpr operator const T&() const & { return value_; } - }; + [[nodiscard]] constexpr my_value operator-() const { return my_value(-value_); } + + [[nodiscard]] friend constexpr my_value operator+(my_value lhs, my_value rhs) { + return my_value(lhs.value_ + rhs.value_); + } + [[nodiscard]] friend constexpr my_value operator-(my_value lhs, my_value rhs) { + return my_value(lhs.value_ - rhs.value_); + } + [[nodiscard]] friend constexpr my_value operator*(my_value lhs, my_value rhs) { + return my_value(lhs.value_ * rhs.value_); + } + [[nodiscard]] friend constexpr my_value operator/(my_value lhs, my_value rhs) { + return my_value(lhs.value_ / rhs.value_); + } + + [[nodiscard]] friend constexpr bool operator==(my_value lhs, my_value rhs) { return lhs.value_ == rhs.value_; } + [[nodiscard]] friend constexpr bool operator!=(my_value lhs, my_value rhs) { return !(lhs == rhs); } + [[nodiscard]] friend constexpr bool operator<(my_value lhs, my_value rhs) { return lhs.value_ < rhs.value_; } + [[nodiscard]] friend constexpr bool operator>(my_value lhs, my_value rhs) { return rhs < lhs; } + [[nodiscard]] friend constexpr bool operator<=(my_value lhs, my_value rhs) { return !(rhs < lhs); } + [[nodiscard]] friend constexpr bool operator>=(my_value lhs, my_value rhs) { return !(lhs < rhs); } + + constexpr operator const T&() const& { return value_; } +}; } // namespace namespace units { - template - inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; +template +inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; - template - struct quantity_values> { - static constexpr my_value zero() { return my_value(0); } - static constexpr my_value max() { return std::numeric_limits::max(); } - static constexpr my_value min() { return std::numeric_limits::lowest(); } - }; +template +struct quantity_values> { + static constexpr my_value zero() { return my_value(0); } + static constexpr my_value max() { return std::numeric_limits::max(); } + static constexpr my_value min() { return std::numeric_limits::lowest(); } +}; } // namespace units namespace std { - template - struct common_type, my_value> : std::type_identity>> { - }; +template +struct common_type, my_value> : std::type_identity>> {}; - template - struct common_type, U> : common_type { - }; +template +struct common_type, U> : common_type {}; - template - struct common_type> : common_type { - }; +template +struct common_type> : common_type {}; } // namespace std namespace { - static_assert(units::Scalar>); - static_assert(std::convertible_to, float>); - static_assert(std::convertible_to>); +static_assert(units::Scalar>); +static_assert(std::convertible_to, float>); +static_assert(std::convertible_to>); - using namespace units; +using namespace units; +using namespace units::si; - // class invariants +// class invariants - // constexpr quantity q; // should a static_assert - // constexpr quantity> error(0m); // should trigger a static_assert - // constexpr quantity error(0); // should trigger a static_assert - // constexpr quantity>, int> error(0); // should trigger a static_assert +// constexpr quantity error(0); // should not compile (unit of a different dimension) +// constexpr quantity> error(0); // should not compile (quantity used as Rep) +// constexpr quantity error(0); // should not compile (reordered arguments) +// constexpr quantity, metre>, int> error(0); // should not compile (negative unit ratio) - // member types +// member types - static_assert(std::is_same_v::rep, int>); - static_assert(std::is_same_v::rep, double>); - static_assert(std::is_same_v::unit, metre>); - static_assert(std::is_same_v::unit, kilometre>); +static_assert(std::is_same_v::rep, int>); +static_assert(std::is_same_v::rep, double>); +static_assert(std::is_same_v::unit, metre>); +static_assert(std::is_same_v::unit, kilometre>); - // constructors +// constructors - using my_int = my_value; - using my_double = my_value; +using my_int = my_value; +using my_double = my_value; - static_assert(quantity().count() == 0); - constexpr quantity km{1000}; - static_assert(km.count() == 1000); - static_assert(quantity(km).count() == km.count()); +static_assert(length().count() == 0); +constexpr length km{1000}; +static_assert(km.count() == 1000); +static_assert(length(km).count() == km.count()); - static_assert(quantity(1).count() == 1); - static_assert(quantity(my_value(1)).count() == 1); - static_assert(quantity(1).count() == my_int{1}); - // static_assert(quantity(1.0).count() == 1); // should not compile - // static_assert(quantity(my_value(1.0)).count() == 1); // should not compile - // static_assert(quantity(1.0).count() == 1); // should not compile - static_assert(quantity(1.0).count() == 1.0); - static_assert(quantity(my_value(1.0)).count() == 1.0); - static_assert(quantity(1).count() == 1.0); - static_assert(quantity(my_value(1)).count() == 1.0); - static_assert(quantity(3.14).count() == 3.14); - static_assert(quantity(1.0).count() == my_double{1.0}); - static_assert(quantity(1).count() == my_double{1.0}); - static_assert(quantity(3.14).count() == my_double{3.14}); +static_assert(length(1).count() == 1); +static_assert(length(my_value(1)).count() == 1); +static_assert(length(1).count() == my_int{1}); +// static_assert(length(1.0).count() == 1); // should not compile (truncating conversion) +// static_assert(length(my_value(1.0)).count() == 1); // should not compile (truncating conversion) +// static_assert(length(1.0).count() == my_int{1}); // should not compile (truncating conversion) +static_assert(length(1.0).count() == 1.0); +static_assert(length(my_value(1.0)).count() == 1.0); +static_assert(length(1).count() == 1.0); +static_assert(length(my_value(1)).count() == 1.0); +static_assert(length(3.14).count() == 3.14); +static_assert(length(1.0).count() == my_double{1.0}); +static_assert(length(1).count() == my_double{1.0}); +static_assert(length(3.14).count() == my_double{3.14}); - static_assert(quantity(km).count() == 1000); - // static_assert(quantity(quantity(3.14)).count() == 3); // should not compile - static_assert(quantity(quantity_cast>(3.14m)).count() == 3); - // static_assert(quantity(quantity(1000.0)).count() == 1000); // should not compile - // static_assert(quantity(1000.0m).count() == 1000); // should not compile - static_assert(quantity(1000.0m).count() == 1000.0); - static_assert(quantity(quantity(1000.0)).count() == 1000.0); - static_assert(quantity(1000.0m).count() == my_double{1000.0}); - static_assert(quantity(km).count() == 1000.0); - static_assert(quantity(km).count() == my_double{1000.0}); - static_assert(quantity(1km).count() == 1000); - // static_assert(quantity(1_s).count() == 1); // should not compile - // static_assert(quantity(1010m).count() == 1); // should not compile - static_assert(quantity(quantity_cast>(1010m)).count() == 1); +static_assert(length(km).count() == 1000); +// static_assert(length(length(3.14)).count() == 3); // should not compile (truncating conversion) +static_assert(length(quantity_cast>(3.14m)).count() == 3); +// static_assert(length(length(1000.0)).count() == 1000); // should not compile (truncating conversion) +// static_assert(length(1000.0m).count() == my_int{1000}); // should not compile (truncating conversion) +static_assert(length(1000.0m).count() == 1000.0); +static_assert(length(length(1000.0)).count() == 1000.0); +static_assert(length(1000.0m).count() == my_double{1000.0}); +static_assert(length(km).count() == 1000.0); +static_assert(length(km).count() == my_double{1000.0}); +static_assert(length(1km).count() == 1000); +// static_assert(length(1s).count() == 1); // should not compile (different dimensions) +//static_assert(length(1010m).count() == 1); // should not compile (truncating conversion) +static_assert(length(quantity_cast>(1010m)).count() == 1); - // assignment operator +// assignment operator - static_assert([]() { - quantity l1(1), l2(2); - return l2 = l1; - }().count() == 1); +static_assert([]() { length l1(1), l2(2); return l2 = l1; }().count() == 1); - // static member functions +// static member functions - static_assert(quantity::zero().count() == 0); - static_assert(quantity::min().count() == std::numeric_limits::lowest()); - static_assert(quantity::max().count() == std::numeric_limits::max()); - static_assert(quantity::zero().count() == 0.0); - static_assert(quantity::min().count() == std::numeric_limits::lowest()); - static_assert(quantity::max().count() == std::numeric_limits::max()); - static_assert(quantity::zero().count() == my_int{0}); - static_assert(quantity::min().count() == my_int{std::numeric_limits::lowest()}); - static_assert(quantity::max().count() == my_int{std::numeric_limits::max()}); - static_assert(quantity::zero().count() == my_double{0.0}); - static_assert(quantity::min().count() == my_double{std::numeric_limits::lowest()}); - static_assert(quantity::max().count() == my_double{std::numeric_limits::max()}); +static_assert(length::zero().count() == 0); +static_assert(length::min().count() == std::numeric_limits::lowest()); +static_assert(length::max().count() == std::numeric_limits::max()); +static_assert(length::zero().count() == 0.0); +static_assert(length::min().count() == std::numeric_limits::lowest()); +static_assert(length::max().count() == std::numeric_limits::max()); +static_assert(length::zero().count() == my_int{0}); +static_assert(length::min().count() == my_int{std::numeric_limits::lowest()}); +static_assert(length::max().count() == my_int{std::numeric_limits::max()}); +static_assert(length::zero().count() == my_double{0.0}); +static_assert(length::min().count() == my_double{std::numeric_limits::lowest()}); +static_assert(length::max().count() == my_double{std::numeric_limits::max()}); - // unary member operators +// unary member operators - static_assert((+km).count() == 1000); - static_assert((-km).count() == -1000); - static_assert((+(-km)).count() == -1000); - static_assert((-(-km)).count() == 1000); +static_assert((+km).count() == 1000); +static_assert((-km).count() == -1000); +static_assert((+(-km)).count() == -1000); +static_assert((-(-km)).count() == 1000); - // binary member operators +// binary member operators - static_assert([](auto v) { - auto vv = v++; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(1001), quantity(1000))); - static_assert([](auto v) { - auto vv = ++v; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(1001), quantity(1001))); - static_assert([](auto v) { - auto vv = v--; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(999), quantity(1000))); - static_assert([](auto v) { - auto vv = --v; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(999), quantity(999))); +static_assert([](auto v) { + auto vv = v++; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(1001), length(1000))); +static_assert([](auto v) { + auto vv = ++v; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(1001), length(1001))); +static_assert([](auto v) { + auto vv = v--; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(999), length(1000))); +static_assert([](auto v) { + auto vv = --v; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(999), length(999))); - // compound assignment +// compound assignment - static_assert((1m += 1m).count() == 2); - static_assert((2m -= 1m).count() == 1); - static_assert((1m *= 2).count() == 2); - static_assert((2m /= 2).count() == 1); - static_assert((7m %= 2).count() == 1); - static_assert((7m %= 2m).count() == 1); -// static_assert((7.m %= 2.).count() == 1); // should not compile -// static_assert((7.m %= 2).count() == 1); // should not compile -// static_assert((7m %= 2.).count() == 1); // should not compile - static_assert((7m %= 2m).count() == 1); -// static_assert((7.m %= 2.m).count() == 1); // should not compile -// static_assert((7.m %= 2m).count() == 1); // should not compile -// static_assert((7m %= 2.m).count() == 1); // should not compile +static_assert((1m += 1m).count() == 2); +static_assert((2m -= 1m).count() == 1); +static_assert((1m *= 2).count() == 2); +static_assert((2m /= 2).count() == 1); +static_assert((7m %= 2).count() == 1); +static_assert((7m %= 2m).count() == 1); +// static_assert((7.m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7.m %= 2).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) +static_assert((7m %= 2m).count() == 1); +// static_assert((7.m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7.m %= 2m).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) - // non-member arithmetic operators +// non-member arithmetic operators - static_assert(std::is_same_v() + quantity()), quantity>); - static_assert(std::is_same_v() + quantity()), quantity>); - static_assert(std::is_same_v() + quantity()), quantity>); - static_assert(std::is_same_v() - quantity()), quantity>); - static_assert(std::is_same_v() - quantity()), quantity>); - static_assert(std::is_same_v() * 1.0), quantity>); - static_assert(std::is_same_v()), quantity>); - static_assert(std::is_same_v() * quantity()), quantity>); - static_assert(std::is_same_v()), quantity>); - static_assert(std::is_same_v() / 1.0), quantity>); - static_assert(std::is_same_v() / quantity()), double>); - static_assert(std::is_same_v() / quantity()), double>); - static_assert(std::is_same_v() / quantity()), quantity>); - static_assert(std::is_same_v() % short(1)), quantity>); - static_assert(std::is_same_v() % quantity(1)), quantity>); +static_assert(std::is_same_v() + length()), length>); +static_assert(std::is_same_v() + length()), length>); +static_assert( + std::is_same_v() + length()), length>); +static_assert(std::is_same_v() - length()), length>); +static_assert( + std::is_same_v() - length()), length>); +static_assert(std::is_same_v() * 1.0), length>); +static_assert(std::is_same_v()), length>); +static_assert( + std::is_same_v() * si::time()), length>); +static_assert( + std::is_same_v() * si::time()), length, metre>, int>>); +static_assert(std::is_same_v() * si::time()), + quantity, units::exp>, scaled_unit, unknown_unit>>>); +static_assert(std::is_same_v()), frequency>); +static_assert(std::is_same_v()), frequency, hertz>, int>>); +static_assert(std::is_same_v()), si::time>); +static_assert(std::is_same_v()), + quantity>, scaled_unit, unknown_unit>>>); +static_assert(std::is_same_v() / 1.0), length>); +static_assert(std::is_same_v() / length()), double>); +static_assert(std::is_same_v() / length()), double>); +static_assert( + std::is_same_v() / si::time()), velocity>); +static_assert( + std::is_same_v() / si::time()), velocity, metre_per_second>>>); +static_assert(std::is_same_v() / length()), + quantity, units::exp>, scaled_unit, unknown_unit>>>); +static_assert(std::is_same_v() % short(1)), length>); +static_assert(std::is_same_v() % length(1)), length>); - static_assert((1m + km).count() == 1001); - static_assert((1m + 1km).count() == 1001); - static_assert((km - 1m).count() == 999); - static_assert((1km - 1m).count() == 999); - static_assert((2m * 2).count() == 4); - static_assert((3 * 3m).count() == 9); - static_assert((4m / 2).count() == 2); - static_assert(4m / 2m == 2); - static_assert(4km / 2000m == 2); - static_assert((7m % 2).count() == 1); - static_assert((7m % 2m).count() == 1); - static_assert((7km % 2000m).count() == 1000); +static_assert((1m + km).count() == 1001); +static_assert((1m + 1km).count() == 1001); +static_assert((km - 1m).count() == 999); +static_assert((1km - 1m).count() == 999); +static_assert((2m * 2).count() == 4); +static_assert((3 * 3m).count() == 9); +static_assert((4m / 2).count() == 2); +static_assert(4m / 2m == 2); +static_assert(4km / 2000m == 2); +static_assert((7m % 2).count() == 1); +static_assert((7m % 2m).count() == 1); +static_assert((7km % 2000m).count() == 1000); - // comparators +static_assert((10sq_km * 10sq_km) / 50sq_km == 2sq_km); - static_assert(2m + 1m == 3m); - static_assert(!(2m + 2m == 3m)); - static_assert(2m + 2m != 3m); - static_assert(!(2m + 2m != 4m)); - static_assert(2m > 1m); - static_assert(!(1m > 1m)); - static_assert(1m < 2m); - static_assert(!(2m < 2m)); - static_assert(2m >= 1m); - static_assert(2m >= 2m); - static_assert(!(2m >= 3m)); - static_assert(1m <= 2m); - static_assert(2m <= 2m); - static_assert(!(3m <= 2m)); +// comparators - static_assert(3m == 3.0m); - static_assert(3m != 3.14m); - static_assert(2m > 1.0m); - static_assert(1.0m < 2m); - static_assert(2.0m >= 1m); - static_assert(1m <= 2.0m); +static_assert(2m + 1m == 3m); +static_assert(!(2m + 2m == 3m)); +static_assert(2m + 2m != 3m); +static_assert(!(2m + 2m != 4m)); +static_assert(2m > 1m); +static_assert(!(1m > 1m)); +static_assert(1m < 2m); +static_assert(!(2m < 2m)); +static_assert(2m >= 1m); +static_assert(2m >= 2m); +static_assert(!(2m >= 3m)); +static_assert(1m <= 2m); +static_assert(2m <= 2m); +static_assert(!(3m <= 2m)); - static_assert(1000m == 1km); - static_assert(1001m != 1km); - static_assert(1001m > 1km); - static_assert(999m < 1km); - static_assert(1000m >= 1km); - static_assert(1000m <= 1km); +static_assert(3m == 3.0m); +static_assert(3m != 3.14m); +static_assert(2m > 1.0m); +static_assert(1.0m < 2m); +static_assert(2.0m >= 1m); +static_assert(1m <= 2.0m); - // is_quantity +static_assert(1000m == 1km); +static_assert(1001m != 1km); +static_assert(1001m > 1km); +static_assert(999m < 1km); +static_assert(1000m >= 1km); +static_assert(1000m <= 1km); - static_assert(Quantity>); +// is_quantity - // common_quantity +static_assert(Quantity>); - static_assert(std::is_same_v, quantity>, quantity>); - static_assert(std::is_same_v, quantity>, quantity>); - static_assert(std::is_same_v, quantity>, quantity>); +// common_quantity - // quantity_cast +static_assert(std::is_same_v, length>, length>); +static_assert( + std::is_same_v, length>, length>); +static_assert(std::is_same_v, length>, + length>); - static_assert(std::is_same_v>>(2km))::unit, metre>); - static_assert(std::is_same_v>, ratio<1>>>(2km))::unit, metre>); +// quantity_cast - // static_assert(quantity_cast(2km).count() == 2000); // should not compile - static_assert(quantity_cast>(2km).count() == 2000); - static_assert(quantity_cast>(2000m).count() == 2); +static_assert(std::is_same_v, metre>>(2km))::unit, metre>); - // time +static_assert(quantity_cast>(2km).count() == 2000); +static_assert(quantity_cast>(2000m).count() == 2); +static_assert(quantity_cast>(1.23m).count() == 1); +static_assert(quantity_cast(2km).count() == 2000); +static_assert(quantity_cast(2000m).count() == 2); +static_assert(quantity_cast(1.23m).count() == 1); - // static_assert(1s == 1m); // should not compile - static_assert(1h == 3600s); +// time - // length +// static_assert(1s == 1m); // should not compile (different dimensions) +static_assert(1h == 3600s); - static_assert(1km == 1000m); - static_assert(1km + 1m == 1001m); - static_assert(10km / 5km == 2); - static_assert(10km / 2 == 5km); +// length - // velocity +static_assert(1km == 1000m); +static_assert(1km + 1m == 1001m); +static_assert(10km / 5km == 2); +static_assert(10km / 2 == 5km); - static_assert(10m / 5s == 2mps); - static_assert(10 / 5s * 1m == 2mps); - static_assert(1km / 1s == 1000mps); - static_assert(2kmph * 2h == 4km); - static_assert(2km / 2kmph == 1h); +// velocity - static_assert(std::is_same_v(2m)), decltype(4sq_m)>); +static_assert(10m / 5s == 2mps); +static_assert(10 / 5s * 1m == 2mps); +static_assert(1km / 1s == 1000mps); +static_assert(2kmph * 2h == 4km); +static_assert(2km / 2kmph == 1h); + +static_assert(std::is_same_v(2m)), decltype(4sq_m)>); } // namespace diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index 997645c0..ec1eae7a 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -1,28 +1,28 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// 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. + // The MIT License (MIT) + // + // Copyright (c) 2018 Mateusz Pusz + // + // 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. -#include "units/ratio.h" + #include "units/ratio.h" -namespace { + namespace { using namespace units; @@ -31,6 +31,8 @@ namespace { static_assert(same, ratio<1, 2>>); + static_assert(std::is_same_v, ratio<3, 8>>, ratio<3, 8>>); + static_assert(std::is_same_v, ratio<1>>, ratio<3, 8>>); static_assert(std::is_same_v, ratio<1, 8>>, ratio<1, 2>>); static_assert(std::is_same_v, ratio<1, 2>>, ratio<2>>); static_assert(std::is_same_v, ratio<2>>, ratio<1, 4>>); @@ -63,4 +65,4 @@ namespace { static_assert(std::is_same_v, ratio<1, 1000>>, ratio<1, 1000>>); static_assert(std::is_same_v, ratio<1>>, ratio<1, 1000>>); -} // namespace \ No newline at end of file + } // namespace \ No newline at end of file diff --git a/test/unit_test/static/si_cgs_test.cpp b/test/unit_test/static/si_cgs_test.cpp new file mode 100644 index 00000000..78605221 --- /dev/null +++ b/test/unit_test/static/si_cgs_test.cpp @@ -0,0 +1,151 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace units; + +static_assert(cgs::length(100) == si::length(1)); +static_assert(cgs::mass(1'000) == si::mass(1)); +static_assert(cgs::time(1) == si::time(1)); +static_assert(cgs::velocity(100) == si::velocity(1)); +static_assert(cgs::area(10000) == si::area(1)); +static_assert(cgs::acceleration(100) == si::acceleration(1)); +static_assert(cgs::force(100'000) == si::force(1)); +static_assert(cgs::energy(10'000'000) == si::energy(1)); +static_assert(cgs::power(10'000'000) == si::power(1)); +static_assert(cgs::pressure(10) == si::pressure(1)); + +namespace si_test { + +using namespace units::si::literals; + +static_assert(cgs::length(100) == 1m); +static_assert(cgs::mass(1'000) == 1kg); +static_assert(cgs::time(1) == 1s); +static_assert(cgs::velocity(100) == 1mps); +static_assert(cgs::acceleration(100) == 1mps_sq); +static_assert(cgs::force(100'000) == 1N); +static_assert(cgs::energy(10'000'000) == 1_J); +static_assert(cgs::power(10'000'000) == 1W); +static_assert(cgs::pressure(10) == 1Pa); + +} + +namespace cgs_test { + +using namespace units::cgs::literals; + +static_assert(100cm == si::length(1)); +static_assert(1'000g == si::mass(1)); +static_assert(1s == si::time(1)); +static_assert(100cmps == si::velocity(1)); +static_assert(100Gal == si::acceleration(1)); +static_assert(100'000dyn == si::force(1)); +static_assert(10'000'000_erg == si::energy(1)); +static_assert(10'000'000_ergps == si::power(1)); +static_assert(10Ba == si::pressure(1)); + +} + +namespace both_test { + +using namespace units::si::literals; +using namespace units::cgs::literals; + +// static_assert(100cm == 1m); // ambiguous +// static_assert(1'000g == 1kg); // ambiguous +static_assert(1s == 1s); +static_assert(100cmps == 1mps); +static_assert(100Gal == 1mps_sq); +static_assert(100'000dyn == 1N); +static_assert(10'000'000_erg == 1_J); +static_assert(10'000'000_ergps == 1W); +static_assert(10Ba == quantity_cast(1Pa)); + +} + +namespace cgs_test { + +// addition + +// static_assert(100cm + si::length(1) == si::length(2)); // should not compile (different dimensions) +// static_assert(si::length(1) + 100cm == si::length(2)); // should not compile (different dimensions) +static_assert(quantity_cast>(100cm) + si::length(1) == si::length(2)); +static_assert(si::length(1) + quantity_cast>(100cm) == si::length(2)); +static_assert(100cm + quantity_cast>(si::length(1)) == 200cm); +static_assert(quantity_cast>(si::length(1)) + 100cm == 200cm); + +// substraction + +// static_assert(500cm - si::length(1) == si::length(4)); // should not compile (different dimensions) +// static_assert(si::length(5) - 100cm == si::length(4)); // should not compile (different dimensions) +static_assert(quantity_cast>(500cm) - si::length(1) == si::length(4)); +static_assert(si::length(5) - quantity_cast>(100cm) == si::length(4)); +static_assert(500cm - quantity_cast>(si::length(1)) == 400cm); +static_assert(quantity_cast>(si::length(5)) - 100cm == 400cm); + +// multiplication + +// static_assert(200cm * si::length(2) == si::area(4)); // should not compile (unknown dimension) + +static_assert(quantity_cast(200cm) * si::length(2) == si::area(4)); +static_assert(200cm * quantity_cast(si::length(2)) == 40'000sq_cm); + +// TODO Add support for quantity_cast on an unknown_dimension? +// static_assert(quantity_cast>(200cm * si::length(2)) == si::area(4)); +// static_assert(quantity_cast(200cm * si::length(2)) == si::area(4)); +// static_assert(quantity_cast>(200cm * si::length(2)) == 40'000sq_cm); +// static_assert(quantity_cast(200cm * si::length(2)) == 40'000sq_cm); + +// division + +// static_assert(si::area(4) / 200cm == si::length(2)); // should not compile (unknown dimension) + +static_assert(si::area(4) / quantity_cast>(200cm) == si::length(2)); +static_assert(quantity_cast>(si::area(4)) / 200cm == 200cm); + +} + +} diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp new file mode 100644 index 00000000..19a3cb8e --- /dev/null +++ b/test/unit_test/static/si_test.cpp @@ -0,0 +1,252 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace units; +using namespace units::si; + +/* ************** BASE DIMENSIONS **************** */ + +// length + +static_assert(1km == 1000m); +static_assert(1m == 100cm); +static_assert(1m == 1000mm); +static_assert(1km + 1m == 1001m); +static_assert(10km / 5km == 2); +static_assert(100mm / 5cm == 2); +static_assert(10km / 2 == 5km); + +static_assert(1yd == 0.9144m); +static_assert(1yd == 3ft); +static_assert(1ft == 12in); +static_assert(1mi == 1760yd); + +static_assert(5in + 8cm == 207mm); + +static_assert(millimetre::symbol == "mm"); +static_assert(centimetre::symbol == "cm"); +static_assert(kilometre::symbol == "km"); + +// mass + +static_assert(1kg == 1000g); + +static_assert(kilogram::symbol == "kg"); + +// time + +static_assert(1us == 1000ns); +static_assert(1ms == 1000us); +static_assert(1s == 1000ms); +static_assert(1min == 60s); +static_assert(1h == 3600s); + +static_assert(nanosecond::symbol == "ns"); +static_assert(microsecond::symbol == "µs"); +static_assert(millisecond::symbol == "ms"); + +// current + +// temperature + +// substance + +// luminous intensity + +/* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ + +// frequency + +static_assert(1000mHz == 1Hz); +static_assert(1000Hz == 1kHz); +static_assert(1000kHz == 1MHz); +static_assert(1000MHz == 1GHz); +static_assert(1000GHz == 1THz); + +static_assert(millihertz::symbol == "mHz"); +static_assert(kilohertz::symbol == "kHz"); +static_assert(megahertz::symbol == "MHz"); +static_assert(gigahertz::symbol == "GHz"); +static_assert(terahertz::symbol == "THz"); + +static_assert(2 / 1s == 2Hz); +static_assert(120 / 1min == 2Hz); +static_assert(1000 / 1s == 1kHz); +static_assert(1 / 1ms == 1kHz); +static_assert(3.2GHz == 3'200'000'000Hz); +static_assert(10Hz * 1min == 600); +static_assert(2 / 1Hz == 2s); + +// force +static_assert(10kg * 10mps_sq == 100N); +static_assert(100N / 1mps_sq == 100kg); +static_assert(100N / 1kg == 100mps_sq); + +// pressure + +static_assert(10N / 10sq_m == 1Pa); +static_assert(10N / 1Pa == 10sq_m); +static_assert(1Pa * 10sq_m == 10N); + +// energy + +static_assert(1000mJ == 1_J); +static_assert(1000_J == 1kJ); +static_assert(1000kJ == 1MJ); +static_assert(1000MJ == 1GJ); + +static_assert(millijoule::symbol == "mJ"); +static_assert(kilojoule::symbol == "kJ"); +static_assert(megajoule::symbol == "MJ"); +static_assert(gigajoule::symbol == "GJ"); + +static_assert(10N * 10m == 100_J); +static_assert(100_J / 10m == 10N); +static_assert(100_J / 10N == 10m); +static_assert(10Pa * 10cub_m == 100_J); +static_assert(100_J / 10Pa == 10cub_m); +static_assert(100_J / 10cub_m == 10Pa); + +// power + +static_assert(1000mW == 1W); +static_assert(1000W == 1kW); +static_assert(1000kW == 1MW); +static_assert(1000MW == 1GW); + +static_assert(milliwatt::symbol == "mW"); +static_assert(kilowatt::symbol == "kW"); +static_assert(megawatt::symbol == "MW"); +static_assert(gigawatt::symbol == "GW"); + +static_assert(10_J / 10s == 1W); +static_assert(1W * 10s == 10_J); +static_assert(10_J / 1W == 10s); + +// electric charge + +static_assert(10A * 10s == 100C); +static_assert(100C / 10A == 10s); +static_assert(100C / 10s == 10A); + +// voltage + +static_assert(10W / 10A == 1V); +static_assert(10W / 1V == 10A); +static_assert(1V * 10A == 10W); +static_assert(10_J / 10C == 1V); +static_assert(10_J / 1V == 10C); +static_assert(10C * 1V == 10_J); + +// capacitance + +static_assert(10C / 10V == 1F); +static_assert(10C / 1F == 10V); +static_assert(10V * 1F == 10C); + +/* ************** DERIVED DIMENSIONS IN TERMS OF BASE UNITS **************** */ + +// velocity + +static_assert(std::is_same_v, metre_per_second>, std::int64_t>>); + +static_assert(10m / 5s == 2mps); +static_assert(10 / 5s * 1m == 2mps); +static_assert(1km / 1s == 1000mps); +// static_assert(1km / 1h == 1kmph); // should not compile +static_assert(1.0km / 1h == 1kmph); +static_assert(1000.0m / 3600.0s == 1kmph); + +static_assert(10.0mi / 2h == 5mph); + +static_assert(2kmph * 2h == 4km); +// static_assert(2kmph * 15min == 500m); // should not compile +static_assert(2kmph * 15.0min == 500m); +static_assert(2.0kmph * 15min == 500m); + +static_assert(2km / 2kmph == 1h); +// static_assert(2000m / 2kmph == 1h); // should not compile +static_assert(quantity_cast(2000m) / 2kmph == 1h); + +static_assert(detail::unit_text() == "m/s"); +static_assert(kilometre_per_hour::symbol == "km/h"); +static_assert(mile_per_hour::symbol == "mi/h"); + +// acceleration + +static_assert(10mps / 10s == 1mps_sq); +static_assert(10mps / 1mps_sq == 10s); +static_assert(1mps_sq * 10s == 10mps); + +static_assert(detail::unit_text() == "m/s²"); + +// area + +static_assert(10m * 10m == 100sq_m); +static_assert(100sq_m / 10m == 10m); +static_assert(10km * 10km == 100sq_km); +static_assert(1sq_m == 10'000sq_cm); + +static_assert(detail::unit_text() == "m²"); + +// volume + +static_assert(1m * 1m * 1m == 1cub_m); +static_assert(10sq_m * 10m == 100cub_m); +static_assert(10km * 10km * 10km == 1000cub_km); +static_assert(1cub_m == 1'000'000cub_cm); + +static_assert(detail::unit_text() == "m³"); + +/* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ + +static_assert(10N / 2m == 5Npm); +static_assert(10N / 5Npm == 2m); +static_assert(2m * 5Npm == 10N); + +static_assert(detail::unit_text() == "N/m"); + +} // namespace diff --git a/test/unit_test/static/type_list_test.cpp b/test/unit_test/static/type_list_test.cpp index 12d89c50..f4236a6a 100644 --- a/test/unit_test/static/type_list_test.cpp +++ b/test/unit_test/static/type_list_test.cpp @@ -20,84 +20,89 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/bits/type_list.h" -#include "units/dimension.h" +#include "units/bits/external/type_list.h" +#include "units/derived_dimension.h" +#include "units/unit.h" #include namespace { - using namespace units; +using namespace units; - template - struct type_list; +template +struct type_list; - // type_list_push_front +// type_list_push_front - static_assert(std::is_same_v, int>, type_list>); - static_assert(std::is_same_v, int, long, double>, type_list>); - static_assert(std::is_same_v, int, long>, type_list>); +static_assert(std::is_same_v, int>, type_list>); +static_assert(std::is_same_v, int, long, double>, type_list>); +static_assert(std::is_same_v, int, long>, type_list>); - // type_list_push_back +// type_list_push_back - static_assert(std::is_same_v, int>, type_list>); - static_assert(std::is_same_v, int, long, double>, type_list>); - static_assert(std::is_same_v, int, long>, type_list>); +static_assert(std::is_same_v, int>, type_list>); +static_assert(std::is_same_v, int, long, double>, type_list>); +static_assert(std::is_same_v, int, long>, type_list>); - // type_list_split +// type_list_split - static_assert(std::is_same_v, 0>::first_list, type_list<>>); - static_assert(std::is_same_v, 0>::second_list, type_list>); +static_assert(std::is_same_v, 0>::first_list, type_list<>>); +static_assert(std::is_same_v, 0>::second_list, type_list>); - static_assert(std::is_same_v, 1>::first_list, type_list>); - static_assert(std::is_same_v, 1>::second_list, type_list<>>); +static_assert(std::is_same_v, 1>::first_list, type_list>); +static_assert(std::is_same_v, 1>::second_list, type_list<>>); - static_assert(std::is_same_v, 0>::first_list, type_list<>>); - static_assert(std::is_same_v, 0>::second_list, type_list>); +static_assert(std::is_same_v, 0>::first_list, type_list<>>); +static_assert(std::is_same_v, 0>::second_list, type_list>); - static_assert(std::is_same_v, 1>::first_list, type_list>); - static_assert(std::is_same_v, 1>::second_list, type_list>); +static_assert(std::is_same_v, 1>::first_list, type_list>); +static_assert(std::is_same_v, 1>::second_list, type_list>); - static_assert(std::is_same_v, 2>::first_list, type_list>); - static_assert(std::is_same_v, 2>::second_list, type_list<>>); +static_assert(std::is_same_v, 2>::first_list, type_list>); +static_assert(std::is_same_v, 2>::second_list, type_list<>>); - static_assert(std::is_same_v, 1>::first_list, type_list>); - static_assert(std::is_same_v, 1>::second_list, type_list>); +static_assert(std::is_same_v, 1>::first_list, type_list>); +static_assert(std::is_same_v, 1>::second_list, type_list>); - static_assert(std::is_same_v, 2>::first_list, type_list>); - static_assert(std::is_same_v, 2>::second_list, type_list>); +static_assert(std::is_same_v, 2>::first_list, type_list>); +static_assert(std::is_same_v, 2>::second_list, type_list>); - // type_list_split_half +// type_list_split_half - static_assert(std::is_same_v>::first_list, type_list>); - static_assert(std::is_same_v>::second_list, type_list<>>); +static_assert(std::is_same_v>::first_list, type_list>); +static_assert(std::is_same_v>::second_list, type_list<>>); - static_assert(std::is_same_v>::first_list, type_list>); - static_assert(std::is_same_v>::second_list, type_list>); +static_assert(std::is_same_v>::first_list, type_list>); +static_assert(std::is_same_v>::second_list, type_list>); - static_assert(std::is_same_v>::first_list, type_list>); - static_assert(std::is_same_v>::second_list, type_list>); +static_assert(std::is_same_v>::first_list, type_list>); +static_assert(std::is_same_v>::second_list, type_list>); - static_assert( - std::is_same_v>::first_list, type_list>); - static_assert( - std::is_same_v>::second_list, type_list>); +static_assert( + std::is_same_v>::first_list, type_list>); +static_assert( + std::is_same_v>::second_list, type_list>); - // type_list_merge_sorted - struct d0 : base_dimension<"d0", ""> {}; - struct d1 : base_dimension<"d1", ""> {}; +// type_list_merge_sorted +struct u0 : named_unit {}; +struct d0 : base_dimension<"d0", u0> {}; +struct u1 : named_unit {}; +struct d1 : base_dimension<"d1", u1> {}; - static_assert(std::is_same_v>, type_list>, exp_less>, - type_list, exp>>); - static_assert(std::is_same_v>, type_list>, exp_less>, - type_list, exp>>); +static_assert(std::is_same_v>, type_list>, exp_less>, + type_list, exp>>); +static_assert(std::is_same_v>, type_list>, exp_less>, + type_list, exp>>); - // type_list_sort +// type_list_sort - template - using exp_sort = type_list_sort; +template +using exp_sort = type_list_sort; - static_assert(std::is_same_v>>, dimension>>); - static_assert(std::is_same_v, exp>>, dimension, exp>>); - static_assert(std::is_same_v, exp>>, dimension, exp>>); +static_assert(std::is_same_v>>, exp_list>>); +static_assert( + std::is_same_v, exp>>, exp_list, exp>>); +static_assert( + std::is_same_v, exp>>, exp_list, exp>>); } // namespace diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 91d910ac..f7b7aa42 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -20,174 +20,39 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include "units/unit.h" +#include "units/physical/si/prefixes.h" namespace { - using namespace units; +using namespace units; - /* ************** BASE DIMENSIONS **************** */ +struct metre : named_unit {}; +struct centimetre : prefixed_unit {}; +struct kilometre : prefixed_unit {}; +struct yard : named_scaled_unit, metre> {}; +struct foot : named_scaled_unit, yard> {}; +struct dim_length : base_dimension<"length", metre> {}; - // length +struct second : named_unit {}; +struct hour : named_scaled_unit, second> {}; +struct dim_time : base_dimension<"time", second> {}; - static_assert(1km == 1000m); - static_assert(1m == 100cm); - static_assert(1m == 1000mm); - static_assert(1km + 1m == 1001m); - static_assert(10km / 5km == 2); - static_assert(100mm / 5cm == 2); - static_assert(10km / 2 == 5km); +struct kelvin : named_unit {}; +// struct kilokelvin : prefixed_unit {}; // should not compile (prefix not allowed for this reference unit) - static_assert(1yd == 0.9144m); - static_assert(1yd == 3ft); - static_assert(1ft == 12in); - static_assert(1mi == 1760yd); +struct metre_per_second : unit {}; +struct dim_velocity : derived_dimension, exp> {}; +struct kilometre_per_hour : deduced_unit {}; - static_assert(5in + 8cm == 207mm); +static_assert(std::is_same_v, metre>>, metre>); +static_assert(std::is_same_v, metre>>, centimetre>); +static_assert(std::is_same_v, metre>>, yard>); +static_assert(std::is_same_v>, metre>>, foot>); +static_assert(std::is_same_v, metre_per_second>>, kilometre_per_hour>); - static_assert(millimetre::symbol == "mm"); - static_assert(centimetre::symbol == "cm"); - static_assert(kilometre::symbol == "km"); - - // mass - - static_assert(1kg == 1000g); - - static_assert(kilogram::symbol == "kg"); - - // time - - static_assert(1h == 3600s); - - static_assert(nanosecond::symbol == "ns"); - static_assert(microsecond::symbol == "µs"); - static_assert(millisecond::symbol == "ms"); - - // current - - // temperature - - // substance - - // luminous intensity - - - /* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ - - // frequency - - static_assert(2 / 1s == 2Hz); - static_assert(120 / 1min == 2Hz); - static_assert(1000 / 1s == 1kHz); - static_assert(1 / 1ms == 1kHz); - static_assert(3.2GHz == 3'200'000'000Hz); - static_assert(10Hz * 1min == 600); - - static_assert(millihertz::symbol == "mHz"); - static_assert(kilohertz::symbol == "kHz"); - static_assert(megahertz::symbol == "MHz"); - static_assert(gigahertz::symbol == "GHz"); - static_assert(terahertz::symbol == "THz"); - - // force - - static_assert(10kg * 10mps_sq == 100N); - - // pressure - - static_assert(10N / 10sq_m == 1Pa); - - // energy - - static_assert(10N * 10m == 100_J); - static_assert(10Pa * 10cub_m == 100_J); - - // power - - static_assert(10_J / 10s == 1W); - - // electric charge - - static_assert(10A * 10s == 100C); - - // voltage - - static_assert(10W / 10A == 1V); - static_assert(10_J / 10C == 1V); - - // capacitance - - static_assert(10C / 10V == 1F); - - /* ************** DERIVED DIMENSIONS IN TERMS OF BASE UNITS **************** */ - - // velocity - - static_assert(std::is_same_v>, std::int64_t>>); - - static_assert(10m / 5s == 2mps); - static_assert(10 / 5s * 1m == 2mps); - static_assert(1km / 1s == 1000mps); - // static_assert(1km / 1h == 1kmph); // should not compile - static_assert(1.0km / 1h == 1kmph); - static_assert(1000.0m / 3600.0s == 1kmph); - - static_assert(10.0mi / 2h == 5mph); - - static_assert(2kmph * 2h == 4km); - // static_assert(2kmph * 15min == 500m); // should not compile - static_assert(2kmph * 15.0min == 500m); - static_assert(2.0kmph * 15min == 500m); - - static_assert(2km / 2kmph == 1h); - // static_assert(2000m / 2kmph == 1h); // should not compile - static_assert(quantity_cast>(2000m) / 2kmph == 1h); - -// static_assert(metre_per_second::symbol == basic_fixed_string("m/s")); - // static_assert(kilometre_per_hour::symbol == basic_fixed_string("km/h")); - - - // acceleration - - static_assert(10mps / 10s == 1mps_sq); - - // area - - static_assert(1m * 1m == 1sq_m); - static_assert(10km * 10km == 100sq_km); - static_assert(1sq_m == 10'000sq_cm); - - // volume - - static_assert(1m * 1m * 1m == 1cub_m); - static_assert(10sq_m * 10m == 100cub_m); - static_assert(10km * 10km * 10km == 1000cub_km); - static_assert(1cub_m == 1'000'000cub_cm); - - - /* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ - - static_assert(10N / 2m == 5Npm); +static_assert(centimetre::symbol == "cm"); +static_assert(kilometre::symbol == "km"); +static_assert(kilometre_per_hour::symbol == "km/h"); } // namespace diff --git a/test_package/test_package.cpp b/test_package/test_package.cpp index 16d5c493..a8a30cd9 100644 --- a/test_package/test_package.cpp +++ b/test_package/test_package.cpp @@ -20,18 +20,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include -using namespace units::literals; - -template -constexpr units::Velocity avg_speed(D d, T t) +constexpr units::Velocity AUTO avg_speed(units::Length AUTO d, units::Time AUTO t) { return d / t; } int main() { + using namespace units::si::literals; std::cout << "Average speed = " << avg_speed(240.km, 2h) << '\n'; }