mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
feat: quantity_kind and quantity_point_kind
This commit is contained in:
committed by
Mateusz Pusz
parent
384f4b2624
commit
6bf09aa646
@ -39,7 +39,7 @@ function(set_warnings target scope)
|
||||
set(MSVC_WARNINGS
|
||||
/W4 # Baseline reasonable warnings
|
||||
/w14062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
|
||||
# /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
|
||||
/w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/w14263 # 'function': member function does not override any base class virtual member function
|
||||
/w14265 # 'classname': class has virtual functions, but destructor is not
|
||||
|
@ -3,8 +3,10 @@
|
||||
- **0.7.0 WIP**
|
||||
- (!) refactor: `ScalableNumber` renamed to `QuantityValue`
|
||||
- (!) refactor: output stream operators moved to the `units/quantity_io.h` header file
|
||||
- refactor: `quantity_point` (partially) updated to reflect latest changes to `quantity`
|
||||
- refactor: basic concepts, `quantity` and `quantity_cast` refactored
|
||||
- refactor: `abs()` definition refactored to be more explicit about the return type
|
||||
- feat: quantity (point) kind support added (thanks [@johelegp](https://github.com/johelegp))
|
||||
- feat: unit constants support added (thanks [@johelegp](https://github.com/johelegp))
|
||||
- feat: interoperability with `std::chrono::duration` and other units libraries
|
||||
- feat: CTAD for dimensionless quantity added
|
||||
|
@ -70,6 +70,7 @@ set(unitsSphinxDocs
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/framework/dimensions.rst"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/framework/quantities.rst"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/framework/quantity_points.rst"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/framework/quantity_kinds.rst"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/framework/text_output.rst"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/framework/units.rst"
|
||||
|
||||
|
BIN
docs/_static/img/concepts.png
vendored
BIN
docs/_static/img/concepts.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 58 KiB |
@ -5,18 +5,18 @@ glide_computer
|
||||
|
||||
This example presents the usage of:
|
||||
|
||||
- different units for length, time, and velocity,
|
||||
- different kinds for length, time, and velocity,
|
||||
- quantities text output formatting,
|
||||
- `quantity_point` to mark "absolute" values like ``altitude`` (as opposed to ``height``),
|
||||
- a simple ``vector`` class to show how to handle 3D space for aviation needs and how to build abstractions
|
||||
on top of a `Quantity` concept.
|
||||
- the use of quantity kinds to show how to handle 3D space for aviation needs,
|
||||
- `quantity_point_kind` to mark "absolute" values like ``altitude`` (as opposed to ``height``).
|
||||
|
||||
``vector`` class template provides a strong type that divides quantities and quantity points to the ones
|
||||
on a horizontal (X, Y) plane and vertical (Z) axis. Its purpose is to make different kinds of quantity
|
||||
(i.e. length) separated strong types (i.e. distance, height). Additionally, it wraps both a `quantity`
|
||||
and `quantity_point` types which means that we can define a ``height`` and ``altitude`` respectively
|
||||
as different strong types. Some of its function are defined only for quantities (additional operators)
|
||||
and some for quantity points (additional constructor).
|
||||
The use of `quantity_kind` and `quantity_point_kind` provide strong typing
|
||||
that divide quantities and quantity points to the ones on a horizontal (X, Y) plane and vertical (Z) axis.
|
||||
Their purpose is to make different kinds of quantity
|
||||
(i.e. length) separate strong types (i.e. distance, height).
|
||||
Additionally, separating a quantity from its quantity point
|
||||
means that we can define a ``height`` and ``altitude`` as different strong types.
|
||||
A quantity point provides a more restricted interface meant for absolute calculations.
|
||||
|
||||
.. literalinclude:: ../../example/glide_computer.cpp
|
||||
:caption: glide_computer.cpp
|
||||
|
@ -4,7 +4,7 @@ Basic Concepts
|
||||
==============
|
||||
|
||||
The most important concepts in the library are `Unit`, `Dimension`,
|
||||
`Quantity`, and `QuantityPoint`:
|
||||
`Quantity`, `QuantityPoint`, `QuantityKind`, and `QuantityPointKind`:
|
||||
|
||||
.. image:: /_static/img/concepts.png
|
||||
:align: center
|
||||
@ -18,15 +18,35 @@ The most important concepts in the library are `Unit`, `Dimension`,
|
||||
[exponent<Dimension, Num, Den>]<-[derived_dimension<Child, Unit, Exponent...>]
|
||||
]
|
||||
|
||||
[<abstract>Quantity|
|
||||
[quantity<Dimension, Unit, Rep>]
|
||||
]
|
||||
|
||||
[<abstract>QuantityPoint|
|
||||
[quantity_point<Dimension, Unit, Rep>]
|
||||
]
|
||||
|
||||
[<abstract>QuantityKind|
|
||||
[quantity_kind<Kind, Unit, Rep>]
|
||||
]
|
||||
|
||||
[<abstract>QuantityPointKind|
|
||||
[quantity_point_kind<PointKind, Unit, Rep>]
|
||||
]
|
||||
|
||||
[<abstract>Unit]<-[Dimension]
|
||||
[Dimension]<-[Quantity]
|
||||
[Unit]<-[Quantity]
|
||||
[Quantity]<-[QuantityPoint]
|
||||
|
||||
[<abstract>Kind]<-[QuantityKind]
|
||||
[Dimension]<-[Kind]
|
||||
[Quantity]<-[QuantityKind]
|
||||
|
||||
[<abstract>PointKind]<-[QuantityPointKind]
|
||||
[Kind]<-[PointKind]
|
||||
[QuantityKind]-<[QuantityPointKind]
|
||||
|
||||
`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.
|
||||
@ -40,3 +60,7 @@ derived dimensions.
|
||||
specific representation.
|
||||
|
||||
`QuantityPoint` is an absolute `Quantity` with respect to some origin.
|
||||
|
||||
`QuantityKind` is a `Quantity` with more specific usage.
|
||||
|
||||
`QuantityPointKind` is an absolute `QuantityKind` with respect to some origin.
|
||||
|
@ -56,7 +56,8 @@ Explicit
|
||||
--------
|
||||
|
||||
Explicit conversions are available with
|
||||
the `quantity_cast` and `quantity_point_cast` function templates.
|
||||
the `quantity_cast`, `quantity_point_cast`,
|
||||
`quantity_kind_cast`, and `quantity_point_kind_cast` function templates.
|
||||
They are especially useful to force a truncating conversion across quantities of the same
|
||||
dimension for integral representation types and ratios that may cause precision loss::
|
||||
|
||||
@ -99,6 +100,13 @@ or a specific target `quantity_point`::
|
||||
|
||||
std::cout << "Point: " << quantity_point_cast<decltype(quantity_point{0_q_m})>(d) << '\n';
|
||||
|
||||
For a single explicit template argument,
|
||||
`quantity_point_kind_cast` is a superset of `quantity_kind_cast`,
|
||||
which is also a superset of `quantity_cast`.
|
||||
For the (dimension, unit) pair of explicit arguments case of `quantity_cast`,
|
||||
`quantity_point_kind_cast` and `quantity_kind_cast`
|
||||
accept a `PointKind` and `Kind` instead of a `Dimension`, respectively.
|
||||
|
||||
.. seealso::
|
||||
|
||||
For more information on conversion and casting and on how to extend the above "integral"
|
||||
@ -115,14 +123,14 @@ represent numbers it would be highly uncomfortable to every time type::
|
||||
|
||||
const auto d1 = 10_q_km;
|
||||
const auto d2 = 3_q_km;
|
||||
if(d1 / d2 > dimensionless<one, 2>) {
|
||||
if(d1 / d2 > dimensionless<one>(2)) {
|
||||
// ...
|
||||
}
|
||||
|
||||
or::
|
||||
|
||||
const auto fill_time_left = (box.height / box.fill_level(measured_mass) -
|
||||
dimensionless<one, 1>) * fill_time;
|
||||
dimensionless<one>(1)) * fill_time;
|
||||
|
||||
This is why it was decided to allow the ``dimensionless<one>`` quantity of any
|
||||
representation type to be implicitly constructible from this representation type.
|
||||
@ -147,7 +155,7 @@ could be ambiguous. For example::
|
||||
return d1 / d2 + 1;
|
||||
}
|
||||
|
||||
As long as we can reason about what such code means for ``foo(10_q_km, 2_q_km)`` it is not that obvious
|
||||
As long as we can reason about what such code means for ``foo(10_q_km, 2_q_km)`` it is not that obvious
|
||||
at all in the case of ``foo(10_q_cm, 2_q_ft)``. To make such code to compile for every case we have to
|
||||
either change the type of the resulting unit to the one having ``ratio(1)`` (:term:`coherent derived unit`)::
|
||||
|
||||
|
@ -50,7 +50,7 @@ probably will always end up in a quantity of a yet another dimension:
|
||||
|
||||
However, please note that there is an exception from the above rule.
|
||||
In case we divide the same dimensions, or multiply by the inverted
|
||||
dimension, than we will end up with just a scalable number type:
|
||||
dimension, than we will end up with just a dimensionless quantity:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 4-5
|
||||
@ -58,8 +58,23 @@ dimension, than we will end up with just a scalable number type:
|
||||
Time auto dur1 = 10_q_s;
|
||||
Time auto dur2 = 2_q_s;
|
||||
Frequency auto fr1 = 5_q_Hz;
|
||||
QuantityValue auto v1 = dur1 / dur2; // 5
|
||||
QuantityValue auto v2 = dur1 * fr1; // 50
|
||||
Dimensionless auto v1 = dur1 / dur2; // quantity(5)
|
||||
Dimensionless auto v2 = dur1 * fr1; // quantity(50)
|
||||
|
||||
Quantity kinds behave the same as quantities for addition, substraction, and
|
||||
multiplication with, and division by a :term:`scalable number`.
|
||||
|
||||
Multiplication and divison with a quantity
|
||||
(but not a quantity kind) is allowed.
|
||||
The result is a quantity kind with the appropiate dimension
|
||||
and related to the original quantity kind.
|
||||
|
||||
struct height : kind<height, dim_length> {};
|
||||
struct rate_of_climb : derived_kind<rate_of_climb, height, dim_speed> {};
|
||||
|
||||
quantity_kind h(height{}, 100 * m);
|
||||
quantity_point_kind rate = h / (25 * s);
|
||||
// `quantity_point_kind<rate_of_climb, si::metre_per_second, int>(4 * m / s)`
|
||||
|
||||
Quantity points have a more restricted set of operations.
|
||||
Quantity points can't be added together,
|
||||
@ -81,7 +96,7 @@ The result will always be a quantity point of the same dimension:
|
||||
|
||||
.. code-block::
|
||||
:emphasize-lines: 3
|
||||
|
||||
|
||||
Length auto dist1 = 2_q_m;
|
||||
Length auto dist2 = 1_q_m;
|
||||
auto res1 = dist1 - quantity_point{dist2}; // ERROR
|
||||
@ -98,6 +113,9 @@ The result is a relative quantity of the same dimension:
|
||||
|
||||
That's it! You can't multiply nor divide quantity points with anything else.
|
||||
|
||||
The same restrictions of a quantity point with respect to its quantity
|
||||
apply to a quantity point kind with respect to its quantity kind.
|
||||
|
||||
|
||||
Base Dimensions
|
||||
---------------
|
||||
|
72
docs/framework/quantity_kinds.rst
Normal file
72
docs/framework/quantity_kinds.rst
Normal file
@ -0,0 +1,72 @@
|
||||
.. namespace:: units
|
||||
|
||||
Quantity Kinds
|
||||
==============
|
||||
|
||||
A quantity kind is a quantity of more specific usage.
|
||||
It is represented in the library with a `quantity_kind` class template.
|
||||
|
||||
|
||||
.. _quantity-point-construction:
|
||||
|
||||
Kind creation
|
||||
-------------
|
||||
|
||||
We need a `kind` to represent the more specific usage of a quantity.
|
||||
|
||||
struct radius : kind<radius, si::dim_length> {};
|
||||
|
||||
Quantities of kind `radius` represent a radius.
|
||||
They are clearly distinct from more generic usages of length quantities.
|
||||
|
||||
|
||||
Construction
|
||||
------------
|
||||
|
||||
To create the quantity kind object from a `quantity` we just have to pass
|
||||
the value to the `quantity_kind` class template constructor::
|
||||
|
||||
quantity_kind<radius, si::kilometre, double> d(123 * km);
|
||||
|
||||
.. note::
|
||||
|
||||
As the constructor without the kind argument is explicit,
|
||||
the quantity object can be created from a quantity only via
|
||||
`direct initialization <https://en.cppreference.com/w/cpp/language/direct_initialization>`_.
|
||||
This is why the code below using
|
||||
`copy initialization <https://en.cppreference.com/w/cpp/language/copy_initialization>`_
|
||||
**does not compile**::
|
||||
|
||||
quantity_kind<radius, si::kilometre, double> d = 123 * km; // ERROR
|
||||
|
||||
|
||||
Differences to quantity
|
||||
-----------------------
|
||||
|
||||
Unlike `quantity`, the library provides:
|
||||
|
||||
- no kinds, such as ``radius`` or ``width``, therefore
|
||||
* no UDLs or unit constants,
|
||||
* no kind-specific concepts, such as ``Radius``,
|
||||
(there's the generic `QuantityKind` and kind-specifiable `QuantityKindOf`),
|
||||
- a slightly different set of operations on quantity kinds
|
||||
(see the :ref:`Dimensions` chapter)
|
||||
|
||||
Quantity point kind
|
||||
-------------------
|
||||
|
||||
A quantity point kind is the analogous of a quantity point for quantity kinds.
|
||||
(see the :ref:`Quantity Points` chapter).
|
||||
|
||||
They are represented in the library with a `quantity_point_kind` class template.
|
||||
|
||||
First, we need a `point_kind` for a `kind`.
|
||||
|
||||
struct width : kind<width, si::dim_length> {};
|
||||
struct x_coordinate : point_kind<x_coordinate, width> {};
|
||||
|
||||
Now x coordinates can be constructed:
|
||||
|
||||
quantity_point_kind<x_coordinate, si::metre, int> auto x_pos(123 * m);
|
||||
// `QuantityPointKindOf<x_coordinate>` with `x_pos.relative()`
|
||||
// equal to `quantity_kind<width, si::metre, int>(123 * m)`
|
@ -40,4 +40,4 @@ Unlike `quantity`, the library provides:
|
||||
- no dimension-specific concepts, such as ``LengthPoint``
|
||||
(there's the dimension-agnostic `QuantityPoint`),
|
||||
- a more limited set of operations on quantity points
|
||||
(see the :ref:`Conversions and Casting` chapter)
|
||||
(see the :ref:`Dimensions` chapter)
|
||||
|
@ -8,13 +8,14 @@ also tries really hard to print any quantity in the most user friendly way.
|
||||
|
||||
.. note::
|
||||
|
||||
The library provides no text output for quantity points.
|
||||
The library provides no text output for
|
||||
quantity points or quantity (point) kinds.
|
||||
|
||||
Output Streams
|
||||
--------------
|
||||
|
||||
.. tip::
|
||||
|
||||
|
||||
The streaming support is provided via the ``<units/quantity_io.h>`` header
|
||||
file.
|
||||
|
||||
|
@ -18,7 +18,7 @@ This repository contains three independent CMake-based projects:
|
||||
but until then it depends on:
|
||||
|
||||
- `{fmt} <https://github.com/fmtlib/fmt>`_ to provide text formatting of quantities.
|
||||
- `gsl-lite <https://github.com/gsl-lite/gsl-lite>`_ to verify runtime contracts with ``Expects`` macro.
|
||||
- `gsl-lite <https://github.com/gsl-lite/gsl-lite>`_ to verify runtime contracts with the ``gsl_Expects`` macro.
|
||||
|
||||
- *.*
|
||||
|
||||
|
@ -4,8 +4,12 @@ Working with Legacy Interfaces
|
||||
==============================
|
||||
|
||||
In case we are working with a legacy/unsafe interface we may be forced to
|
||||
extract the :term:`value of a quantity` with :func:`quantity::count()` or to
|
||||
extract the value of a `quantity_point` with :func:`quantity_point::relative()`
|
||||
extract the :term:`value of a quantity` with :func:`quantity::count()`
|
||||
(in addition
|
||||
to the quantity of a `quantity_point` with :func:`quantity_point::relative()`,
|
||||
or the quantity of a `quantity_kind` with :func:`quantity_kind::common()`,
|
||||
or the quantity kind of a `quantity_point_kind`
|
||||
with :func:`quantity_point_kind::relative()`)
|
||||
and pass it to the library's output:
|
||||
|
||||
.. code-block::
|
||||
|
@ -24,130 +24,17 @@
|
||||
#include <units/math.h>
|
||||
#include <units/physical/si/international/base/length.h>
|
||||
#include <units/physical/si/derived/speed.h>
|
||||
#include <units/quantity_point.h>
|
||||
#include <units/quantity_point_kind.h>
|
||||
#include <array>
|
||||
#include <compare>
|
||||
#include <iostream>
|
||||
|
||||
// horizontal/vertical vector
|
||||
namespace {
|
||||
|
||||
using namespace units;
|
||||
|
||||
enum class direction {
|
||||
horizontal,
|
||||
vertical
|
||||
};
|
||||
|
||||
template<typename Q, direction D>
|
||||
requires Quantity<Q> || QuantityPoint<Q>
|
||||
class vector {
|
||||
public:
|
||||
using value_type = Q;
|
||||
using magnitude_type = Q;
|
||||
static constexpr direction dir = D;
|
||||
|
||||
vector() = default;
|
||||
explicit constexpr vector(const Q& m): magnitude_(m) {}
|
||||
|
||||
template<Quantity QQ>
|
||||
requires QuantityPoint<Q> && std::constructible_from<Q, QQ>
|
||||
explicit constexpr vector(const QQ& q) : magnitude_(q) {}
|
||||
|
||||
constexpr Q magnitude() const { return magnitude_; }
|
||||
|
||||
[[nodiscard]] constexpr vector operator-() const
|
||||
requires requires { -magnitude(); }
|
||||
{
|
||||
return vector(-magnitude());
|
||||
}
|
||||
|
||||
template<typename Q2>
|
||||
constexpr vector& operator-=(const vector<Q2, D>& v)
|
||||
requires requires(Q q) { q -= v.magnitude(); }
|
||||
{
|
||||
magnitude_ -= v.magnitude();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Q2>
|
||||
[[nodiscard]] friend constexpr auto operator+(const vector& lhs, const vector<Q2, D>& rhs)
|
||||
requires requires { lhs.magnitude() + rhs.magnitude(); }
|
||||
{
|
||||
using ret_type = decltype(lhs.magnitude() + rhs.magnitude());
|
||||
return vector<ret_type, D>(lhs.magnitude() + rhs.magnitude());
|
||||
}
|
||||
|
||||
template<typename Q2>
|
||||
[[nodiscard]] friend constexpr auto operator-(const vector& lhs, const vector<Q2, D>& rhs)
|
||||
requires requires { lhs.magnitude() - rhs.magnitude(); }
|
||||
{
|
||||
using ret_type = decltype(lhs.magnitude() - rhs.magnitude());
|
||||
return vector<ret_type, D>(lhs.magnitude() - rhs.magnitude());
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
requires (QuantityValue<V> || Dimensionless<V>)
|
||||
[[nodiscard]] friend constexpr auto operator*(const vector& lhs, const V& value)
|
||||
requires requires { lhs.magnitude() * value; }
|
||||
{
|
||||
return vector<Q, D>(lhs.magnitude() * value);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
requires (QuantityValue<V> || Dimensionless<V>)
|
||||
[[nodiscard]] friend constexpr auto operator*(const V& value, const vector& rhs)
|
||||
requires requires { value * rhs.magnitude(); }
|
||||
{
|
||||
return vector<Q, D>(value * rhs.magnitude());
|
||||
}
|
||||
|
||||
template<typename Q2, direction D2>
|
||||
[[nodiscard]] friend constexpr auto operator/(const vector& lhs, const vector<Q2, D2>& rhs)
|
||||
requires requires { lhs.magnitude() / rhs.magnitude(); }
|
||||
{
|
||||
return lhs.magnitude() / rhs.magnitude();
|
||||
}
|
||||
|
||||
template<typename Q2>
|
||||
[[nodiscard]] friend constexpr auto operator<=>(const vector& lhs, const vector<Q2, D>& rhs)
|
||||
requires requires { lhs.magnitude() <=> rhs.magnitude(); }
|
||||
{
|
||||
return lhs.magnitude() <=> rhs.magnitude();
|
||||
}
|
||||
|
||||
template<typename Q2>
|
||||
[[nodiscard]] friend constexpr bool operator==(const vector& lhs, const vector<Q2, D>& rhs)
|
||||
requires requires { lhs.magnitude() == rhs.magnitude(); }
|
||||
{
|
||||
return lhs.magnitude() == rhs.magnitude();
|
||||
}
|
||||
|
||||
template<class CharT, class Traits>
|
||||
requires Quantity<Q>
|
||||
friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const vector& v)
|
||||
{
|
||||
return os << v.magnitude();
|
||||
}
|
||||
|
||||
private:
|
||||
Q magnitude_{};
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_vector = false;
|
||||
template<typename Q, direction D>
|
||||
inline constexpr bool is_vector<vector<Q, D>> = true;
|
||||
|
||||
} // namespace
|
||||
|
||||
template<Quantity Q, direction D>
|
||||
struct fmt::formatter<vector<Q, D>> : formatter<Q> {
|
||||
template<units::QuantityKind QK>
|
||||
struct fmt::formatter<QK> : formatter<typename QK::quantity_type> {
|
||||
template <typename FormatContext>
|
||||
auto format(const vector<Q, D>& v, FormatContext& ctx)
|
||||
auto format(const QK& v, FormatContext& ctx)
|
||||
{
|
||||
return formatter<Q>::format(v.magnitude(), ctx);
|
||||
return formatter<typename QK::quantity_type>::format(v.common(), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
@ -155,20 +42,27 @@ struct fmt::formatter<vector<Q, D>> : formatter<Q> {
|
||||
namespace {
|
||||
|
||||
using namespace units::physical;
|
||||
using namespace units;
|
||||
|
||||
using distance = vector<si::length<si::kilometre>, direction::horizontal>;
|
||||
using height = vector<si::length<si::metre>, direction::vertical>;
|
||||
using altitude = vector<quantity_point<si::dim_length, si::metre>, direction::vertical>;
|
||||
struct horizontal_vector : kind<horizontal_vector, si::dim_length> {};
|
||||
struct vertical_vector : kind<vertical_vector, si::dim_length> {};
|
||||
struct vertical_point : point_kind<vertical_point, vertical_vector> {};
|
||||
struct velocity_vector : derived_kind<velocity_vector, horizontal_vector, si::dim_speed> {};
|
||||
struct rate_of_climb_vector : derived_kind<rate_of_climb_vector, vertical_vector, si::dim_speed> {};
|
||||
|
||||
using distance = quantity_kind<horizontal_vector, si::kilometre>;
|
||||
using height = quantity_kind<vertical_vector, si::metre>;
|
||||
using altitude = quantity_point_kind<vertical_point, si::metre>;
|
||||
|
||||
using duration = si::time<si::second>;
|
||||
|
||||
using velocity = vector<si::speed<si::kilometre_per_hour>, direction::horizontal>;
|
||||
using rate_of_climb = vector<si::speed<si::metre_per_second>, direction::vertical>;
|
||||
using velocity = quantity_kind<velocity_vector, si::kilometre_per_hour>;
|
||||
using rate_of_climb = quantity_kind<rate_of_climb_vector, si::metre_per_second>;
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const altitude& a)
|
||||
{
|
||||
return os << a.magnitude().relative() << " AMSL";
|
||||
return os << a.relative().common() << " AMSL";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -178,7 +72,7 @@ struct fmt::formatter<altitude> : formatter<si::length<si::metre>> {
|
||||
template <typename FormatContext>
|
||||
auto format(altitude a, FormatContext& ctx)
|
||||
{
|
||||
formatter<si::length<si::metre>>::format(a.magnitude().relative(), ctx);
|
||||
formatter<si::length<si::metre>>::format(a.relative().common(), ctx);
|
||||
return format_to(ctx.out(), " AMSL");
|
||||
}
|
||||
};
|
||||
@ -201,8 +95,15 @@ struct fmt::formatter<altitude> : formatter<si::length<si::metre>> {
|
||||
// gliders database
|
||||
namespace {
|
||||
|
||||
using namespace units::physical::si::literals;
|
||||
using namespace units::physical::si::international::literals;
|
||||
using namespace si::literals;
|
||||
using namespace si::international::literals;
|
||||
using namespace si::unit_constants;
|
||||
|
||||
template<QuantityKind QK1, QuantityKind QK2>
|
||||
constexpr Quantity auto operator/(const QK1& lhs, const QK2& rhs)
|
||||
requires requires { lhs.common() / rhs.common(); } {
|
||||
return lhs.common() / rhs.common();
|
||||
}
|
||||
|
||||
struct glider {
|
||||
struct polar_point {
|
||||
@ -217,10 +118,10 @@ struct glider {
|
||||
auto get_gliders()
|
||||
{
|
||||
const std::array gliders = {
|
||||
glider{"SZD-30 Pirat", {velocity(83_q_km_per_h), rate_of_climb(-0.7389_q_m_per_s)}},
|
||||
glider{"SZD-51 Junior", {velocity(80_q_km_per_h), rate_of_climb(-0.6349_q_m_per_s)}},
|
||||
glider{"SZD-48 Jantar Std 3", {velocity(110_q_km_per_h), rate_of_climb(-0.77355_q_m_per_s)}},
|
||||
glider{"SZD-56 Diana", {velocity(110_q_km_per_h), rate_of_climb(-0.63657_q_m_per_s)}}
|
||||
glider{"SZD-30 Pirat", {velocity(83 * km / h), rate_of_climb(-0.7389 * m / s)}},
|
||||
glider{"SZD-51 Junior", {velocity(80 * km / h), rate_of_climb(-0.6349 * m / s)}},
|
||||
glider{"SZD-48 Jantar Std 3", {velocity(110 * km / h), rate_of_climb(-0.77355 * m / s)}},
|
||||
glider{"SZD-56 Diana", {velocity(110 * km / h), rate_of_climb(-0.63657 * m / s)}}
|
||||
};
|
||||
return gliders;
|
||||
}
|
||||
@ -235,7 +136,7 @@ template<std::ranges::forward_range R>
|
||||
void print(const R& gliders)
|
||||
{
|
||||
std::cout << "Gliders:\n";
|
||||
std::cout << "========\n";
|
||||
std::cout << "========\n";
|
||||
for(const auto& g : gliders) {
|
||||
std::cout << "- Name: " << g.name << "\n";
|
||||
std::cout << "- Polar:\n";
|
||||
@ -258,9 +159,9 @@ struct weather {
|
||||
auto get_weather_conditions()
|
||||
{
|
||||
const std::array weather_conditions = {
|
||||
std::pair("Good", weather{height(1900_q_m), rate_of_climb(4.3_q_m_per_s)}),
|
||||
std::pair("Medium", weather{height(1550_q_m), rate_of_climb(2.8_q_m_per_s)}),
|
||||
std::pair("Bad", weather{height(850_q_m), rate_of_climb(1.8_q_m_per_s)})
|
||||
std::pair("Good", weather{height(1900 * m), rate_of_climb(4.3 * m / s)}),
|
||||
std::pair("Medium", weather{height(1550 * m), rate_of_climb(2.8 * m / s)}),
|
||||
std::pair("Bad", weather{height(850 * m), rate_of_climb(1.8 * m / s)})
|
||||
};
|
||||
return weather_conditions;
|
||||
}
|
||||
@ -270,7 +171,7 @@ template<std::ranges::forward_range R>
|
||||
void print(const R& conditions)
|
||||
{
|
||||
std::cout << "Weather:\n";
|
||||
std::cout << "========\n";
|
||||
std::cout << "========\n";
|
||||
for(const auto& c : conditions) {
|
||||
std::cout << "- Kind: " << c.first << "\n";
|
||||
const auto& w = c.second;
|
||||
@ -409,12 +310,12 @@ flight_point circle(const flight_point& point, const glider& g, const weather& w
|
||||
constexpr distance glide_distance(const flight_point& point, const glider& g, const task& t, const safety& s, altitude ground_alt)
|
||||
{
|
||||
const auto dist_to_finish = t.dist - point.dist;
|
||||
return distance((ground_alt + s.min_agl_height - point.alt).magnitude() / ((ground_alt - t.finish.alt) / dist_to_finish - 1 / glide_ratio(g.polar[0])));
|
||||
return distance((ground_alt + s.min_agl_height - point.alt).common() / ((ground_alt - t.finish.alt) / dist_to_finish - 1 / glide_ratio(g.polar[0])));
|
||||
}
|
||||
|
||||
inline si::length<si::kilometre> length_3d(distance dist, height h)
|
||||
{
|
||||
return sqrt(pow<2>(dist.magnitude()) + pow<2>(h.magnitude()));
|
||||
return sqrt(pow<2>(dist.common()) + pow<2>(h.common()));
|
||||
}
|
||||
|
||||
flight_point glide(const flight_point& point, const glider& g, const task& t, const safety& s)
|
||||
@ -423,7 +324,7 @@ flight_point glide(const flight_point& point, const glider& g, const task& t, co
|
||||
const auto dist = glide_distance(point, g, t, s, ground_alt);
|
||||
const auto alt = ground_alt + s.min_agl_height;
|
||||
const auto l3d = length_3d(dist, point.alt - alt);
|
||||
const duration d = l3d / g.polar[0].v.magnitude();
|
||||
const duration d = l3d / g.polar[0].v.common();
|
||||
const flight_point new_point{point.dur + d, point.dist + dist, terrain_level_alt(t, point.dist + dist) + s.min_agl_height};
|
||||
|
||||
print("Glide", point, new_point);
|
||||
@ -434,7 +335,7 @@ flight_point final_glide(const flight_point& point, const glider& g, const task&
|
||||
{
|
||||
const auto dist = t.dist - point.dist;
|
||||
const auto l3d = length_3d(dist, point.alt - t.finish.alt);
|
||||
const duration d = l3d / g.polar[0].v.magnitude();
|
||||
const duration d = l3d / g.polar[0].v.common();
|
||||
const flight_point new_point{point.dur + d, point.dist + dist, t.finish.alt};
|
||||
|
||||
print("Final Glide", point, new_point);
|
||||
@ -450,7 +351,7 @@ void estimate(const glider& g, const weather& w, const task& t, const safety& s,
|
||||
point = tow(point, at);
|
||||
|
||||
// estimate the altitude needed to reach the finish line from this place
|
||||
const altitude final_glide_alt = t.finish.alt + height(t.dist.magnitude() / glide_ratio(g.polar[0]));
|
||||
const altitude final_glide_alt = t.finish.alt + height(t.dist.common() / glide_ratio(g.polar[0]));
|
||||
|
||||
// how much height we still need to gain in the thermalls to reach the destination?
|
||||
height height_to_gain = final_glide_alt - point.alt;
|
||||
@ -462,7 +363,7 @@ void estimate(const glider& g, const weather& w, const task& t, const safety& s,
|
||||
// circle in a thermall to gain height
|
||||
point = circle(point, g, w, t, height_to_gain);
|
||||
}
|
||||
while(height_to_gain > height(0_q_m));
|
||||
while(height_to_gain > height(0 * m));
|
||||
|
||||
// final glide
|
||||
point = final_glide(point, g, t);
|
||||
@ -488,10 +389,10 @@ void example()
|
||||
};
|
||||
print(t);
|
||||
|
||||
const safety s = {height(300_q_m)};
|
||||
const safety s = {height(300 * m)};
|
||||
print(s);
|
||||
|
||||
const aircraft_tow tow = {height(400_q_m), rate_of_climb(1.6_q_m_per_s)};
|
||||
const aircraft_tow tow = {height(400 * m), rate_of_climb(1.6 * m / ::s)};
|
||||
print(tow);
|
||||
|
||||
for(const auto& g : gliders) {
|
||||
|
@ -202,7 +202,51 @@ concept UnitOf =
|
||||
Dimension<D> &&
|
||||
std::same_as<typename U::reference, typename dimension_unit<D>::reference>;
|
||||
|
||||
// Quantity, QuantityPoint
|
||||
// Kind
|
||||
namespace detail {
|
||||
|
||||
template<typename, Dimension>
|
||||
struct _kind_base;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, template<typename...> typename Base>
|
||||
concept kind_impl_ =
|
||||
is_derived_from_specialization_of<T, Base> &&
|
||||
requires(T* t) {
|
||||
typename T::base_kind;
|
||||
typename T::dimension;
|
||||
requires Dimension<typename T::dimension>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A concept matching all kind types
|
||||
*
|
||||
* Satisfied by all kind types derived from an specialization of :class:`kind`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept Kind =
|
||||
kind_impl_<T, detail::_kind_base> &&
|
||||
kind_impl_<typename T::base_kind, detail::_kind_base> &&
|
||||
std::same_as<typename T::base_kind, typename T::base_kind::base_kind>;
|
||||
|
||||
// PointKind
|
||||
namespace detail {
|
||||
|
||||
template<Kind>
|
||||
struct _point_kind_base;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A concept matching all point kind types
|
||||
*
|
||||
* Satisfied by all point kind types derived from an specialization of :class:`point_kind`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept PointKind = kind_impl_<T, detail::_point_kind_base>;
|
||||
|
||||
// Quantity, QuantityPoint, QuantityKind, QuantityPointKind
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
@ -211,6 +255,12 @@ inline constexpr bool is_quantity = false;
|
||||
template<typename T>
|
||||
inline constexpr bool is_quantity_point = false;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_quantity_kind = false;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_quantity_point_kind = false;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
@ -229,9 +279,27 @@ concept Quantity = detail::is_quantity<T>;
|
||||
template<typename T>
|
||||
concept QuantityPoint = detail::is_quantity_point<T>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity kinds in the library.
|
||||
*
|
||||
* Satisfied by all specializations of @c quantity_kind.
|
||||
*/
|
||||
template<typename T>
|
||||
concept QuantityKind = detail::is_quantity_kind<T>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity point kinds in the library.
|
||||
*
|
||||
* Satisfied by all specializations of @c quantity_point_kind.
|
||||
*/
|
||||
template<typename T>
|
||||
concept QuantityPointKind = detail::is_quantity_point_kind<T>;
|
||||
|
||||
// QuantityLike
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity-like types (other than specialization of @c quantity)
|
||||
*
|
||||
*
|
||||
* Satisfied by all types for which a correct specialization of `quantity_like_traits`
|
||||
* type trait is provided.
|
||||
*/
|
||||
@ -261,12 +329,12 @@ concept castable_number_ = // exposition only
|
||||
common_type_with_<T, std::intmax_t> &&
|
||||
scalable_number_<std::common_type_t<T, std::intmax_t>>;
|
||||
|
||||
template<typename T>
|
||||
template<typename T>
|
||||
concept scalable_ = // exposition only
|
||||
castable_number_<T> ||
|
||||
(requires { typename T::value_type; } && castable_number_<typename T::value_type> && scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
|
||||
|
||||
template<typename T, typename U>
|
||||
template<typename T, typename U>
|
||||
concept scalable_with_ = // exposition only
|
||||
common_type_with_<T, U> &&
|
||||
scalable_<std::common_type_t<T, U>>;
|
||||
@ -281,6 +349,10 @@ template<typename T>
|
||||
requires requires { typename T::value_type; }
|
||||
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::value_type> || QuantityLike<typename T::value_type> || is_wrapped_quantity<typename T::value_type>;
|
||||
|
||||
template<typename T>
|
||||
requires requires { typename T::quantity_type; }
|
||||
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::quantity_type>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
|
@ -34,6 +34,12 @@ class quantity;
|
||||
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
|
||||
class quantity_point;
|
||||
|
||||
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep>
|
||||
class quantity_kind;
|
||||
|
||||
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep>
|
||||
class quantity_point_kind;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename Q1, typename Q2, typename Rep>
|
||||
@ -65,33 +71,53 @@ struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep>
|
||||
using type = quantity<dimension, unit, Rep>;
|
||||
};
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
quantity_point<D, U, Rep> common_quantity_point_impl(quantity<D, U, Rep>);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2, QuantityValue Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
|
||||
using common_quantity = TYPENAME detail::common_quantity_impl<Q1, Q2, Rep>::type;
|
||||
|
||||
template<QuantityPoint QP1, QuantityPoint QP2>
|
||||
requires requires { typename common_quantity<typename QP1::quantity_type, typename QP2::quantity_type>; }
|
||||
using common_quantity_point = decltype(
|
||||
detail::common_quantity_point_impl(common_quantity<typename QP1::quantity_type, typename QP2::quantity_type>{}));
|
||||
template<QuantityPoint QP1, QuantityPointEquivalentTo<QP1> QP2>
|
||||
using common_quantity_point = std::common_type_t<QP1, QP2>;
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
using common_quantity_kind = std::common_type_t<QK1, QK2>;
|
||||
|
||||
template<QuantityPointKind QPK1, QuantityPointKindEquivalentTo<QPK1> QPK2>
|
||||
using common_quantity_point_kind = std::common_type_t<QPK1, QPK2>;
|
||||
|
||||
} // namespace units
|
||||
|
||||
namespace std {
|
||||
|
||||
template<units::Quantity Q1, units::Quantity Q2>
|
||||
requires units::equivalent<typename Q1::dimension, typename Q2::dimension>
|
||||
template<units::Quantity Q1, units::QuantityEquivalentTo<Q1> Q2>
|
||||
requires requires { typename common_type_t<typename Q1::rep, typename Q2::rep>; }
|
||||
struct common_type<Q1, Q2> {
|
||||
using type = units::common_quantity<Q1, Q2>;
|
||||
};
|
||||
|
||||
template<units::QuantityPoint QP1, units::QuantityPoint QP2>
|
||||
requires requires { typename units::common_quantity_point<QP1, QP2>; }
|
||||
template<units::QuantityPoint QP1, units::QuantityPointEquivalentTo<QP1> QP2>
|
||||
requires requires { typename common_type_t<typename QP1::rep, typename QP2::rep>; }
|
||||
struct common_type<QP1, QP2> {
|
||||
using type = units::common_quantity_point<QP1, QP2>;
|
||||
using type = units::quantity_point<
|
||||
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::dimension,
|
||||
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::unit,
|
||||
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::rep>;
|
||||
};
|
||||
|
||||
template<units::QuantityKind QK1, units::QuantityKindEquivalentTo<QK1> QK2>
|
||||
requires requires { typename common_type_t<typename QK1::rep, typename QK2::rep>; }
|
||||
struct common_type<QK1, QK2> {
|
||||
using type = units::quantity_kind<typename QK1::kind_type,
|
||||
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::unit,
|
||||
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::rep>;
|
||||
};
|
||||
|
||||
template<units::QuantityPointKind QPK1, units::QuantityPointKindEquivalentTo<QPK1> QPK2>
|
||||
requires requires { typename common_type_t<typename QPK1::rep, typename QPK2::rep>; }
|
||||
struct common_type<QPK1, QPK2> {
|
||||
using type = units::quantity_point_kind<typename QPK1::point_kind_type,
|
||||
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::unit,
|
||||
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::rep>;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ struct equivalent_impl<T, T> : std::true_type {
|
||||
// units
|
||||
|
||||
template<Unit U1, Unit U2>
|
||||
struct equivalent_impl<U1, U2> : std::is_base_of<U1, U2>, std::is_base_of<U2, U1> {};
|
||||
struct equivalent_impl<U1, U2> : std::disjunction<std::is_base_of<U1, U2>, std::is_base_of<U2, U1>> {};
|
||||
|
||||
|
||||
// dimensions
|
||||
@ -81,7 +81,17 @@ template<Unit U1, Dimension D1, Unit U2, Dimension D2>
|
||||
struct equivalent_unit : std::disjunction<equivalent_impl<U1, U2>,
|
||||
std::bool_constant<U1::ratio / dimension_unit<D1>::ratio == U2::ratio / dimension_unit<D2>::ratio>> {};
|
||||
|
||||
// quantities and quantity points
|
||||
|
||||
// (point) kinds
|
||||
|
||||
template<typename T, typename U>
|
||||
requires (Kind<T> && Kind<U>) || (PointKind<T> && PointKind<U>)
|
||||
struct equivalent_impl<T, U> :
|
||||
std::conjunction<std::is_same<typename T::base_kind, typename U::base_kind>,
|
||||
equivalent_impl<typename T::dimension, typename U::dimension>> {};
|
||||
|
||||
|
||||
// quantities, quantity points, quantity (point) kinds
|
||||
|
||||
template<typename Q1, typename Q2>
|
||||
requires (Quantity<Q1> && Quantity<Q2>) || (QuantityPoint<Q1> && QuantityPoint<Q2>)
|
||||
@ -89,6 +99,11 @@ struct equivalent_impl<Q1, Q2> : std::conjunction<equivalent_impl<typename Q1::d
|
||||
equivalent_unit<typename Q1::unit, typename Q1::dimension,
|
||||
typename Q2::unit, typename Q2::dimension>> {};
|
||||
|
||||
template<typename QK1, typename QK2>
|
||||
requires (QuantityKind<QK1> && QuantityKind<QK2>) || (QuantityPointKind<QK1> && QuantityPointKind<QK2>)
|
||||
struct equivalent_impl<QK1, QK2> : std::conjunction<equivalent_impl<typename QK1::kind_type, typename QK2::kind_type>,
|
||||
equivalent_impl<typename QK1::quantity_type, typename QK2::quantity_type>> {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, typename U>
|
||||
|
@ -79,4 +79,41 @@ concept QuantityOf = Quantity<Q> && Dimension<Dim> && equivalent<typename Q::dim
|
||||
template<typename Q1, typename Q2>
|
||||
concept QuantityEquivalentTo = Quantity<Q1> && QuantityOf<Q2, typename Q1::dimension>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity points with provided dimension
|
||||
*
|
||||
* Satisfied by all quantity points with a dimension being the instantiation derived from
|
||||
* the provided dimension type.
|
||||
*/
|
||||
template<typename QP, typename Dim>
|
||||
concept QuantityPointOf = QuantityPoint<QP> && Dimension<Dim> && equivalent<typename QP::dimension, Dim>;
|
||||
|
||||
template<typename QP1, typename QP2>
|
||||
concept QuantityPointEquivalentTo = QuantityPoint<QP1> && QuantityPointOf<QP2, typename QP1::dimension>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching only quantity kinds of a specific kind.
|
||||
*
|
||||
* @tparam QK Quantity kind to verify.
|
||||
* @tparam K Kind type to use for verification.
|
||||
*/
|
||||
template<typename QK, typename K>
|
||||
concept QuantityKindOf = QuantityKind<QK> && Kind<K> && equivalent<typename QK::kind_type, K>;
|
||||
|
||||
template<typename QK1, typename QK2>
|
||||
concept QuantityKindEquivalentTo = QuantityKind<QK1> && QuantityKindOf<QK2, typename QK1::kind_type>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching only quantity point kinds of a specific point kind.
|
||||
*
|
||||
* @tparam QPK Quantity point kind to verify.
|
||||
* @tparam PK Point kind type to use for verification.
|
||||
*/
|
||||
template<typename QPK, typename PK>
|
||||
concept QuantityPointKindOf = QuantityPointKind<QPK> && PointKind<PK> && equivalent<typename QPK::point_kind_type, PK>;
|
||||
|
||||
template<typename QPK1, typename QPK2>
|
||||
concept QuantityPointKindEquivalentTo =
|
||||
QuantityPointKind<QPK1> && QuantityPointKindOf<QPK2, typename QPK1::point_kind_type>;
|
||||
|
||||
} // namespace units
|
||||
|
64
src/include/units/kind.h
Normal file
64
src/include/units/kind.h
Normal file
@ -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 <units/bits/basic_concepts.h>
|
||||
#include <units/bits/external/downcasting.h>
|
||||
|
||||
namespace units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename K, Dimension D>
|
||||
struct _kind_base : downcast_base<_kind_base<K, D>> {
|
||||
using base_kind = K;
|
||||
using dimension = D;
|
||||
};
|
||||
|
||||
template<Kind K>
|
||||
struct _point_kind_base : downcast_base<_point_kind_base<K>> {
|
||||
using base_kind = K;
|
||||
using dimension = typename K::dimension;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<Kind K, Dimension D>
|
||||
requires Kind<downcast<detail::_kind_base<typename K::base_kind, D>>>
|
||||
using downcast_kind = downcast<detail::_kind_base<typename K::base_kind, D>>;
|
||||
|
||||
template<Kind K>
|
||||
requires PointKind<downcast<detail::_point_kind_base<K>>>
|
||||
using downcast_point_kind = downcast<detail::_point_kind_base<K>>;
|
||||
|
||||
template<typename K, Dimension D>
|
||||
struct kind : downcast_dispatch<K, detail::_kind_base<K, D>> {};
|
||||
|
||||
template<typename DK, Kind BK, Dimension D>
|
||||
requires std::same_as<BK, typename BK::base_kind>
|
||||
struct derived_kind : downcast_dispatch<DK, detail::_kind_base<BK, D>> {};
|
||||
|
||||
template<typename DPK, Kind BK>
|
||||
struct point_kind : downcast_dispatch<DPK, detail::_point_kind_base<BK>> {};
|
||||
|
||||
} // namespace units
|
@ -42,6 +42,12 @@ class quantity;
|
||||
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
|
||||
class quantity_point;
|
||||
|
||||
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep>
|
||||
class quantity_kind;
|
||||
|
||||
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep>
|
||||
class quantity_point_kind;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
@ -80,7 +86,7 @@ struct cast_traits<From, To> {
|
||||
|
||||
template<typename From, typename To>
|
||||
requires (!common_type_with_<std::common_type_t<From, To>, std::intmax_t>) &&
|
||||
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
|
||||
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
|
||||
requires { typename std::common_type_t<From, To>::value_type; } &&
|
||||
common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>
|
||||
struct cast_traits<From, To> {
|
||||
@ -103,7 +109,7 @@ struct cast_traits<From, To> {
|
||||
* @tparam To a target quantity type to cast to
|
||||
*/
|
||||
template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep>
|
||||
requires QuantityOf<To, D>
|
||||
requires QuantityOf<To, D> && std::constructible_from<typename To::rep, std::common_type_t<typename To::rep, Rep>>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
using traits = detail::cast_traits<Rep, typename To::rep>;
|
||||
@ -137,7 +143,7 @@ template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> R
|
||||
*
|
||||
* This cast gets only the target dimension to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::physical::si::acceleration>(200_q_Gal);
|
||||
* auto q1 = units::quantity_cast<units::physical::si::dim_acceleration>(200_q_Gal);
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
*/
|
||||
@ -179,7 +185,7 @@ template<Unit ToU, typename D, typename U, typename Rep>
|
||||
*
|
||||
* @note This cast is especially useful when working with quantities of unknown dimensions
|
||||
* (@c unknown_dimension).
|
||||
*
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
@ -203,6 +209,7 @@ template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<QuantityValue ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
|
||||
requires std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<D, U, ToRep>>(q);
|
||||
@ -218,7 +225,7 @@ template<QuantityValue ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
|
||||
*
|
||||
* auto q1 = units::quantity_point_cast<decltype(quantity_point{0_q_s})>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::time<units::physical::si::second>>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::acceleration>(quantity_point{200_q_Gal});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::dim_acceleration>(quantity_point{200_q_Gal});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::second>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<int>(quantity_point{1_q_ms});
|
||||
*
|
||||
@ -247,7 +254,7 @@ template<typename CastSpec, typename D, typename U, typename Rep>
|
||||
*
|
||||
* @note This cast is especially useful when working with quantity points of unknown dimensions
|
||||
* (@c unknown_dimension).
|
||||
*
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
@ -258,6 +265,119 @@ template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
|
||||
return quantity_point_cast<quantity_point<ToD, ToU, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity kind
|
||||
*
|
||||
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_kind_cast<decltype(ns::width{1 * m})>(quantity_kind{ns::width{1 * mm});
|
||||
* auto q1 = units::quantity_kind_cast<ns::height_kind>(ns::width{1 * m});
|
||||
* auto q1 = units::quantity_kind_cast<units::physical::si::length<units::physical::si::metre>>(ns::width{1 * mm});
|
||||
* auto q1 = units::quantity_kind_cast<units::physical::si::dim_acceleration>(ns::rate_of_climb{200 * Gal});
|
||||
* auto q1 = units::quantity_kind_cast<units::physical::si::metre>(ns::width{1 * mm});
|
||||
* auto q1 = units::quantity_kind_cast<int>(ns::width{1.0 * mm});
|
||||
*
|
||||
* @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast
|
||||
*/
|
||||
template<typename CastSpec, typename K, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
|
||||
requires (is_specialization_of<CastSpec, quantity_kind> &&
|
||||
requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); }) ||
|
||||
(Kind<CastSpec> && UnitOf<U, typename CastSpec::dimension>) ||
|
||||
requires { quantity_cast<CastSpec>(qk.common()); }
|
||||
{
|
||||
if constexpr (is_specialization_of<CastSpec, quantity_kind>)
|
||||
return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common()));
|
||||
else if constexpr (Kind<CastSpec>)
|
||||
return quantity_kind<CastSpec, U, Rep>(qk.common());
|
||||
else {
|
||||
auto q{quantity_cast<CastSpec>(qk.common())};
|
||||
using Q = decltype(q);
|
||||
return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity kind
|
||||
*
|
||||
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets both the target kind and unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_kind_cast<ns::height_kind, units::physical::si::kilometre>(w);
|
||||
*
|
||||
* @note This cast is especially useful when working with quantity kinds of unknown kind.
|
||||
*
|
||||
* @tparam ToK the kind type to use for the target quantity
|
||||
* @tparam ToU the unit type to use for the target quantity
|
||||
*/
|
||||
template<Kind ToK, Unit ToU, typename K, typename U, typename Rep>
|
||||
requires equivalent<typename ToK::dimension, typename K::dimension> && UnitOf<ToU, typename ToK::dimension>
|
||||
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
|
||||
{
|
||||
return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity point kind
|
||||
*
|
||||
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<decltype(ns::width{1 * m})>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind>(ns::x_coordinate{1 * m});
|
||||
* auto q1 = units::quantity_point_kind_cast<ns::height_kind>(ns::x_coordinate{1 * m});
|
||||
* auto q1 = units::quantity_point_kind_cast<units::physical::si::length<units::physical::si::metre>>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<units::physical::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200 * Gal}));
|
||||
* auto q1 = units::quantity_point_kind_cast<units::physical::si::metre>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm});
|
||||
*
|
||||
* @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast
|
||||
*/
|
||||
template<typename CastSpec, typename PK, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
|
||||
requires (is_specialization_of<CastSpec, quantity_point_kind> &&
|
||||
requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); }) ||
|
||||
(PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>) ||
|
||||
requires { quantity_kind_cast<CastSpec>(qpk.relative()); }
|
||||
{
|
||||
if constexpr (is_specialization_of<CastSpec, quantity_point_kind>)
|
||||
return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()));
|
||||
else if constexpr (PointKind<CastSpec>)
|
||||
return quantity_point_kind(quantity_kind_cast<typename CastSpec::base_kind>(qpk.relative()));
|
||||
else
|
||||
return quantity_point_kind(quantity_kind_cast<CastSpec>(qpk.relative()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity point kind
|
||||
*
|
||||
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets both the target point kind and unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind, units::physical::si::kilometre>(x);
|
||||
*
|
||||
* @note This cast is especially useful when working with quantity point kinds of unknown point kind.
|
||||
*
|
||||
* @tparam ToPK the point kind type to use for the target quantity
|
||||
* @tparam ToU the unit type to use for the target quantity
|
||||
*/
|
||||
template<PointKind ToPK, Unit ToU, typename PK, typename U, typename Rep>
|
||||
requires equivalent<typename ToPK::dimension, typename PK::dimension> && UnitOf<ToU, typename ToPK::dimension>
|
||||
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
|
||||
{
|
||||
return quantity_point_kind_cast<quantity_point_kind<ToPK, ToU, Rep>>(qpk);
|
||||
}
|
||||
|
||||
} // namespace units
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
351
src/include/units/quantity_kind.h
Normal file
351
src/include/units/quantity_kind.h
Normal file
@ -0,0 +1,351 @@
|
||||
|
||||
// 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 <units/kind.h>
|
||||
#include <units/quantity.h>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<Kind K>
|
||||
inline constexpr auto make_quantity_kind_fn = [](auto q) {
|
||||
using Q = decltype(q);
|
||||
return quantity_kind<K, typename Q::unit, typename Q::rep>(std::move(q));
|
||||
};
|
||||
|
||||
template<QuantityKind QK>
|
||||
inline constexpr auto& make_quantity_kind = make_quantity_kind_fn<typename QK::kind_type>;
|
||||
|
||||
template<Kind K>
|
||||
inline constexpr auto downcasted_kind_fn = [](auto q) {
|
||||
using Q = decltype(q);
|
||||
return make_quantity_kind_fn<downcast_kind<K, typename Q::dimension>>(std::move(q));
|
||||
};
|
||||
|
||||
template<QuantityKind QK>
|
||||
inline constexpr auto& downcasted_kind = downcasted_kind_fn<typename QK::kind_type::base_kind>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A quantity kind
|
||||
*
|
||||
* A quantity with more specific usage as determined by its kind.
|
||||
* See https://jcgm.bipm.org/vim/en/1.2.html and NOTE 1 at https://jcgm.bipm.org/vim/en/1.1.html.
|
||||
*
|
||||
* @tparam K the kind of quantity
|
||||
* @tparam U the measurement unit of the quantity kind
|
||||
* @tparam Rep the type to be used to represent values of the quantity kind
|
||||
*/
|
||||
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep = double>
|
||||
class quantity_kind {
|
||||
public:
|
||||
using kind_type = K;
|
||||
using quantity_type = quantity<typename K::dimension, U, Rep>;
|
||||
using dimension = typename quantity_type::dimension;
|
||||
using unit = typename quantity_type::unit;
|
||||
using rep = typename quantity_type::rep;
|
||||
|
||||
private:
|
||||
quantity_type q_;
|
||||
|
||||
public:
|
||||
quantity_kind() = default;
|
||||
quantity_kind(const quantity_kind&) = default;
|
||||
quantity_kind(quantity_kind&&) = default;
|
||||
|
||||
template<safe_convertible_to_<rep> Value>
|
||||
requires is_same_v<dimension, dim_one> && std::is_constructible_v<quantity_type, Value>
|
||||
constexpr explicit quantity_kind(const Value& v) : q_(v) {}
|
||||
|
||||
template<typename Q>
|
||||
requires (Quantity<Q> || QuantityLike<Q>) && std::is_constructible_v<quantity_type, Q>
|
||||
constexpr explicit quantity_kind(const Q& q) : q_{q} {}
|
||||
|
||||
template<QuantityKindEquivalentTo<quantity_kind> QK2>
|
||||
requires std::is_convertible_v<typename QK2::quantity_type, quantity_type>
|
||||
constexpr explicit(false) quantity_kind(const QK2& qk) : q_{qk.common()} {}
|
||||
|
||||
quantity_kind& operator=(const quantity_kind&) = default;
|
||||
quantity_kind& operator=(quantity_kind&&) = default;
|
||||
|
||||
[[nodiscard]] constexpr quantity_type common() const noexcept { return q_; }
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind zero() noexcept
|
||||
requires requires { quantity_type::zero(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::zero());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind one() noexcept
|
||||
requires requires { quantity_type::one(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::one());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind min() noexcept
|
||||
requires requires { quantity_type::min(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::min());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind max() noexcept
|
||||
requires requires { quantity_type::max(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::max());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind operator+() const
|
||||
requires requires(quantity_type q) { +q; }
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr QuantityKind auto operator-() const
|
||||
requires requires(quantity_type q) { -q; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(-q_);
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator++()
|
||||
requires requires(quantity_type q) { ++q; }
|
||||
{
|
||||
++q_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind operator++(int)
|
||||
requires requires(quantity_type q) { q++; }
|
||||
{
|
||||
return quantity_kind(q_++);
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator--()
|
||||
requires requires(quantity_type q) { --q; }
|
||||
{
|
||||
--q_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind operator--(int)
|
||||
requires requires(quantity_type q) { q--; }
|
||||
{
|
||||
return quantity_kind(q_--);
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator+=(const quantity_kind& qk)
|
||||
requires requires(quantity_type q) { q += qk.common(); }
|
||||
{
|
||||
q_ += qk.common();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator-=(const quantity_kind& qk)
|
||||
requires requires(quantity_type q) { q -= qk.common(); }
|
||||
{
|
||||
q_ -= qk.common();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Rep2>
|
||||
constexpr quantity_kind& operator*=(const Rep2& rhs)
|
||||
requires requires(quantity_type q) { q *= rhs; }
|
||||
{
|
||||
q_ *= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Rep2>
|
||||
constexpr quantity_kind& operator/=(const Rep2& rhs)
|
||||
requires requires(quantity_type q) { q /= rhs; }
|
||||
{
|
||||
q_ /= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator%=(const rep& rhs)
|
||||
requires requires(quantity_type q) { q %= rhs; }
|
||||
{
|
||||
q_ %= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator%=(const quantity_kind& qk)
|
||||
requires requires(quantity_type q) { q %= qk.common(); }
|
||||
{
|
||||
q_ %= qk.common();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Hidden Friends
|
||||
// Below friend functions are to be found via argument-dependent lookup only
|
||||
[[nodiscard]] friend constexpr quantity_kind operator+(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires requires { lhs.common() + rhs.common(); }
|
||||
{
|
||||
return quantity_kind(lhs.common() + rhs.common());
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr quantity_kind operator-(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires requires { lhs.common() - rhs.common(); }
|
||||
{
|
||||
return quantity_kind(lhs.common() - rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator*(const quantity_kind& qk, const Value& v)
|
||||
requires requires { { qk.common() * v } -> Quantity; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(qk.common() * v);
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator*(const Value& v, const quantity_kind& qk)
|
||||
requires requires { { v * qk.common() } -> Quantity; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(v * qk.common());
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator/(const quantity_kind& qk, const Value& v)
|
||||
requires requires { { qk.common() / v } -> Quantity; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(qk.common() / v);
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator/(const Value& v, const quantity_kind& qk)
|
||||
requires requires { { v / qk.common() } -> Quantity; }
|
||||
{
|
||||
return detail::downcasted_kind<quantity_kind>(v / qk.common());
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator%(const quantity_kind& qk, const Value& v)
|
||||
requires requires { qk.common() % v; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(qk.common() % v);
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr quantity_kind operator%(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires requires { lhs.common() % rhs.common(); }
|
||||
{
|
||||
return quantity_kind(lhs.common() % rhs.common());
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr auto operator<=>(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires std::three_way_comparable<quantity_type>
|
||||
{
|
||||
return lhs.common() <=> rhs.common();
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(const quantity_kind&, const quantity_kind&) = default;
|
||||
};
|
||||
|
||||
template<Kind K, QuantityValue V>
|
||||
explicit(false) quantity_kind(K, V) -> quantity_kind<K, one, V>;
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator+(const QK1& lhs, const QK2& rhs)
|
||||
requires requires { lhs.common() + rhs.common(); }
|
||||
{
|
||||
return detail::make_quantity_kind<QK1>(lhs.common() + rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator-(const QK1& lhs, const QK2& rhs)
|
||||
requires requires { lhs.common() - rhs.common(); }
|
||||
{
|
||||
return detail::make_quantity_kind<QK1>(lhs.common() - rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK, Quantity Q>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator*(const QK& lhs, const Q& rhs)
|
||||
requires requires { lhs.common() * rhs; }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs.common() * rhs);
|
||||
}
|
||||
|
||||
template<Quantity Q, QuantityKind QK>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator*(const Q& lhs, const QK& rhs)
|
||||
requires requires { lhs * rhs.common(); }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs * rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK, Quantity Q>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator/(const QK& lhs, const Q& rhs)
|
||||
requires requires { lhs.common() / rhs; }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs.common() / rhs);
|
||||
}
|
||||
|
||||
template<Quantity Q, QuantityKind QK>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator/(const Q& lhs, const QK& rhs)
|
||||
requires requires { lhs / rhs.common(); }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs / rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK, Dimensionless D>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator%(const QK& lhs, const D& rhs)
|
||||
requires requires { lhs.common() % rhs; }
|
||||
{
|
||||
return detail::make_quantity_kind<QK>(lhs.common() % rhs);
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator%(const QK1& lhs, const QK2& rhs)
|
||||
requires requires { lhs.common() % rhs.common(); }
|
||||
{
|
||||
return detail::make_quantity_kind<QK1>(lhs.common() % rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
requires std::three_way_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
|
||||
[[nodiscard]] constexpr auto operator<=>(const QK1& lhs, const QK2& rhs)
|
||||
{
|
||||
return lhs.common() <=> rhs.common();
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
requires std::equality_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
|
||||
[[nodiscard]] constexpr bool operator==(const QK1& lhs, const QK2& rhs)
|
||||
{
|
||||
return lhs.common() == rhs.common();
|
||||
}
|
||||
|
||||
// type traits
|
||||
namespace detail {
|
||||
|
||||
template<typename K, typename U, typename Rep>
|
||||
inline constexpr bool is_quantity_kind<quantity_kind<K, U, Rep>> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace units
|
@ -53,8 +53,14 @@ public:
|
||||
quantity_point(const quantity_point&) = default;
|
||||
quantity_point(quantity_point&&) = default;
|
||||
|
||||
template<Quantity Q>
|
||||
requires std::is_convertible_v<Q, quantity_type>
|
||||
template<safe_convertible_to_<rep> Value>
|
||||
requires is_same_v<dimension, dim_one> && std::is_constructible_v<quantity_type, Value>
|
||||
constexpr explicit quantity_point(const Value& v) : q_(v) {}
|
||||
|
||||
constexpr explicit quantity_point(const quantity_type& q) : q_{q} {}
|
||||
|
||||
template<QuantityLike Q>
|
||||
requires std::is_constructible_v<quantity_type, Q>
|
||||
constexpr explicit quantity_point(const Q& q) : q_{q} {}
|
||||
|
||||
template<QuantityPoint QP2>
|
||||
@ -169,8 +175,12 @@ public:
|
||||
|
||||
};
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
quantity_point(quantity<D, U, Rep>) -> quantity_point<D, U, Rep>;
|
||||
template<QuantityValue V>
|
||||
explicit(false) quantity_point(V) -> quantity_point<dim_one, one, V>;
|
||||
|
||||
template<QuantityLike Q>
|
||||
quantity_point(Q) -> quantity_point<typename quantity_like_traits<Q>::dimension,
|
||||
typename quantity_like_traits<Q>::unit, typename quantity_like_traits<Q>::rep>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
195
src/include/units/quantity_point_kind.h
Normal file
195
src/include/units/quantity_point_kind.h
Normal file
@ -0,0 +1,195 @@
|
||||
|
||||
// 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 <units/quantity_kind.h>
|
||||
#include <units/quantity_point.h>
|
||||
#include <compare>
|
||||
|
||||
namespace units {
|
||||
|
||||
/**
|
||||
* @brief A quantity point kind
|
||||
*
|
||||
* An absolute quantity kind with respect to zero (which represents some origin).
|
||||
*
|
||||
* @tparam PK the point kind of quantity point
|
||||
* @tparam U the measurement unit of the quantity point kind
|
||||
* @tparam Rep the type to be used to represent values of the quantity point kind
|
||||
*/
|
||||
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep = double>
|
||||
class quantity_point_kind {
|
||||
public:
|
||||
using point_kind_type = PK;
|
||||
using kind_type = typename PK::base_kind;
|
||||
using quantity_kind_type = quantity_kind<kind_type, U, Rep>;
|
||||
using quantity_type = typename quantity_kind_type::quantity_type;
|
||||
using dimension = typename quantity_type::dimension;
|
||||
using unit = typename quantity_type::unit;
|
||||
using rep = typename quantity_type::rep;
|
||||
|
||||
private:
|
||||
quantity_kind_type qk_;
|
||||
|
||||
public:
|
||||
quantity_point_kind() = default;
|
||||
quantity_point_kind(const quantity_point_kind&) = default;
|
||||
quantity_point_kind(quantity_point_kind&&) = default;
|
||||
|
||||
template<safe_convertible_to_<rep> Value>
|
||||
requires std::is_constructible_v<quantity_kind_type, Value>
|
||||
constexpr explicit quantity_point_kind(const Value& v) : qk_(v) {}
|
||||
|
||||
constexpr explicit quantity_point_kind(const quantity_type& q) : qk_{q} {}
|
||||
|
||||
template<QuantityLike Q>
|
||||
requires std::is_constructible_v<quantity_type, Q>
|
||||
constexpr explicit quantity_point_kind(const Q& q) : qk_{q} {}
|
||||
|
||||
constexpr explicit quantity_point_kind(const quantity_point<dimension, U, Rep>& qp) : qk_{qp.relative()} {}
|
||||
|
||||
constexpr explicit quantity_point_kind(const quantity_kind_type& qk) : qk_{qk} {}
|
||||
|
||||
template<QuantityPointKindEquivalentTo<quantity_point_kind> QPK2>
|
||||
requires std::is_convertible_v<typename QPK2::quantity_kind_type, quantity_kind_type>
|
||||
constexpr explicit(false) quantity_point_kind(const QPK2& qpk) : qk_{qpk.relative()} {}
|
||||
|
||||
quantity_point_kind& operator=(const quantity_point_kind&) = default;
|
||||
quantity_point_kind& operator=(quantity_point_kind&&) = default;
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind_type relative() const noexcept { return qk_; }
|
||||
|
||||
[[nodiscard]] static constexpr quantity_point_kind min() noexcept
|
||||
requires requires { quantity_kind_type::min(); }
|
||||
{
|
||||
return quantity_point_kind(quantity_kind_type::min());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_point_kind max() noexcept
|
||||
requires requires { quantity_kind_type::max(); }
|
||||
{
|
||||
return quantity_point_kind(quantity_kind_type::max());
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator++()
|
||||
requires requires(quantity_kind_type qk) { ++qk; }
|
||||
{
|
||||
++qk_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_point_kind operator++(int)
|
||||
requires requires(quantity_kind_type qk) { qk++; }
|
||||
{
|
||||
return quantity_point_kind(qk_++);
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator--()
|
||||
requires requires(quantity_kind_type qk) { --qk; }
|
||||
{
|
||||
--qk_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_point_kind operator--(int)
|
||||
requires requires(quantity_kind_type qk) { qk--; }
|
||||
{
|
||||
return quantity_point_kind(qk_--);
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator+=(const quantity_kind_type& qk)
|
||||
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 += qk2; }
|
||||
{
|
||||
qk_ += qk;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator-=(const quantity_kind_type& qk)
|
||||
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 -= qk2; }
|
||||
{
|
||||
qk_ -= qk;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Hidden Friends
|
||||
// Below friend functions are to be found via argument-dependent lookup only
|
||||
|
||||
template<QuantityKind QK>
|
||||
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const quantity_point_kind& lhs, const QK& rhs)
|
||||
requires requires { lhs.relative() + rhs; }
|
||||
{
|
||||
return units::quantity_point_kind(lhs.relative() + rhs);
|
||||
}
|
||||
|
||||
template<QuantityKind QK>
|
||||
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const QK& lhs, const quantity_point_kind& rhs)
|
||||
requires requires { lhs + rhs.relative(); }
|
||||
{
|
||||
return units::quantity_point_kind(lhs + rhs.relative());
|
||||
}
|
||||
|
||||
template<QuantityKind QK>
|
||||
[[nodiscard]] friend constexpr QuantityPointKind auto operator-(const quantity_point_kind& lhs, const QK& rhs)
|
||||
requires requires { lhs.relative() - rhs; }
|
||||
{
|
||||
return units::quantity_point_kind(lhs.relative() - rhs);
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator-(const quantity_point_kind& lhs, const quantity_point_kind& rhs)
|
||||
requires requires { lhs.relative() - rhs.relative(); }
|
||||
{
|
||||
return lhs.relative() - rhs.relative();
|
||||
}
|
||||
|
||||
template<QuantityPointKind QPK>
|
||||
requires std::three_way_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
|
||||
[[nodiscard]] friend constexpr auto operator<=>(const quantity_point_kind& lhs, const QPK& rhs)
|
||||
{
|
||||
return lhs.relative() <=> rhs.relative();
|
||||
}
|
||||
|
||||
template<QuantityPointKind QPK>
|
||||
requires std::equality_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
|
||||
[[nodiscard]] friend constexpr bool operator==(const quantity_point_kind& lhs, const QPK& rhs)
|
||||
{
|
||||
return lhs.relative() == rhs.relative();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<PointKind PK, QuantityValue V>
|
||||
explicit(false) quantity_point_kind(PK, V) -> quantity_point_kind<PK, one, V>;
|
||||
|
||||
template<QuantityKind QK>
|
||||
quantity_point_kind(QK) ->
|
||||
quantity_point_kind<downcast_point_kind<typename QK::kind_type>, typename QK::unit, typename QK::rep>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename PK, typename U, typename Rep>
|
||||
inline constexpr bool is_quantity_point_kind<quantity_point_kind<PK, U, Rep>> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace units
|
@ -34,9 +34,12 @@ add_library(unit_tests_static
|
||||
dimensions_concepts_test.cpp
|
||||
fixed_string_test.cpp
|
||||
fps_test.cpp
|
||||
kind_test.cpp
|
||||
math_test.cpp
|
||||
quantity_point_test.cpp
|
||||
quantity_test.cpp
|
||||
quantity_point_test.cpp
|
||||
quantity_kind_test.cpp
|
||||
quantity_point_kind_test.cpp
|
||||
ratio_test.cpp
|
||||
si_test.cpp
|
||||
si_cgs_test.cpp
|
||||
|
203
test/unit_test/static/kind_test.cpp
Normal file
203
test/unit_test/static/kind_test.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
// 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 "test_tools.h"
|
||||
#include "units/generic/angle.h"
|
||||
#include "units/kind.h"
|
||||
#include "units/physical/si/base/length.h"
|
||||
#include "units/physical/si/derived/area.h"
|
||||
#include "units/physical/si/derived/speed.h"
|
||||
|
||||
using namespace units;
|
||||
using namespace physical::si;
|
||||
|
||||
namespace {
|
||||
|
||||
template<Downcastable T>
|
||||
using downcast_result = std::conditional_t<UNITS_DOWNCAST_MODE, T, units::downcast_base_t<T>>;
|
||||
|
||||
|
||||
// no library-defined base kind
|
||||
|
||||
|
||||
// spherical coordinates
|
||||
struct radius : kind<radius, dim_length> {}; // program-defined base kind
|
||||
struct colatitude : kind<colatitude, units::dim_angle<>> {};
|
||||
struct azimuth : kind<azimuth, units::dim_angle<>> {};
|
||||
|
||||
static_assert(Kind<radius>);
|
||||
static_assert(Kind<colatitude>);
|
||||
static_assert(Kind<azimuth>);
|
||||
static_assert(Kind<azimuth::kind>);
|
||||
static_assert(Kind<azimuth::_kind_base>);
|
||||
|
||||
static_assert(!PointKind<radius>);
|
||||
static_assert(!PointKind<colatitude>);
|
||||
static_assert(!PointKind<azimuth>);
|
||||
static_assert(!PointKind<azimuth::kind>);
|
||||
static_assert(!PointKind<azimuth::_kind_base>);
|
||||
|
||||
static_assert(is_same_v<radius::base_kind, radius>);
|
||||
static_assert(is_same_v<radius::dimension, dim_length>);
|
||||
static_assert(is_same_v<downcast_result<radius>, downcast_kind<radius, dim_length>>);
|
||||
|
||||
static_assert(equivalent<radius, radius>);
|
||||
static_assert(equivalent<radius, radius::kind>);
|
||||
static_assert(equivalent<radius, radius::_kind_base>);
|
||||
static_assert(equivalent<radius::kind, radius::_kind_base>);
|
||||
static_assert(equivalent<radius::_kind_base, radius::_kind_base>);
|
||||
static_assert(!equivalent<radius, colatitude>);
|
||||
static_assert(!equivalent<radius, azimuth>);
|
||||
static_assert(!equivalent<azimuth, colatitude>);
|
||||
static_assert(!equivalent<azimuth, colatitude::_kind_base>);
|
||||
static_assert(!equivalent<azimuth::_kind_base, colatitude::_kind_base>);
|
||||
static_assert(!equivalent<colatitude, downcast_kind<radius, dim_length>>);
|
||||
static_assert(!equivalent<azimuth, downcast_kind<radius, dim_length>>);
|
||||
|
||||
using radial_area = downcast_kind<radius, dim_area>; // library-defined derived kind
|
||||
using radial_point = downcast_point_kind<radius>; // library-defined base point kind
|
||||
|
||||
static_assert(Kind<radial_area>);
|
||||
static_assert(!PointKind<radial_area>);
|
||||
|
||||
static_assert(is_same_v<radial_area::base_kind, radius>);
|
||||
static_assert(is_same_v<radial_area::dimension, dim_area>);
|
||||
static_assert(is_same_v<radial_area, detail::_kind_base<radius, dim_area>>);
|
||||
static_assert(is_same_v<radial_area, downcast_kind<radius, dim_area>>);
|
||||
static_assert(is_same_v<radial_area, downcast_kind<radial_area, dim_area>>);
|
||||
|
||||
static_assert(equivalent<radial_area, radial_area>);
|
||||
static_assert(!equivalent<radial_area, radius>);
|
||||
static_assert(!equivalent<radial_area, radius::_kind_base>);
|
||||
|
||||
static_assert(!Kind<radial_point>);
|
||||
static_assert(PointKind<radial_point>);
|
||||
|
||||
static_assert(is_same_v<radial_point::base_kind, radius>);
|
||||
static_assert(is_same_v<radial_point::dimension, dim_length>);
|
||||
static_assert(is_same_v<radial_point, detail::_point_kind_base<radius>>);
|
||||
static_assert(is_same_v<radial_point, downcast_point_kind<radius>>);
|
||||
|
||||
static_assert(equivalent<radial_point, radial_point>);
|
||||
static_assert(!equivalent<radial_point, radius>);
|
||||
static_assert(!equivalent<radial_point, radius::_kind_base>);
|
||||
|
||||
static_assert(equivalent<radius, downcast_kind<radial_area, dim_length>>);
|
||||
static_assert(!equivalent<radius, radial_area>);
|
||||
static_assert(!equivalent<radius, radial_point>);
|
||||
|
||||
|
||||
struct width : kind<width, units::physical::si::dim_length> {};
|
||||
using horizontal_speed = downcast_kind<width, dim_speed>;
|
||||
|
||||
struct abscissa : point_kind<abscissa, width> {}; // program-defined base point kind
|
||||
using horizontal_velocity = downcast_point_kind<downcast_kind<width, dim_speed>>; // library-defined derived point kind
|
||||
|
||||
static_assert(!Kind<abscissa>);
|
||||
static_assert(!Kind<abscissa::point_kind>);
|
||||
static_assert(!Kind<abscissa::_point_kind_base>);
|
||||
static_assert(PointKind<abscissa>);
|
||||
static_assert(PointKind<abscissa::point_kind>);
|
||||
static_assert(PointKind<abscissa::_point_kind_base>);
|
||||
|
||||
static_assert(is_same_v<abscissa::base_kind, width>);
|
||||
static_assert(is_same_v<abscissa::dimension, dim_length>);
|
||||
static_assert(is_same_v<downcast_result<abscissa>, downcast_point_kind<width>>);
|
||||
|
||||
static_assert(equivalent<abscissa, abscissa>);
|
||||
static_assert(equivalent<abscissa, abscissa::point_kind>);
|
||||
static_assert(equivalent<abscissa, abscissa::_point_kind_base>);
|
||||
static_assert(!equivalent<abscissa, width>);
|
||||
static_assert(!equivalent<abscissa, width::_kind_base>);
|
||||
|
||||
static_assert(!Kind<horizontal_velocity>);
|
||||
static_assert(PointKind<horizontal_velocity>);
|
||||
|
||||
static_assert(is_same_v<horizontal_velocity::base_kind, horizontal_speed>);
|
||||
static_assert(is_same_v<horizontal_velocity::dimension, dim_speed>);
|
||||
static_assert(is_same_v<horizontal_velocity, detail::_point_kind_base<horizontal_speed>>);
|
||||
static_assert(is_same_v<horizontal_velocity, downcast_point_kind<horizontal_speed>>);
|
||||
|
||||
static_assert(equivalent<horizontal_velocity, horizontal_velocity>);
|
||||
static_assert(!equivalent<horizontal_velocity, horizontal_speed>);
|
||||
static_assert(!equivalent<horizontal_velocity, width>);
|
||||
static_assert(!equivalent<horizontal_velocity, width::_kind_base>);
|
||||
|
||||
static_assert(!equivalent<abscissa, horizontal_velocity>);
|
||||
static_assert(!equivalent<abscissa::_point_kind_base, horizontal_velocity>);
|
||||
|
||||
|
||||
struct height : kind<height, dim_length> {};
|
||||
|
||||
struct rate_of_climb : derived_kind<rate_of_climb, height, dim_speed> {}; // program-defined derived kind
|
||||
struct velocity_of_climb : point_kind<velocity_of_climb, rate_of_climb> {}; // program-defined derived point kind
|
||||
|
||||
static_assert(Kind<rate_of_climb>);
|
||||
static_assert(Kind<rate_of_climb::derived_kind>);
|
||||
static_assert(Kind<rate_of_climb::_kind_base>);
|
||||
static_assert(!PointKind<rate_of_climb>);
|
||||
static_assert(!PointKind<rate_of_climb::derived_kind>);
|
||||
static_assert(!PointKind<rate_of_climb::_kind_base>);
|
||||
|
||||
static_assert(is_same_v<rate_of_climb::base_kind, height>);
|
||||
static_assert(is_same_v<rate_of_climb::dimension, dim_speed>);
|
||||
static_assert(is_same_v<downcast_result<rate_of_climb>, downcast_kind<rate_of_climb, dim_speed>>);
|
||||
static_assert(is_same_v<downcast_result<rate_of_climb>, downcast_kind<height, dim_speed>>);
|
||||
|
||||
static_assert(equivalent<rate_of_climb, rate_of_climb>);
|
||||
static_assert(equivalent<rate_of_climb, rate_of_climb::derived_kind>);
|
||||
static_assert(equivalent<rate_of_climb, rate_of_climb::_kind_base>);
|
||||
static_assert(equivalent<rate_of_climb::derived_kind, rate_of_climb::_kind_base>);
|
||||
static_assert(!equivalent<rate_of_climb, height>);
|
||||
static_assert(!equivalent<rate_of_climb, height::_kind_base>);
|
||||
|
||||
static_assert(!Kind<velocity_of_climb>);
|
||||
static_assert(!Kind<velocity_of_climb::point_kind>);
|
||||
static_assert(!Kind<velocity_of_climb::_point_kind_base>);
|
||||
static_assert(PointKind<velocity_of_climb>);
|
||||
static_assert(PointKind<velocity_of_climb::point_kind>);
|
||||
static_assert(PointKind<velocity_of_climb::_point_kind_base>);
|
||||
|
||||
static_assert(is_same_v<velocity_of_climb::base_kind, rate_of_climb>);
|
||||
static_assert(is_same_v<velocity_of_climb::dimension, dim_speed>);
|
||||
static_assert(is_same_v<downcast_result<velocity_of_climb>, downcast_point_kind<rate_of_climb>>);
|
||||
|
||||
static_assert(equivalent<velocity_of_climb, velocity_of_climb>);
|
||||
static_assert(equivalent<velocity_of_climb, velocity_of_climb::point_kind>);
|
||||
static_assert(equivalent<velocity_of_climb, velocity_of_climb::_point_kind_base>);
|
||||
static_assert(equivalent<velocity_of_climb::point_kind, velocity_of_climb::_point_kind_base>);
|
||||
static_assert(equivalent<velocity_of_climb::_point_kind_base, velocity_of_climb::_point_kind_base>);
|
||||
|
||||
static_assert(!equivalent<height, rate_of_climb>);
|
||||
static_assert(!equivalent<height, rate_of_climb::_kind_base>);
|
||||
static_assert(!equivalent<height::_kind_base, rate_of_climb::_kind_base>);
|
||||
|
||||
static_assert(!equivalent<height, velocity_of_climb>);
|
||||
static_assert(!equivalent<height, velocity_of_climb::_point_kind_base>);
|
||||
static_assert(!equivalent<height::_kind_base, velocity_of_climb::_point_kind_base>);
|
||||
|
||||
static_assert(!equivalent<rate_of_climb, velocity_of_climb>);
|
||||
static_assert(!equivalent<rate_of_climb::_kind_base, velocity_of_climb::_point_kind_base>);
|
||||
|
||||
static_assert(is_same_v<downcast_result<height>, downcast_kind<rate_of_climb, dim_length>>);
|
||||
|
||||
} // namespace
|
675
test/unit_test/static/quantity_kind_test.cpp
Normal file
675
test/unit_test/static/quantity_kind_test.cpp
Normal file
@ -0,0 +1,675 @@
|
||||
// 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 "test_tools.h"
|
||||
#include "units/chrono.h"
|
||||
#include "units/physical/si/cgs/derived/speed.h"
|
||||
#include "units/physical/si/derived/area.h"
|
||||
#include "units/physical/si/derived/frequency.h"
|
||||
#include "units/physical/si/derived/speed.h"
|
||||
#include "units/physical/si/fps/derived/speed.h"
|
||||
#include "units/quantity_point.h"
|
||||
#include "units/quantity_kind.h"
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace units;
|
||||
namespace si = physical::si;
|
||||
using namespace si;
|
||||
using namespace unit_constants;
|
||||
|
||||
constexpr auto cgs_cm = cgs::unit_constants::cm;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
struct radius_kind : kind<radius_kind, dim_length> {};
|
||||
struct width_kind : kind<width_kind, dim_length> {};
|
||||
struct height_kind : kind<height_kind, dim_length> {};
|
||||
|
||||
struct horizontal_area_kind : derived_kind<horizontal_area_kind, width_kind, dim_area> {};
|
||||
struct rate_of_climb_kind : derived_kind<rate_of_climb_kind, height_kind, dim_speed> {};
|
||||
|
||||
struct apple : kind<apple, dim_one> {};
|
||||
struct orange : kind<orange, dim_one> {};
|
||||
|
||||
struct time_kind : kind<time_kind, dim_time> {};
|
||||
|
||||
struct cgs_width_kind : kind<cgs_width_kind, cgs::dim_length> {};
|
||||
|
||||
template <Unit U, QuantityValue Rep = double> using radius = quantity_kind<radius_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using width = quantity_kind<width_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using height = quantity_kind<height_kind, U, Rep>;
|
||||
|
||||
template <Unit U, QuantityValue Rep = double> using horizontal_area = quantity_kind<horizontal_area_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using rate_of_climb = quantity_kind<rate_of_climb_kind, U, Rep>;
|
||||
|
||||
template <Unit U = one, QuantityValue Rep = double> using apples = quantity_kind<apple, U, Rep>;
|
||||
template <Unit U = one, QuantityValue Rep = double> using oranges = quantity_kind<orange, U, Rep>;
|
||||
|
||||
template <Unit U, QuantityValue Rep = double> using cgs_width = quantity_kind<cgs_width_kind, U, Rep>;
|
||||
|
||||
/////////////
|
||||
// concepts
|
||||
/////////////
|
||||
|
||||
static_assert(QuantityKind<width<metre>>);
|
||||
static_assert(QuantityKind<rate_of_climb<metre_per_second>>);
|
||||
static_assert(!QuantityKind<double>);
|
||||
static_assert(!QuantityKind<length<metre>>);
|
||||
static_assert(!QuantityKind<quantity_point<dim_length, metre>>);
|
||||
|
||||
static_assert(QuantityKindOf<width<metre>, width_kind>);
|
||||
static_assert(!QuantityKindOf<width<metre>, height_kind>);
|
||||
static_assert(!QuantityKindOf<width<metre>, metre>);
|
||||
static_assert(!QuantityKindOf<length<metre>, width_kind>);
|
||||
static_assert(!QuantityKindOf<length<metre>, metre>);
|
||||
static_assert(!QuantityKindOf<quantity_point<dim_length, metre>, width_kind>);
|
||||
static_assert(!QuantityKindOf<quantity_point<dim_length, metre>, dim_length>);
|
||||
static_assert(!QuantityKindOf<quantity_point<dim_length, metre>, metre>);
|
||||
|
||||
|
||||
///////////////
|
||||
// invariants
|
||||
///////////////
|
||||
|
||||
static_assert(sizeof(width<metre, double>) == sizeof(double));
|
||||
static_assert(sizeof(height<metre, short>) == sizeof(short));
|
||||
|
||||
template<typename Width>
|
||||
concept invalid_types = requires {
|
||||
requires !requires { typename quantity_kind<Width, second, int>; }; // unit of a different dimension
|
||||
requires !requires { typename quantity_kind<Width, metre, length<metre>>; }; // quantity used as Rep
|
||||
requires !requires { typename quantity_kind<Width, metre, quantity_point<dim_length, metre>>; }; // quantity point used as Rep
|
||||
requires !requires { typename quantity_kind<Width, metre, width<metre>>; }; // quantity kind used as Rep
|
||||
requires !requires { typename quantity_kind<metre, Width, double>; }; // reordered arguments
|
||||
requires !requires { typename quantity_kind<metre, double, Width>; }; // reordered arguments
|
||||
};
|
||||
static_assert(invalid_types<width_kind>);
|
||||
|
||||
static_assert(std::is_trivially_default_constructible_v<width<metre>>);
|
||||
static_assert(std::is_trivially_copy_constructible_v<width<metre>>);
|
||||
static_assert(std::is_trivially_move_constructible_v<width<metre>>);
|
||||
static_assert(std::is_trivially_copy_assignable_v<width<metre>>);
|
||||
static_assert(std::is_trivially_move_assignable_v<width<metre>>);
|
||||
static_assert(std::is_trivially_destructible_v<width<metre>>);
|
||||
|
||||
static_assert(std::is_nothrow_default_constructible_v<width<metre>>);
|
||||
static_assert(std::is_nothrow_copy_constructible_v<width<metre>>);
|
||||
static_assert(std::is_nothrow_move_constructible_v<width<metre>>);
|
||||
static_assert(std::is_nothrow_copy_assignable_v<width<metre>>);
|
||||
static_assert(std::is_nothrow_move_assignable_v<width<metre>>);
|
||||
static_assert(std::is_nothrow_destructible_v<width<metre>>);
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<width<metre>>);
|
||||
static_assert(std::is_standard_layout_v<width<metre>>);
|
||||
|
||||
static_assert(std::default_initializable<width<metre>>);
|
||||
static_assert(std::move_constructible<width<metre>>);
|
||||
static_assert(std::copy_constructible<width<metre>>);
|
||||
static_assert(std::equality_comparable<width<metre>>);
|
||||
static_assert(std::totally_ordered<width<metre>>);
|
||||
static_assert(std::regular<width<metre>>);
|
||||
|
||||
static_assert(std::three_way_comparable<width<metre>>);
|
||||
|
||||
static_assert(!std::is_aggregate_v<width<metre>>);
|
||||
|
||||
|
||||
///////////////////
|
||||
// member aliases
|
||||
///////////////////
|
||||
|
||||
static_assert(is_same_v<width<metre>::kind_type, width_kind>);
|
||||
static_assert(is_same_v<width<metre>::quantity_type, length<metre>>);
|
||||
static_assert(is_same_v<width<metre>::dimension, dim_length>);
|
||||
static_assert(is_same_v<width<metre>::unit, metre>);
|
||||
static_assert(is_same_v<width<metre>::rep, double>);
|
||||
|
||||
|
||||
////////////////////
|
||||
// common observer
|
||||
////////////////////
|
||||
|
||||
static_assert(same(radius<metre>{}.common(), length<metre>{}));
|
||||
static_assert(radius<metre>{}.common() == // [VIM3] 1.2 kind of quantity
|
||||
height<metre>{}.common()); // aspect common to mutually comparable quantities
|
||||
// hence `.common()`
|
||||
static_assert(!std::equality_comparable_with<apples<>, oranges<>>);
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// static member functions
|
||||
////////////////////////////
|
||||
|
||||
static_assert(width<metre, double>::zero().common() == 0 * m);
|
||||
static_assert(width<metre, double>::one().common() == 1 * m);
|
||||
static_assert(width<metre, unsigned>::min().common() == 0 * m);
|
||||
static_assert(width<metre, unsigned>::max().common() == std::numeric_limits<unsigned>::max() * m);
|
||||
static_assert(width<metre, double>::min().common().count() == std::numeric_limits<double>::lowest());
|
||||
static_assert(width<metre, double>::max().common().count() == std::numeric_limits<double>::max());
|
||||
|
||||
|
||||
////////////////////////
|
||||
// default constructor
|
||||
////////////////////////
|
||||
|
||||
// default initialization
|
||||
#if !defined(COMP_MSVC)
|
||||
static_assert([] {
|
||||
const auto read_uninitialized_quantity = [] {
|
||||
width<metre> w;
|
||||
++w;
|
||||
};
|
||||
return !require_constant_invocation<read_uninitialized_quantity>;
|
||||
}());
|
||||
#endif
|
||||
|
||||
// value initialization
|
||||
static_assert(width<metre>{}.common() == 0 * m);
|
||||
|
||||
|
||||
/////////
|
||||
// CTAD
|
||||
/////////
|
||||
|
||||
static_assert(same(quantity_kind(rate_of_climb<kilometre_per_hour, double>(0.01 * km / h)),
|
||||
rate_of_climb<kilometre_per_hour, double>(0.01 * km / h)));
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// construction from a rep
|
||||
////////////////////////////
|
||||
|
||||
static_assert(construct_from_only<apples<one, int>>(1).common() == 1);
|
||||
static_assert(construct_from_only<apples<one, double>>(1.0).common() == 1);
|
||||
static_assert(construct_from_only<apples<percent, int>>(1ULL).common().count() == 1);
|
||||
static_assert(construct_from_only<apples<percent, double>>(1.0L).common().count() == 1);
|
||||
static_assert(!constructible_or_convertible_from<apples<one, int>>(1.0));
|
||||
static_assert(!constructible_or_convertible_from<apples<percent, int>>(1.0));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(1.0));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(1.0f));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, float>>(1.0));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(1));
|
||||
|
||||
|
||||
/////////////////////////////////
|
||||
// construction from a quantity
|
||||
/////////////////////////////////
|
||||
|
||||
static_assert(construct_from_only<width<metre, int>>(1 * m).common() == 1 * m);
|
||||
static_assert(construct_from_only<width<metre, int>>(1 * km).common() == 1 * km);
|
||||
// static_assert(construct_from_only<width<metre, int>>(1 * cgs_cm).common() == 1 * cm); // TODO: Fix #210
|
||||
static_assert(construct_from_only<width<metre, double>>(1 * cgs_cm).common() == 1 * cm);
|
||||
static_assert(construct_from_only<width<metre, double>>(1 * mm).common() == 1 * mm);
|
||||
static_assert(construct_from_only<width<metre, double>>(1 * m).common() == 1 * m);
|
||||
static_assert(construct_from_only<width<metre, double>>(1 * km).common() == 1 * km);
|
||||
static_assert(construct_from_only<width<metre, double>>(1.0 * mm).common() == 1 * mm);
|
||||
static_assert(construct_from_only<width<metre, double>>(1.0 * m).common() == 1 * m);
|
||||
static_assert(construct_from_only<width<metre, double>>(1.0 * km).common() == 1 * km);
|
||||
static_assert(construct_from_only<width<metre, float>>(1.0 * mm).common() == 1 * mm);
|
||||
static_assert(construct_from_only<width<metre, float>>(1.0 * m).common() == 1 * m);
|
||||
static_assert(construct_from_only<width<metre, float>>(1.0 * km).common() == 1 * km);
|
||||
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1 * mm));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1.0 * mm));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1.0 * m));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1.0 * km));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1 * s));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1 * m * m));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1 * m / s));
|
||||
|
||||
|
||||
static_assert(construct_from_only<width<metre, double>>(1.0f * m).common() == 1 * m);
|
||||
static_assert(construct_from_only<width<metre, double>>(short{1} * m).common() == 1 * m);
|
||||
static_assert(construct_from_only<width<metre, short>>(1 * m).common() == 1 * m);
|
||||
|
||||
|
||||
static_assert(construct_from_only<apples<one, int>>(quantity(1)).common() == 1);
|
||||
static_assert(construct_from_only<apples<one, double>>(dimensionless<percent>(1)).common() == 0.01);
|
||||
static_assert(construct_from_only<apples<percent, double>>(quantity(1.0)).common().count() == 100);
|
||||
static_assert(construct_from_only<apples<percent, double>>(dimensionless<percent>(1)).common().count() == 1);
|
||||
static_assert(construct_from_only<apples<one, double>>(quantity(1.0)).common() == 1);
|
||||
static_assert(construct_from_only<apples<one, double>>(quantity(1.0f)).common() == 1);
|
||||
static_assert(construct_from_only<apples<one, float>>(quantity(1.0)).common() == 1);
|
||||
static_assert(construct_from_only<apples<one, double>>(quantity(1)).common() == 1);
|
||||
static_assert(construct_from_only<apples<one, double>>(quantity(short{1})).common() == 1);
|
||||
static_assert(construct_from_only<apples<one, short>>(quantity(1)).common() == 1);
|
||||
static_assert(construct_from_only<apples<percent, double>>(quantity(1.0)).common().count() == 1e2);
|
||||
static_assert(construct_from_only<apples<percent, double>>(quantity(1.0f)).common().count() == 1e2);
|
||||
static_assert(construct_from_only<apples<percent, float>>(quantity(1.0)).common().count() == 1e2f);
|
||||
static_assert(construct_from_only<apples<percent, double>>(quantity(1)).common().count() == 1e2);
|
||||
static_assert(construct_from_only<apples<percent, double>>(quantity(short{1})).common().count() == 1e2);
|
||||
static_assert(construct_from_only<apples<percent, short>>(quantity(1)).common().count() == 1e2);
|
||||
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<one, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<one, float>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, float>>(apples<one, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<one, int>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<one, short>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, short>>(apples<one, int>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<percent, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<percent, float>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, float>>(apples<percent, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<percent, int>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<percent, short>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, short>>(apples<percent, int>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<percent, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<percent, float>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, float>>(apples<percent, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<percent, int>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, double>>(apples<percent, short>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<one, short>>(apples<percent, int>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<one, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<one, float>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, float>>(apples<one, double>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<one, int>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, double>>(apples<one, short>{}));
|
||||
static_assert(!constructible_or_convertible_from<dimensionless<percent, short>>(apples<one, int>{}));
|
||||
|
||||
|
||||
static_assert(construct_from_only<quantity_kind<time_kind, second, int>>(42s).common() == 42 * s);
|
||||
|
||||
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1 * s));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1 * m * m));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(1 * m / s));
|
||||
|
||||
|
||||
static_assert(construct_from_only<width<centimetre, double>>(1.0 * cgs_cm).common() == 1 * cm);
|
||||
static_assert(construct_from_only<width<cgs::centimetre, double>>(1.0 * cm).common() == 1 * cm);
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
// construction from another quantity kind
|
||||
////////////////////////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(construct_and_convert_from<width<metre, int>>(width<metre, int>(1 * m)).common() == 1 * m);
|
||||
static_assert(construct_and_convert_from<width<centimetre, int>>(width<cgs::centimetre, int>(1 * cgs_cm)).common() == 1 * cm);
|
||||
static_assert(construct_and_convert_from<width<fps::foot, double>>(width<cgs::centimetre, int>(1 * cgs_cm)).common() == 1 * cm);
|
||||
|
||||
static_assert(construct_and_convert_from<width<metre, double>>(width<metre, int>(1 * m)).common() == 1 * m);
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(width<metre, double>(1.0 * m)));
|
||||
|
||||
static_assert(construct_and_convert_from<width<metre, int>>(width<kilometre, int>(1 * km)).common() == 1 * km);
|
||||
static_assert(!constructible_or_convertible_from<width<kilometre, int>>(width<metre, int>(1 * m)));
|
||||
|
||||
static_assert(construct_and_convert_from<width<metre, double>>(width<kilometre, int>(1 * km)).common() == 1 * km);
|
||||
static_assert(construct_and_convert_from<width<kilometre, double>>(width<metre, int>(1 * m)).common() == 1 * m);
|
||||
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(height<metre, int>(1 * m)));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, int>>(width<metre, int>(1 * m) / m));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, int>>(oranges<one, int>(1)));
|
||||
// clang-format on
|
||||
|
||||
|
||||
//////////////////////////////////
|
||||
// construction from other types
|
||||
//////////////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(quantity_point(1 * m)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, int>>(quantity_point(1 * km)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(quantity_point(1 * m)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(quantity_point(1 * km)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(quantity_point(1.0 * m)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(quantity_point(1.0 * km)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(quantity_point(1.0 * m * m)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(quantity_point(1.0 * s)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(quantity_point(1.0 * s)));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(1s));
|
||||
static_assert(!constructible_or_convertible_from<width<metre, double>>(1.0s));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, int>>(quantity_point(1)));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, int>>(quantity_point(dimensionless<percent, int>(1))));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, double>>(quantity_point(1)));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, double>>(quantity_point(dimensionless<percent, int>(1))));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, double>>(quantity_point(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, double>>(quantity_point(dimensionless<percent, double>(1.0))));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, double>>(quantity_point(1.0 * m)));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, double>>(1s));
|
||||
static_assert(!constructible_or_convertible_from<apples<one, double>>(1.0s));
|
||||
// clang-format on
|
||||
|
||||
|
||||
////////////////////////
|
||||
// assignment operator
|
||||
////////////////////////
|
||||
|
||||
static_assert((width<metre, int>(2 * m) = width<metre, int>(1 * m)).common() == 1 * m);
|
||||
static_assert((width<metre, int>(2 * m) = width<metre, int>(1 * km)).common() == 1 * km);
|
||||
static_assert(!std::is_assignable_v<width<metre, int>, width<metre, double>>);
|
||||
static_assert(!std::is_assignable_v<width<metre, int>, width<millimetre, int>>);
|
||||
|
||||
|
||||
/////////////////////
|
||||
// member operators
|
||||
/////////////////////
|
||||
|
||||
#if !defined(COMP_MSVC) || defined(NDEBUG)
|
||||
static_assert([]() {
|
||||
width<metre, int> w(1 * m);
|
||||
assert(+w.common() == 1 * m);
|
||||
assert(-w.common() == -1 * m);
|
||||
assert(&++w == &w && w.common() == 2 * m);
|
||||
assert(&--w == &w && w.common() == 1 * m);
|
||||
assert((w++).common() == 1 * m && w.common() == 2 * m);
|
||||
assert((w--).common() == 2 * m && w.common() == 1 * m);
|
||||
assert(&(w += w) == &w && w.common() == 2 * m);
|
||||
assert(&(w -= w) == &w && w.common() == 0 * m);
|
||||
w = width<metre, int>(3 * m);
|
||||
assert(&(w *= 3) == &w && w.common() == 9 * m);
|
||||
assert(&(w /= 2) == &w && w.common() == 4 * m);
|
||||
assert(&(w %= 3) == &w && w.common() == 1 * m);
|
||||
assert(&(w %= w) == &w && w.common() == 0 * m);
|
||||
w = width<metre, int>(3 * m);
|
||||
assert(&(w *= 3.9) == &w && w.common() == 11 * m);
|
||||
assert(&(w /= 3.9) == &w && w.common() == 2 * m);
|
||||
assert(&(w %= 3.9) == &w && w.common() == 2 * m);
|
||||
return true;
|
||||
}());
|
||||
#endif
|
||||
|
||||
static_assert(same((-width<metre, short>(short{1} * m)).common(), int{-1} * m));
|
||||
|
||||
template<typename K, typename U, typename Qx>
|
||||
concept invalid_compound_assignments_ = requires(quantity_kind<K, U, int> w, Qx q) {
|
||||
requires !requires { w += q; };
|
||||
requires !requires { w -= q; };
|
||||
requires !requires { w *= q; };
|
||||
requires !requires { w /= q; };
|
||||
requires !requires { w %= q; };
|
||||
};
|
||||
template<typename Width>
|
||||
concept invalid_compound_assignments = requires(quantity_kind<Width, metre, int> w) {
|
||||
requires !requires { w += 1; };
|
||||
requires !requires { w -= 1; };
|
||||
requires !requires { w %= w * 1.0; };
|
||||
requires invalid_compound_assignments_<Width, metre, length<metre, int>>;
|
||||
requires invalid_compound_assignments_<Width, metre, height<metre, int>>;
|
||||
requires invalid_compound_assignments_<Width, metre, horizontal_area<square_metre, int>>;
|
||||
requires invalid_compound_assignments_<Width, metre, quantity_point<dim_length, metre, int>>;
|
||||
requires invalid_compound_assignments_<Width, metre, std::chrono::seconds>;
|
||||
};
|
||||
static_assert(invalid_compound_assignments<width_kind>);
|
||||
static_assert(invalid_compound_assignments_<time_kind, second, std::chrono::seconds>);
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// non-member operators
|
||||
/////////////////////////
|
||||
|
||||
static_assert(same(width<metre, int>(2 * m) + width<metre, int>(3 * m), width<metre, int>(5 * m)));
|
||||
static_assert(same(width<metre, int>(2 * m) + width<metre, double>(3. * m), width<metre, double>(5. * m)));
|
||||
static_assert(same(width<metre, double>(2. * m) + width<metre, int>(3 * m), width<metre, double>(5. * m)));
|
||||
static_assert(same(width<kilometre, int>(2 * km) + width<metre, double>(3e3 * m), width<metre, double>(5e3 * m)));
|
||||
static_assert(same(width<metre, int>(2 * m) - width<metre, int>(3 * m), width<metre, int>(-1 * m)));
|
||||
static_assert(same(width<metre, int>(2 * m) - width<metre, double>(3. * m), width<metre, double>(-1. * m)));
|
||||
static_assert(same(width<metre, double>(2. * m) - width<metre, int>(3 * m), width<metre, double>(-1. * m)));
|
||||
static_assert(same(width<metre, double>(2e3 * m) - width<kilometre, int>(3 * km), width<metre, double>(-1e3 * m)));
|
||||
|
||||
static_assert(!std::is_invocable_v<std::plus<>, width<metre>, double>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, width<metre>, length<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, width<metre>, quantity_point<dim_length, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, width<metre>, height<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, width<metre>, double>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, width<metre>, length<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, width<metre>, quantity_point<dim_length, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, width<metre>, height<metre>>);
|
||||
|
||||
// clang-format off
|
||||
static_assert(!std::is_invocable_v<std::plus<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
// clang-format on
|
||||
|
||||
static_assert(same(width<metre, int>(2 * m) * 3, width<metre, int>(6 * m)));
|
||||
static_assert(same(width<metre, int>(2 * m) * 3., width<metre, double>(6. * m)));
|
||||
static_assert(same(width<metre, double>(2. * m) * 3, width<metre, double>(6. * m)));
|
||||
static_assert(same(2 * width<metre, int>(3 * m), width<metre, int>(6 * m)));
|
||||
static_assert(same(2 * width<metre, double>(3. * m), width<metre, double>(6. * m)));
|
||||
static_assert(same(2. * width<metre, int>(3 * m), width<metre, double>(6. * m)));
|
||||
|
||||
static_assert(same(height<metre, int>(2 * m) * (3 * Hz), rate_of_climb<metre_per_second, int>(6 * m / s)));
|
||||
static_assert(same(height<metre, int>(2 * m) * (3. * Hz), rate_of_climb<metre_per_second, double>(6. * m / s)));
|
||||
static_assert(same(height<metre, double>(2. * m) * (3 * Hz), rate_of_climb<metre_per_second, double>(6. * m / s)));
|
||||
static_assert(same((2 * Hz) * height<metre, int>(3 * m), rate_of_climb<metre_per_second, int>(6 * m / s)));
|
||||
static_assert(same((2 * Hz) * height<metre, double>(3. * m), rate_of_climb<metre_per_second, double>(6. * m / s)));
|
||||
static_assert(same((2. * Hz) * height<metre, int>(3 * m), rate_of_climb<metre_per_second, double>(6. * m / s)));
|
||||
|
||||
static_assert(same(width<metre, int>(2 * m) / 3, width<metre, int>(0 * m)));
|
||||
static_assert(same(width<metre, int>(2 * m) / 3., width<metre, double>(2 / 3. * m)));
|
||||
static_assert(same(width<metre, double>(2. * m) / 3, width<metre, double>(2. / 3 * m)));
|
||||
|
||||
static_assert(same((2 / width<metre, int>(3 * m)).common(), 2 / 3 / m));
|
||||
static_assert(same((2 / width<metre, double>(3. * m)).common(), 2 / 3. / m));
|
||||
static_assert(same((2. / width<metre, int>(3 * m)).common(), 2. / 3 / m));
|
||||
|
||||
static_assert(same(height<metre, int>(2 * m) / (3 * s), rate_of_climb<metre_per_second, int>(0 * m / s)));
|
||||
static_assert(same(height<metre, int>(2 * m) / (3. * s), rate_of_climb<metre_per_second, double>(2 / 3. * m / s)));
|
||||
static_assert(same(height<metre, double>(2. * m) / (3 * s), rate_of_climb<metre_per_second, double>(2. / 3 * m / s)));
|
||||
|
||||
static_assert(same(width<metre, int>(2 * m) * dimensionless<percent, int>(3), width<centimetre, int>(6 * cm)));
|
||||
static_assert(same(dimensionless<percent, int>(2) * width<metre, int>(3 * m), width<centimetre, int>(6 * cm)));
|
||||
static_assert(same(width<metre, int>(2 * m) / dimensionless<percent, double>(3), width<hectometre, double>(2. / 3 * hm)));
|
||||
static_assert(same(width<metre, int>(2 * m) % dimensionless<percent, int>(3), width<centimetre, int>(2 * cm)));
|
||||
|
||||
static_assert(same(((2 * m) / height<metre, int>(3 * m)), quantity_kind<downcast_kind<height_kind, dim_one>, one, int>(0)));
|
||||
static_assert(same(((2 * m) / height<metre, int>(3 * m)).common(), quantity(0)));
|
||||
static_assert(same(((2 * m) / height<metre, double>(3. * m)).common(), quantity(2 / 3.)));
|
||||
static_assert(same(((2. * m) / height<metre, int>(3 * m)).common(), quantity(2. / 3)));
|
||||
static_assert(same(((2 * m) / height<metre, int>(3 * m) * (0 * m)), height<metre, int>(0 * m)));
|
||||
|
||||
static_assert(same(width<metre, int>(2 * m) % 3, width<metre, int>(2 * m)));
|
||||
static_assert(same(width<metre, int>(3 * m) % width<metre, int>(2 * m), width<metre, int>(1 * m)));
|
||||
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, width<metre>, width<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, width<metre>, height<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, height<metre>, quantity_point<dim_length, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, quantity_point<dim_length, metre>, height<metre>>);
|
||||
|
||||
static_assert(!std::is_invocable_v<std::divides<>, width<metre>, height<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, height<metre>, quantity_point<dim_length, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, quantity_point<dim_length, metre>, height<metre>>);
|
||||
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, width<metre, int>, length<metre, int>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, width<metre, int>, quantity_point<dim_length, metre, int>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, width<metre, int>, double>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, width<metre, int>, width<metre, double>>);
|
||||
|
||||
// clang-format off
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::multiplies<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::divides<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, quantity_kind<downcast_kind<width_kind, dim_one>, one>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_one>, one>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind< height_kind, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, quantity_kind< width_kind, metre>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
static_assert(!std::is_invocable_v<std::modulus<>, quantity_kind<downcast_kind<width_kind, dim_time>, day>, quantity_kind<downcast_kind<height_kind, dim_time>, day>>);
|
||||
// clang-format on
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// comparison operators
|
||||
/////////////////////////
|
||||
|
||||
static_assert(width<metre, int>(1 * m) == width<metre, int>(1 * m));
|
||||
static_assert(width<metre, int>(1 * m) == width<metre, double>(1.0 * m));
|
||||
static_assert(width<metre, int>(1 * m) == width<millimetre, int>(1000 * mm));
|
||||
static_assert(width<metre, int>(1 * m) == width<millimetre, double>(1e3 * mm));
|
||||
static_assert(width<metre, int>(2 * m) != width<metre, int>(1 * m));
|
||||
static_assert(width<metre, int>(2 * m) != width<cgs::centimetre, double>(1.0 * cgs_cm));
|
||||
static_assert(std::equality_comparable_with<width<metre, int>, width<metre, double>>);
|
||||
static_assert(std::equality_comparable_with<width<nanometre, int>, width<kilometre, int>>);
|
||||
static_assert(std::equality_comparable_with<width<cgs::centimetre, int>, width<millimetre, double>>);
|
||||
static_assert(std::equality_comparable_with<width<metre>, width<cgs::centimetre>>);
|
||||
template<typename Width>
|
||||
concept invalid_equality = requires(quantity_kind<Width, metre, int> w) {
|
||||
requires !requires { w == 1; };
|
||||
requires !requires { w != 1.0; };
|
||||
requires !requires { w == 1 * m; };
|
||||
requires !requires { w != 1.0 * cgs_cm; };
|
||||
requires !requires { w == 1 * km; };
|
||||
requires !requires { w != quantity(1); };
|
||||
requires !requires { w == dimensionless<percent>(1.0); };
|
||||
requires !requires { w != height<metre, int>(1 * m); };
|
||||
requires !requires { w == height<kilometre, double>(1.0 * km); };
|
||||
requires !requires { w != horizontal_area<square_metre, int>(1 * m * m); };
|
||||
requires !requires { w == rate_of_climb<kilometre_per_hour, double>(1.0 * km / h); };
|
||||
requires !requires { w != quantity_point(1 * m); };
|
||||
requires !requires { w == quantity_point(1.0 * mm); };
|
||||
requires !requires { w != quantity_point(quantity(1)); };
|
||||
requires !requires { w == quantity_point(dimensionless<percent>(1.0)); };
|
||||
};
|
||||
static_assert(invalid_equality<width_kind>);
|
||||
|
||||
static_assert(width<metre, int>(1 * m) < width<metre, int>(2 * m));
|
||||
static_assert(width<metre, int>(1 * m) <= width<metre, double>(2.0 * m));
|
||||
static_assert(width<metre, int>(1 * m) <= width<kilometre, int>(1 * km));
|
||||
static_assert(width<metre, int>(1 * m) >= width<millimetre, double>(1e3 * mm));
|
||||
static_assert(width<metre, int>(2 * m) >= width<millimetre, int>(1 * mm));
|
||||
static_assert(width<metre, int>(2 * m) > width<cgs::centimetre, int>(1 * cgs_cm));
|
||||
static_assert(std::three_way_comparable_with<width<metre, int>, width<metre, double>>);
|
||||
static_assert(std::three_way_comparable_with<width<nanometre, int>, width<kilometre, int>>);
|
||||
static_assert(std::three_way_comparable_with<width<cgs::centimetre, int>, width<millimetre, double>>);
|
||||
static_assert(std::three_way_comparable_with<width<metre>, width<cgs::centimetre>>);
|
||||
template<typename Width>
|
||||
concept invalid_relational = requires(quantity_kind<Width, metre, int> w) {
|
||||
requires !requires { w < 1; };
|
||||
requires !requires { w <= 1.0; };
|
||||
requires !requires { w >= 1 * m; };
|
||||
requires !requires { w > 1.0 * cgs_cm; };
|
||||
requires !requires { w <=> 1 * km; };
|
||||
requires !requires { w < quantity(1); };
|
||||
requires !requires { w <= dimensionless<percent>(1.0); };
|
||||
requires !requires { w >= height<metre, int>(1 * m); };
|
||||
requires !requires { w > height<kilometre, double>(1.0 * km); };
|
||||
requires !requires { w <=> horizontal_area<square_metre, int>(1 * m * m); };
|
||||
requires !requires { w < rate_of_climb<kilometre_per_hour, double>(1.0 * km / h); };
|
||||
requires !requires { w <= quantity_point(1 * m); };
|
||||
requires !requires { w >= quantity_point(1.0 * mm); };
|
||||
requires !requires { w > quantity_point(quantity(1)); };
|
||||
requires !requires { w <=> quantity_point(dimensionless<percent>(1.0)); };
|
||||
};
|
||||
static_assert(invalid_relational<width_kind>);
|
||||
|
||||
|
||||
///////////////////////
|
||||
// quantity_kind_cast
|
||||
///////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(same(quantity_kind_cast<width<metre, int>>(width<metre, int>(1 * m)), width<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_kind_cast<width<metre, double>>(width<metre, int>(1 * m)), width<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_kind_cast<width<kilometre, int>>(width<metre, int>(999 * m)), width<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_kind_cast<width<kilometre, int>>(width<metre, int>(1000 * m)), width<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_kind_cast<width<kilometre, double>>(width<metre, int>(999 * m)), width<kilometre, double>(0.999 * km)));
|
||||
static_assert(same(quantity_kind_cast<double>(width<metre, int>(1 * m)), width<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_kind_cast<metre>(width<metre, int>(1 * m)), width<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_kind_cast<kilometre>(width<metre, int>(999 * m)), width<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_kind_cast<kilometre>(width<metre, int>(1000 * m)), width<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_kind_cast<height<metre, int>>(width<metre, int>(1 * m)), height<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_kind_cast<height<metre, double>>(width<metre, int>(1 * m)), height<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_kind_cast<height<kilometre, int>>(width<metre, int>(999 * m)), height<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_kind_cast<height<kilometre, int>>(width<metre, int>(1000 * m)), height<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_kind_cast<height<kilometre, double>>(width<metre, int>(999 * m)), height<kilometre, double>(0.999 * km)));
|
||||
static_assert(same(quantity_kind_cast<height_kind>(width<metre, int>(1 * m)), height<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_kind_cast<height_kind, metre>(width<metre, int>(1 * m)), height<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_kind_cast<height_kind, kilometre>(width<metre, int>(999 * m)), height<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_kind_cast<height_kind, kilometre>(width<metre, int>(1000 * m)), height<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_kind_cast<cgs_width<cgs::centimetre, int>>(width<centimetre, int>(1 * cm)), cgs_width<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
static_assert(same(quantity_kind_cast<cgs_width_kind>(width<centimetre, int>(1 * cm)), cgs_width<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
static_assert(same(quantity_kind_cast<cgs_width_kind, cgs::centimetre>(width<centimetre, int>(1 * cm)), cgs_width<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
static_assert(same(quantity_kind_cast<cgs_width_kind>(width<metre, int>(1 * m)), cgs_width<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_kind_cast<cgs_width_kind, metre>(width<metre, int>(1 * m)), cgs_width<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_kind_cast<cgs::dim_length>(width<centimetre, int>(1 * cm)), width<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
static_assert(same(quantity_kind_cast<length<kilometre, int>>(width<metre, int>(1 * m)), width<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_kind_cast<length<centimetre, int>>(width<metre, int>(1 * m)), width<centimetre, int>(100 * cm)));
|
||||
static_assert(same(quantity_kind_cast<length<centimetre, int>>(width<metre, double>(0.01 * m)), width<centimetre, int>(1 * cm)));
|
||||
static_assert(same(quantity_kind_cast<length<centimetre, int>>(width<cgs::centimetre, int>(1 * cgs_cm)), width<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
// clang-format on
|
||||
template<typename Width>
|
||||
concept invalid_cast = requires {
|
||||
requires !requires { quantity_kind_cast<width<metre, one_rep>>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<apples<one, int>>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<horizontal_area<square_metre, int>>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<rate_of_climb<metre_per_second, int>>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<apple>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<horizontal_area_kind>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<rate_of_climb_kind>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<apple, one>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<horizontal_area_kind, square_metre>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<rate_of_climb_kind, metre_per_second>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<dimensionless<one>>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<square_metre>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<second>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<one_rep>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<quantity_point<dim_length, metre, int>>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
requires !requires { quantity_kind_cast<quantity_point<dim_one, one, int>>(quantity_kind<Width, metre, int>(1 * m)); };
|
||||
};
|
||||
static_assert(invalid_cast<width_kind>);
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// extensible interface
|
||||
/////////////////////////
|
||||
|
||||
namespace mylib {
|
||||
|
||||
struct radius_kind : kind<radius_kind, si::dim_length> {};
|
||||
|
||||
struct cylinder_size {};
|
||||
|
||||
template <units::QuantityKindOf<radius_kind> Radius, units::QuantityKindOf<height_kind> Height>
|
||||
cylinder_size operator+(Radius, Height);
|
||||
|
||||
} // namespace mylib
|
||||
|
||||
namespace yourapp {
|
||||
|
||||
static_assert(is_same_v<mylib::cylinder_size,
|
||||
decltype(quantity_kind<mylib::radius_kind, metre, double>(1. * m) + height<metre, double>(1. * m))>);
|
||||
|
||||
} // namespace yourapp
|
||||
|
||||
} // namespace
|
654
test/unit_test/static/quantity_point_kind_test.cpp
Normal file
654
test/unit_test/static/quantity_point_kind_test.cpp
Normal file
@ -0,0 +1,654 @@
|
||||
// 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 "test_tools.h"
|
||||
#include "units/chrono.h"
|
||||
#include "units/physical/si/cgs/derived/speed.h"
|
||||
#include "units/physical/si/derived/area.h"
|
||||
#include "units/physical/si/derived/frequency.h"
|
||||
#include "units/physical/si/derived/speed.h"
|
||||
#include "units/physical/si/fps/derived/speed.h"
|
||||
#include "units/quantity_point.h"
|
||||
#include "units/quantity_point_kind.h"
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace units;
|
||||
namespace si = physical::si;
|
||||
using namespace si;
|
||||
using namespace unit_constants;
|
||||
|
||||
constexpr auto cgs_cm = cgs::unit_constants::cm;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
struct width_kind : kind<width_kind, dim_length> {};
|
||||
struct height_kind : kind<height_kind, dim_length> {};
|
||||
struct abscissa_kind : point_kind<abscissa_kind, width_kind> {};
|
||||
struct ordinate_kind : point_kind<ordinate_kind, height_kind> {};
|
||||
|
||||
struct distance_kind : kind<distance_kind, dim_length> {};
|
||||
struct cgs_width_kind : kind<cgs_width_kind, cgs::dim_length> {};
|
||||
struct cgs_height_kind : kind<cgs_height_kind, cgs::dim_length> {};
|
||||
struct rate_of_climb_kind : derived_kind<rate_of_climb_kind, height_kind, dim_speed> {};
|
||||
struct altitude_kind : point_kind<altitude_kind, cgs_height_kind> {};
|
||||
|
||||
struct apple : kind<apple, dim_one> {};
|
||||
struct orange : kind<orange, dim_one> {};
|
||||
struct nth_apple_kind : point_kind<nth_apple_kind, apple> {};
|
||||
struct nth_orange_kind : point_kind<nth_orange_kind, orange> {};
|
||||
|
||||
struct time_kind : kind<time_kind, dim_time> {};
|
||||
struct time_point_kind : point_kind<time_point_kind, time_kind> {};
|
||||
|
||||
template <Unit U, QuantityValue Rep = double> using width = quantity_kind<width_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using height = quantity_kind<height_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using abscissa = quantity_point_kind<abscissa_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using ordinate = quantity_point_kind<ordinate_kind, U, Rep>;
|
||||
|
||||
template <Unit U, QuantityValue Rep = double> using distance = quantity_kind<distance_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using cgs_width = quantity_kind<cgs_width_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using cgs_height = quantity_kind<cgs_height_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using rate_of_climb = quantity_kind<rate_of_climb_kind, U, Rep>;
|
||||
template <Unit U, QuantityValue Rep = double> using altitude = quantity_point_kind<altitude_kind, U, Rep>;
|
||||
|
||||
template <Unit U = one, QuantityValue Rep = double> using apples = quantity_kind<apple, U, Rep>;
|
||||
template <Unit U = one, QuantityValue Rep = double> using oranges = quantity_kind<orange, U, Rep>;
|
||||
template <Unit U = one, QuantityValue Rep = double> using nth_apple = quantity_point_kind<nth_apple_kind, U, Rep>;
|
||||
template <Unit U = one, QuantityValue Rep = double> using nth_orange = quantity_point_kind<nth_orange_kind, U, Rep>;
|
||||
|
||||
/////////////
|
||||
// concepts
|
||||
/////////////
|
||||
|
||||
static_assert(QuantityPointKind<abscissa<metre>>);
|
||||
static_assert(QuantityPointKind<nth_apple<one>>);
|
||||
static_assert(!QuantityPointKind<double>);
|
||||
static_assert(!QuantityPointKind<length<metre>>);
|
||||
static_assert(!QuantityPointKind<quantity_point<dim_length, metre>>);
|
||||
static_assert(!QuantityPointKind<width<metre>>);
|
||||
|
||||
static_assert(QuantityPointKindOf<abscissa<metre>, abscissa_kind>);
|
||||
static_assert(!QuantityPointKindOf<abscissa<metre>, ordinate_kind>);
|
||||
static_assert(!QuantityPointKindOf<abscissa<metre>, metre>);
|
||||
static_assert(!QuantityPointKindOf<length<metre>, abscissa_kind>);
|
||||
static_assert(!QuantityPointKindOf<length<metre>, metre>);
|
||||
static_assert(!QuantityPointKindOf<width<metre>, abscissa_kind>);
|
||||
static_assert(!QuantityPointKindOf<width<metre>, width_kind>);
|
||||
static_assert(!QuantityPointKindOf<width<metre>, metre>);
|
||||
static_assert(!QuantityPointKindOf<quantity_point<dim_length, metre>, width_kind>);
|
||||
static_assert(!QuantityPointKindOf<quantity_point<dim_length, metre>, dim_length>);
|
||||
static_assert(!QuantityPointKindOf<quantity_point<dim_length, metre>, metre>);
|
||||
|
||||
|
||||
///////////////
|
||||
// invariants
|
||||
///////////////
|
||||
|
||||
static_assert(sizeof(abscissa<metre, double>) == sizeof(double));
|
||||
static_assert(sizeof(ordinate<metre, short>) == sizeof(short));
|
||||
|
||||
template<typename Width, typename Abscissa>
|
||||
concept invalid_types = requires {
|
||||
requires !requires { typename quantity_point_kind<Width, metre, int>; }; // width_kind is not a point kind
|
||||
requires !requires { typename quantity_point_kind<Abscissa, second, int>; }; // unit of a different dimension
|
||||
requires !requires { typename quantity_point_kind<Abscissa, metre, length<metre>>; }; // quantity used as Rep
|
||||
requires !requires { typename quantity_point_kind<Abscissa, metre, quantity_point<dim_length, metre>>; }; // quantity point used as Rep
|
||||
requires !requires { typename quantity_point_kind<Abscissa, metre, width<metre>>; }; // quantity kind used as Rep
|
||||
requires !requires { typename quantity_point_kind<Abscissa, metre, abscissa<metre>>; }; // quantity point kind used as Rep
|
||||
requires !requires { typename quantity_point_kind<metre, Abscissa, double>; }; // reordered arguments
|
||||
requires !requires { typename quantity_point_kind<metre, double, Abscissa>; }; // reordered arguments
|
||||
};
|
||||
static_assert(invalid_types<width_kind, abscissa_kind>);
|
||||
|
||||
static_assert(std::is_trivially_default_constructible_v<abscissa<metre>>);
|
||||
static_assert(std::is_trivially_copy_constructible_v<abscissa<metre>>);
|
||||
static_assert(std::is_trivially_move_constructible_v<abscissa<metre>>);
|
||||
static_assert(std::is_trivially_copy_assignable_v<abscissa<metre>>);
|
||||
static_assert(std::is_trivially_move_assignable_v<abscissa<metre>>);
|
||||
static_assert(std::is_trivially_destructible_v<abscissa<metre>>);
|
||||
|
||||
static_assert(std::is_nothrow_default_constructible_v<abscissa<metre>>);
|
||||
static_assert(std::is_nothrow_copy_constructible_v<abscissa<metre>>);
|
||||
static_assert(std::is_nothrow_move_constructible_v<abscissa<metre>>);
|
||||
static_assert(std::is_nothrow_copy_assignable_v<abscissa<metre>>);
|
||||
static_assert(std::is_nothrow_move_assignable_v<abscissa<metre>>);
|
||||
static_assert(std::is_nothrow_destructible_v<abscissa<metre>>);
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<abscissa<metre>>);
|
||||
static_assert(std::is_standard_layout_v<abscissa<metre>>);
|
||||
|
||||
static_assert(std::default_initializable<abscissa<metre>>);
|
||||
static_assert(std::move_constructible<abscissa<metre>>);
|
||||
static_assert(std::copy_constructible<abscissa<metre>>);
|
||||
static_assert(std::equality_comparable<abscissa<metre>>);
|
||||
static_assert(std::totally_ordered<abscissa<metre>>);
|
||||
static_assert(std::regular<abscissa<metre>>);
|
||||
|
||||
static_assert(std::three_way_comparable<abscissa<metre>>);
|
||||
|
||||
static_assert(!std::is_aggregate_v<abscissa<metre>>);
|
||||
|
||||
|
||||
///////////////////
|
||||
// member aliases
|
||||
///////////////////
|
||||
|
||||
static_assert(is_same_v<abscissa<metre>::point_kind_type, abscissa_kind>);
|
||||
static_assert(is_same_v<abscissa<metre>::kind_type, width_kind>);
|
||||
static_assert(is_same_v<abscissa<metre>::quantity_kind_type, width<metre>>);
|
||||
static_assert(is_same_v<abscissa<metre>::quantity_type, length<metre>>);
|
||||
static_assert(is_same_v<abscissa<metre>::dimension, dim_length>);
|
||||
static_assert(is_same_v<abscissa<metre>::unit, metre>);
|
||||
static_assert(is_same_v<abscissa<metre>::rep, double>);
|
||||
|
||||
|
||||
//////////////////////
|
||||
// relative observer
|
||||
//////////////////////
|
||||
|
||||
static_assert(same(abscissa<metre>{}.relative(), width<metre>{}));
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// static member functions
|
||||
////////////////////////////
|
||||
|
||||
static_assert(abscissa<metre, unsigned>::min().relative().common() == 0 * m);
|
||||
static_assert(abscissa<metre, unsigned>::max().relative().common() == std::numeric_limits<unsigned>::max() * m);
|
||||
static_assert(abscissa<metre, double>::min().relative().common().count() == std::numeric_limits<double>::lowest());
|
||||
static_assert(abscissa<metre, double>::max().relative().common().count() == std::numeric_limits<double>::max());
|
||||
|
||||
|
||||
////////////////////////
|
||||
// default constructor
|
||||
////////////////////////
|
||||
|
||||
// default initialization
|
||||
#if !defined(COMP_MSVC)
|
||||
static_assert([] {
|
||||
const auto read_uninitialized_quantity = [] {
|
||||
abscissa<metre> w;
|
||||
++w;
|
||||
};
|
||||
return !require_constant_invocation<read_uninitialized_quantity>;
|
||||
}());
|
||||
#endif
|
||||
|
||||
// value initialization
|
||||
static_assert(abscissa<metre>{}.relative().common() == 0 * m);
|
||||
|
||||
|
||||
/////////
|
||||
// CTAD
|
||||
/////////
|
||||
|
||||
static_assert(same(quantity_point_kind(width<metre, int>(0 * m)), abscissa<metre, int>{}));
|
||||
static_assert(same(quantity_point_kind(abscissa<metre, int>(0 * m)), abscissa<metre, int>{}));
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// construction from a rep
|
||||
////////////////////////////
|
||||
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(1.0).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(1.0f).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(1).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(short{1}).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, short>>(1).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, int>>(1).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(one_rep{}).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, int>>(one_rep{}).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, short>>(one_rep{}).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<percent, int>>(1ULL).relative().common().count() == 1);
|
||||
static_assert(construct_from_only<nth_apple<percent, double>>(1).relative().common().count() == 1);
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(1.0));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(1.0));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, double>>(1.0));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, double>>(1.0f));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, double>>(1));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, double>>(short{1}));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, short>>(1));
|
||||
|
||||
|
||||
/////////////////////////////////
|
||||
// construction from a quantity
|
||||
/////////////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(construct_from_only<abscissa<metre, short>>(1 * m).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(1 * m).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(1 * km).relative().common() == 1 * km);
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(1ULL * m).relative().common() == 1 * m);
|
||||
// static_assert(construct_from_only<abscissa<metre, int>>(1 * cgs_cm).relative().common() == 1 * cm); // TODO: Fix #210
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(1 * m).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(1.0 * km).relative().common() == 1 * km);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(1 * cgs_cm).relative().common() == 1 * cm);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(1.0 * cgs_cm).relative().common() == 1 * cm);
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(1 * mm));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(1.0 * m));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(1.0 * km));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(1 * cgs_cm));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(quantity(1)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(1 * s));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(1s));
|
||||
|
||||
static_assert(construct_from_only<nth_apple<one, int>>(quantity(1)).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(dimensionless<percent>(1)).relative().common() == 0.01);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(dimensionless<percent>(1)).relative().common() == 0.01);
|
||||
static_assert(construct_from_only<nth_apple<percent, double>>(dimensionless<percent>(1)).relative().common().count() == 1);
|
||||
static_assert(construct_from_only<nth_apple<percent, double>>(quantity(1)).relative().common().count() == 100);
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(quantity(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(dimensionless<percent>(1)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(quantity(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(dimensionless<percent>(1)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(1 * m));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(1 * s));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(1s));
|
||||
// clang-format on
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
// construction from a quantity point
|
||||
///////////////////////////////////////
|
||||
|
||||
static_assert(construct_from_only<abscissa<metre, short>>(1 * m).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(quantity_point(short{1} * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(quantity_point(1 * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(quantity_point(1 * km)).relative().common() == 1 * km);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(quantity_point(1 * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(quantity_point(1 * km)).relative().common() == 1 * km);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(quantity_point(1.0 * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(quantity_point(1.0 * mm)).relative().common() == 1 * mm);
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(quantity_point(1 * mm)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(quantity_point(1.0 * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(quantity_point(1.0 * km)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, double>>(quantity_point(1.0 * m * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, double>>(quantity_point(1.0 * s)));
|
||||
|
||||
// clang-format off
|
||||
static_assert(construct_from_only<nth_apple<one, short>>(quantity_point(1)).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, int>>(quantity_point(1)).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(quantity_point(1)).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(quantity_point(dimensionless<percent, int>(1))).relative().common() == 0.01);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(quantity_point(1.0)).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(quantity_point(dimensionless<percent, double>(1.0))).relative().common() == 0.01);
|
||||
static_assert(construct_from_only<nth_apple<percent, int>>(quantity_point(1)).relative().common().count() == 100);
|
||||
static_assert(construct_from_only<nth_apple<percent, double>>(quantity_point(dimensionless<percent>(1))).relative().common().count() == 1);
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(quantity_point(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(quantity_point(dimensionless<percent>(1))));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(quantity_point(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(quantity_point(dimensionless<percent, int>(1))));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(quantity_point(dimensionless<percent, double>(1))));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, double>>(quantity_point(1.0 * s)));
|
||||
// clang-format on
|
||||
|
||||
|
||||
//////////////////////////////////////
|
||||
// construction from a quantity kind
|
||||
//////////////////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(width<metre, int>(1 * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, int>>(width<kilometre, unsigned long long>(1ULL * km)).relative().common() == 1 * km);
|
||||
static_assert(construct_from_only<abscissa<centimetre, int>>(width<cgs::centimetre, int>(1 * cgs_cm)).relative().common() == 1 * cm);
|
||||
static_assert(construct_from_only<abscissa<fps::foot, double>>(width<cgs::centimetre, int>(1 * cgs_cm)).relative().common() == 1 * cm);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(width<metre, int>(1 * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(width<millimetre, double>(1.0 * mm)).relative().common() == 1 * mm);
|
||||
static_assert(construct_from_only<abscissa<metre, double>>(width<kilometre, unsigned long long>(1ULL * km)).relative().common() == 1 * km);
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(width<metre, double>(1.0 * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(width<millimetre, int>(1 * mm)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(height<metre, int>(1 * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(abscissa_kind{}, width<metre, double>(1.0 * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(abscissa_kind{}, width<millimetre, int>(1 * mm)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(abscissa_kind{}, height<metre, int>(1 * m)));
|
||||
|
||||
static_assert(construct_from_only<nth_apple<one, int>>(apples<one, int>(1)).relative().common() == 1);
|
||||
static_assert(construct_from_only<nth_apple<one, double>>(apples<percent, double>(dimensionless<percent>(1))).relative().common() == 0.01);
|
||||
static_assert(construct_from_only<nth_apple<percent, int>>(apples<one, int>(1)).relative().common().count() == 100);
|
||||
static_assert(construct_from_only<nth_apple<percent, double>>(apples<percent, double>(dimensionless<percent>(1))).relative().common().count() == 1);
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(apples<one, double>(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(apples<percent, double>(dimensionless<percent>(1))));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(apples<one, double>(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(apples<percent, double>(dimensionless<percent>(1))));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(width<metre, int>(1 * m) / m));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(oranges<one, int>(1)));
|
||||
// clang-format on
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// construction from another quantity point kind
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(construct_and_convert_from<abscissa<metre, int>>(abscissa<metre, int>(1 * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_and_convert_from<abscissa<metre, int>>(abscissa<kilometre, unsigned long long>(1ULL * km)).relative().common() == 1 * km);
|
||||
static_assert(construct_and_convert_from<abscissa<metre, double>>(abscissa<metre, unsigned long long>(1ULL * m)).relative().common() == 1 * m);
|
||||
static_assert(construct_and_convert_from<abscissa<metre, double>>(abscissa<cgs::centimetre, int>(1 * cgs_cm)).relative().common() == 1 * cm);
|
||||
static_assert(construct_and_convert_from<abscissa<fps::foot, double>>(abscissa<cgs::centimetre, int>(1 * cgs_cm)).relative().common() == 1 * cm);
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(abscissa<metre, double>(1.0 * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<kilometre, int>>(abscissa<metre, int>(1 * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(ordinate<metre, int>(1 * m)));
|
||||
static_assert(!constructible_or_convertible_from<abscissa<metre, int>>(quantity_point_kind<time_point_kind, second, int>(1 * s)));
|
||||
|
||||
static_assert(construct_and_convert_from<nth_apple<one, int>>(nth_apple<one, int>(1)).relative().common() == 1);
|
||||
static_assert(construct_and_convert_from<nth_apple<one, double>>(nth_apple<percent, double>(dimensionless<percent>(1))).relative().common() == 0.01);
|
||||
static_assert(construct_and_convert_from<nth_apple<percent, int>>(nth_apple<one, int>(1)).relative().common().count() == 100);
|
||||
static_assert(construct_and_convert_from<nth_apple<percent, double>>(nth_apple<percent, double>(dimensionless<percent>(1))).relative().common().count() == 1);
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(nth_apple<one, double>(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<percent, int>>(nth_apple<percent, double>(dimensionless<percent>(1))));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(nth_apple<one, double>(1.0)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(nth_apple<percent, int>(dimensionless<percent, int>(1))));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(nth_orange<one, int>(1)));
|
||||
static_assert(!constructible_or_convertible_from<nth_apple<one, int>>(abscissa<metre, int>(1 * m)));
|
||||
// clang-format on
|
||||
|
||||
|
||||
//////////////////////
|
||||
// other conversions
|
||||
//////////////////////
|
||||
|
||||
static_assert(!std::is_convertible_v<abscissa<metre, int>, int>);
|
||||
static_assert(!std::is_convertible_v<abscissa<metre, int>, dimensionless<one, int>>);
|
||||
static_assert(!std::is_convertible_v<abscissa<metre, int>, length<metre, int>>);
|
||||
static_assert(!std::is_convertible_v<abscissa<metre, int>, width<metre, int>>);
|
||||
static_assert(!std::is_convertible_v<abscissa<metre, int>, height<metre, int>>);
|
||||
static_assert(!std::is_convertible_v<abscissa<metre, int>, quantity_point<dim_length, metre, int>>);
|
||||
|
||||
|
||||
////////////////////////
|
||||
// assignment operator
|
||||
////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert((abscissa<metre, int>(2 * m) = abscissa<metre, int>(1 * m)).relative().common() == 1 * m);
|
||||
static_assert((abscissa<metre, int>(2 * m) = abscissa<kilometre, int>(1 * km)).relative().common() == 1 * km);
|
||||
static_assert(!std::is_assignable_v<abscissa<metre, int>, abscissa<metre, double>>);
|
||||
static_assert(!std::is_assignable_v<abscissa<metre, int>, abscissa<millimetre, int>>);
|
||||
// clang-format on
|
||||
|
||||
|
||||
/////////////////////
|
||||
// member operators
|
||||
/////////////////////
|
||||
|
||||
#if !defined(COMP_MSVC) || defined(NDEBUG)
|
||||
static_assert([]() {
|
||||
const width<metre, int> w(1 * m);
|
||||
quantity_point_kind x(w);
|
||||
assert(&++x == &x && x.relative().common() == 2 * m);
|
||||
assert(&--x == &x && x.relative().common() == 1 * m);
|
||||
assert((x++).relative().common() == 1 * m && x.relative().common() == 2 * m);
|
||||
assert((x--).relative().common() == 2 * m && x.relative().common() == 1 * m);
|
||||
assert(&(x += w) == &x && x.relative().common() == 2 * m);
|
||||
assert(&(x -= w) == &x && x.relative().common() == 1 * m);
|
||||
return true;
|
||||
}());
|
||||
#endif
|
||||
|
||||
template<typename PK, typename U, typename Qx>
|
||||
concept invalid_compound_assignments_ = requires(quantity_point_kind<PK, U, int> x, Qx q) {
|
||||
requires !requires { x += q; };
|
||||
requires !requires { x -= q; };
|
||||
};
|
||||
template<typename Abscissa>
|
||||
concept invalid_compound_assignments = requires(quantity_point_kind<Abscissa, metre, int> x) {
|
||||
requires !requires { x += 1; };
|
||||
requires !requires { x -= 1; };
|
||||
requires invalid_compound_assignments_<Abscissa, metre, length<metre, int>>;
|
||||
requires invalid_compound_assignments_<Abscissa, metre, height<metre, int>>;
|
||||
requires invalid_compound_assignments_<Abscissa, metre, rate_of_climb<metre_per_second, int>>;
|
||||
requires invalid_compound_assignments_<Abscissa, metre, quantity_point<dim_length, metre, int>>;
|
||||
requires invalid_compound_assignments_<Abscissa, metre, std::chrono::seconds>;
|
||||
};
|
||||
static_assert(invalid_compound_assignments<abscissa_kind>);
|
||||
static_assert(invalid_compound_assignments_<time_point_kind, second, std::chrono::seconds>);
|
||||
#if __cpp_lib_chrono >= 201907L
|
||||
static_assert(invalid_compound_assignments_<time_point_kind, second, std::chrono::sys_seconds>);
|
||||
#endif
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// non-member operators
|
||||
/////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(same(abscissa<metre, int>(2 * m) + width<metre, int>(3 * m), abscissa<metre, int>(5 * m)));
|
||||
static_assert(same(abscissa<metre, int>(2 * m) + width<metre, double>(3. * m), abscissa<metre, double>(5. * m)));
|
||||
static_assert(same(abscissa<metre, double>(2. * m) + width<metre, int>(3 * m), abscissa<metre, double>(5. * m)));
|
||||
static_assert(same(abscissa<kilometre, int>(2 * km) + width<metre, double>(3e3 * m), abscissa<metre, double>(5e3 * m)));
|
||||
static_assert(same(abscissa<metre, double>(2e3 * m) + width<kilometre, int>(3 * km), abscissa<metre, double>(5e3 * m)));
|
||||
static_assert(same(width<metre, int>(2 * m) + abscissa<metre, int>(3 * m), abscissa<metre, int>(5 * m)));
|
||||
static_assert(same(width<metre, int>(2 * m) + abscissa<metre, double>(3. * m), abscissa<metre, double>(5. * m)));
|
||||
static_assert(same(width<metre, double>(2. * m) + abscissa<metre, int>(3 * m), abscissa<metre, double>(5. * m)));
|
||||
static_assert(same(width<kilometre, int>(2 * km) + abscissa<metre, double>(3e3 * m), abscissa<metre, double>(5e3 * m)));
|
||||
static_assert(same(width<metre, double>(2e3 * m) + abscissa<kilometre, int>(3 * km), abscissa<metre, double>(5e3 * m)));
|
||||
static_assert(!std::is_invocable_v<std::plus<>, abscissa<metre>, double>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, abscissa<metre>, length<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, abscissa<metre>, quantity_point<dim_length, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, abscissa<metre>, height<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, abscissa<metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, abscissa<metre>, abscissa<kilometre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, abscissa<kilometre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, height<metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, quantity_point<dim_length, metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, length<metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::plus<>, double, abscissa<metre>>);
|
||||
|
||||
static_assert(same(abscissa<metre, int>(2 * m) - width<metre, int>(3 * m), abscissa<metre, int>(-1 * m)));
|
||||
static_assert(same(abscissa<metre, int>(2 * m) - width<metre, double>(3. * m), abscissa<metre, double>(-1. * m)));
|
||||
static_assert(same(abscissa<metre, double>(2. * m) - width<metre, int>(3 * m), abscissa<metre, double>(-1. * m)));
|
||||
static_assert(same(abscissa<kilometre, int>(2 * km) - width<metre, double>(3e3 * m), abscissa<metre, double>(-1e3 * m)));
|
||||
static_assert(same(abscissa<metre, double>(2e3 * m) - width<kilometre, int>(3 * km), abscissa<metre, double>(-1e3 * m)));
|
||||
static_assert(same(abscissa<metre, int>(2 * m) - abscissa<metre, int>(3 * m), width<metre, int>(-1 * m)));
|
||||
static_assert(same(abscissa<metre, int>(2 * m) - abscissa<metre, double>(3. * m), width<metre, double>(-1. * m)));
|
||||
static_assert(same(abscissa<metre, double>(2. * m) - abscissa<metre, int>(3 * m), width<metre, double>(-1. * m)));
|
||||
static_assert(same(abscissa<kilometre, int>(2 * km) - abscissa<metre, double>(3e3 * m), width<metre, double>(-1e3 * m)));
|
||||
static_assert(same(abscissa<metre, double>(2e3 * m) - abscissa<kilometre, int>(3 * km), width<metre, double>(-1e3 * m)));
|
||||
static_assert(!std::is_invocable_v<std::minus<>, abscissa<metre>, double>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, abscissa<metre>, length<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, abscissa<metre>, quantity_point<dim_length, metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, abscissa<metre>, height<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, abscissa<metre>, ordinate<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, ordinate<metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, height<metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, quantity_point<dim_length, metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, length<metre>, abscissa<metre>>);
|
||||
static_assert(!std::is_invocable_v<std::minus<>, double, abscissa<metre>>);
|
||||
// clang-format on
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// comparison operators
|
||||
/////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(abscissa<metre, int>(1 * m) == abscissa<metre, int>(1 * m));
|
||||
static_assert(abscissa<metre, int>(1 * m) == abscissa<metre, double>(1.0 * m));
|
||||
static_assert(abscissa<metre, int>(1 * m) == abscissa<millimetre, int>(1000 * mm));
|
||||
static_assert(abscissa<metre, int>(1 * m) == abscissa<millimetre, double>(1e3 * mm));
|
||||
static_assert(abscissa<metre, int>(2 * m) != abscissa<metre, int>(1 * m));
|
||||
static_assert(abscissa<metre, int>(2 * m) != abscissa<cgs::centimetre, double>(1.0 * cgs_cm));
|
||||
static_assert(std::equality_comparable_with<abscissa<metre, int>, abscissa<metre, double>>);
|
||||
static_assert(std::equality_comparable_with<abscissa<nanometre, int>, abscissa<kilometre, int>>);
|
||||
static_assert(std::equality_comparable_with<abscissa<cgs::centimetre, int>, abscissa<millimetre, double>>);
|
||||
static_assert(std::equality_comparable_with<abscissa<metre>, abscissa<cgs::centimetre>>);
|
||||
// clang-format on
|
||||
template<typename Abscissa>
|
||||
concept invalid_equality = requires(quantity_point_kind<Abscissa, metre, int> x) {
|
||||
requires !requires { x == 1; };
|
||||
requires !requires { x != 1.0; };
|
||||
requires !requires { x == 1 * m; };
|
||||
requires !requires { x != 1.0 * cgs_cm; };
|
||||
requires !requires { x == 1 * km; };
|
||||
requires !requires { x != quantity(1); };
|
||||
requires !requires { x == dimensionless<percent>(1.0); };
|
||||
requires !requires { x != width<metre, int>(1 * m); };
|
||||
requires !requires { x == width<kilometre, double>(1.0 * km); };
|
||||
requires !requires { x != height<metre, int>(1 * m); };
|
||||
requires !requires { x == height<kilometre, double>(1.0 * km); };
|
||||
requires !requires { x == rate_of_climb<kilometre_per_hour, double>(1.0 * km / h); };
|
||||
requires !requires { x != quantity_point(1 * m); };
|
||||
requires !requires { x == quantity_point(1.0 * mm); };
|
||||
requires !requires { x != quantity_point(quantity(1)); };
|
||||
requires !requires { x == quantity_point(dimensionless<percent>(1.0)); };
|
||||
requires !requires { x != quantity_point_kind(cgs_width<metre, int>(1 * m)); };
|
||||
requires !requires { x == ordinate<metre, int>(1 * m); };
|
||||
};
|
||||
static_assert(invalid_equality<abscissa_kind>);
|
||||
|
||||
// clang-format off
|
||||
static_assert(abscissa<metre, int>(1 * m) < abscissa<metre, int>(2 * m));
|
||||
static_assert(abscissa<metre, int>(1 * m) <= abscissa<metre, double>(2.0 * m));
|
||||
static_assert(abscissa<metre, int>(1 * m) <= abscissa<kilometre, int>(1 * km));
|
||||
static_assert(abscissa<metre, int>(1 * m) >= abscissa<millimetre, double>(1e3 * mm));
|
||||
static_assert(abscissa<metre, int>(2 * m) >= abscissa<millimetre, int>(1 * mm));
|
||||
static_assert(abscissa<metre, int>(2 * m) > abscissa<cgs::centimetre, int>(1 * cgs_cm));
|
||||
static_assert(std::three_way_comparable_with<abscissa<metre, int>, abscissa<metre, double>>);
|
||||
static_assert(std::three_way_comparable_with<abscissa<nanometre, int>, abscissa<kilometre, int>>);
|
||||
static_assert(std::three_way_comparable_with<abscissa<cgs::centimetre, int>, abscissa<millimetre, double>>);
|
||||
static_assert(std::three_way_comparable_with<abscissa<metre>, abscissa<cgs::centimetre>>);
|
||||
// clang-format on
|
||||
template<typename Abscissa>
|
||||
concept invalid_relational = requires(quantity_point_kind<Abscissa, metre, int> x) {
|
||||
requires !requires { x < 1; };
|
||||
requires !requires { x <= 1.0; };
|
||||
requires !requires { x >= 1 * m; };
|
||||
requires !requires { x > 1.0 * cgs_cm; };
|
||||
requires !requires { x <=> 1 * km; };
|
||||
requires !requires { x < quantity(1); };
|
||||
requires !requires { x <= dimensionless<percent>(1.0); };
|
||||
requires !requires { x >= width<metre, int>(1 * m); };
|
||||
requires !requires { x > width<kilometre, double>(1.0 * km); };
|
||||
requires !requires { x <=> height<metre, int>(1 * m); };
|
||||
requires !requires { x < height<kilometre, double>(1.0 * km); };
|
||||
requires !requires { x <= rate_of_climb<kilometre_per_hour, double>(1.0 * km / h); };
|
||||
requires !requires { x >= quantity_point(1 * m); };
|
||||
requires !requires { x > quantity_point(1.0 * mm); };
|
||||
requires !requires { x <=> quantity_point(quantity(1)); };
|
||||
requires !requires { x < quantity_point(dimensionless<percent>(1.0)); };
|
||||
requires !requires { x <= quantity_point_kind(cgs_width<metre, int>(1 * m)); };
|
||||
requires !requires { x >= ordinate<metre, int>(1 * m); };
|
||||
};
|
||||
static_assert(invalid_relational<abscissa_kind>);
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// quantity_point_kind_cast
|
||||
/////////////////////////////
|
||||
|
||||
// clang-format off
|
||||
static_assert(same(quantity_point_kind_cast<abscissa<metre, int>>(abscissa<metre, int>(1 * m)), abscissa<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<abscissa<metre, double>>(abscissa<metre, int>(1 * m)), abscissa<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<abscissa<kilometre, int>>(abscissa<metre, int>(999 * m)), abscissa<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<abscissa<kilometre, int>>(abscissa<metre, int>(1000 * m)), abscissa<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<abscissa<kilometre, double>>(abscissa<metre, int>(999 * m)), abscissa<kilometre, double>(0.999 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<width<metre, int>>(abscissa<metre, int>(1 * m)), abscissa<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<width<metre, double>>(abscissa<metre, int>(1 * m)), abscissa<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<width<kilometre, int>>(abscissa<metre, int>(999 * m)), abscissa<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<width<kilometre, int>>(abscissa<metre, int>(1000 * m)), abscissa<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<width<kilometre, double>>(abscissa<metre, int>(999 * m)), abscissa<kilometre, double>(0.999 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<double>(abscissa<metre, int>(1 * m)), abscissa<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<metre>(abscissa<metre, int>(1 * m)), abscissa<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<kilometre>(abscissa<metre, int>(999 * m)), abscissa<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<kilometre>(abscissa<metre, int>(1000 * m)), abscissa<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate<metre, int>>(abscissa<metre, int>(1 * m)), ordinate<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate<metre, double>>(abscissa<metre, int>(1 * m)), ordinate<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate<kilometre, int>>(abscissa<metre, int>(999 * m)), ordinate<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate<kilometre, int>>(abscissa<metre, int>(1000 * m)), ordinate<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate<kilometre, double>>(abscissa<metre, int>(999 * m)), ordinate<kilometre, double>(0.999 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<height<metre, int>>(abscissa<metre, int>(1 * m)), ordinate<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<height<metre, double>>(abscissa<metre, int>(1 * m)), ordinate<metre, double>(1.0 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<height<kilometre, int>>(abscissa<metre, int>(999 * m)), ordinate<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<height<kilometre, int>>(abscissa<metre, int>(1000 * m)), ordinate<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<height<kilometre, double>>(abscissa<metre, int>(999 * m)), ordinate<kilometre, double>(0.999 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<height_kind>(abscissa<metre, int>(1 * m)), ordinate<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate_kind, metre>(abscissa<metre, int>(1 * m)), ordinate<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate_kind, kilometre>(abscissa<metre, int>(999 * m)), ordinate<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<ordinate_kind, kilometre>(abscissa<metre, int>(1000 * m)), ordinate<kilometre, int>(1 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<cgs_width<cgs::centimetre, int>>(abscissa<centimetre, int>(1 * cm)), quantity_point_kind(cgs_width<cgs::centimetre, int>(1 * cgs_cm))));
|
||||
static_assert(same(quantity_point_kind_cast<cgs_width_kind>(abscissa<centimetre, int>(1 * cm)), quantity_point_kind(cgs_width<cgs::centimetre, int>(1 * cgs_cm))));
|
||||
static_assert(same(quantity_point_kind_cast<altitude_kind>(abscissa<centimetre, int>(1 * cm)), altitude<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
static_assert(same(quantity_point_kind_cast<altitude_kind, cgs::centimetre>(abscissa<centimetre, int>(1 * cm)), altitude<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
static_assert(same(quantity_point_kind_cast<cgs_width_kind>(abscissa<metre, int>(1 * m)), quantity_point_kind(cgs_width<metre, int>(1 * m))));
|
||||
static_assert(same(quantity_point_kind_cast<altitude_kind>(abscissa<metre, int>(1 * m)), altitude<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<altitude_kind, metre>(abscissa<metre, int>(1 * m)), altitude<metre, int>(1 * m)));
|
||||
static_assert(same(quantity_point_kind_cast<cgs::dim_length>(abscissa<centimetre, int>(1 * cm)), abscissa<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
static_assert(same(quantity_point_kind_cast<length<kilometre, int>>(abscissa<metre, int>(1 * m)), abscissa<kilometre, int>(0 * km)));
|
||||
static_assert(same(quantity_point_kind_cast<length<centimetre, int>>(abscissa<metre, int>(1 * m)), abscissa<centimetre, int>(100 * cm)));
|
||||
static_assert(same(quantity_point_kind_cast<length<centimetre, int>>(abscissa<metre, double>(0.01 * m)), abscissa<centimetre, int>(1 * cm)));
|
||||
static_assert(same(quantity_point_kind_cast<length<centimetre, int>>(abscissa<cgs::centimetre, int>(1 * cgs_cm)), abscissa<cgs::centimetre, int>(1 * cgs_cm)));
|
||||
// clang-format on
|
||||
template<typename Int>
|
||||
concept invalid_cast = requires(Int i) {
|
||||
requires !requires { quantity_point_kind_cast<abscissa<metre, one_rep>>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<apples<one, Int>>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<rate_of_climb<metre_per_second, Int>>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<apple>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<rate_of_climb_kind>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<apple, one>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<width_kind, metre>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<width_kind, kilometre>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<height_kind, metre>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<height_kind, kilometre>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<cgs_width_kind, cgs::centimetre>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<rate_of_climb_kind, metre_per_second>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<dimensionless<one>>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<square_metre>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<second>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<one_rep>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<quantity_point<dim_length, metre, Int>>(abscissa<metre, Int>(i * m)); };
|
||||
requires !requires { quantity_point_kind_cast<quantity_point<dim_one, one, Int>>(abscissa<metre, Int>(i * m)); };
|
||||
};
|
||||
static_assert(invalid_cast<int>);
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// extensible interface
|
||||
/////////////////////////
|
||||
|
||||
namespace mylib {
|
||||
|
||||
struct width_kind : kind<width_kind, units::physical::si::dim_length> {};
|
||||
struct height_kind : kind<height_kind, units::physical::si::dim_length> {};
|
||||
struct abscissa_kind : point_kind<abscissa_kind, width_kind> {};
|
||||
struct ordinate_kind : point_kind<ordinate_kind, height_kind> {};
|
||||
|
||||
struct point {};
|
||||
|
||||
template <units::QuantityPointKindOf<abscissa_kind> Abscissa, units::QuantityPointKindOf<ordinate_kind> Ordinate>
|
||||
point operator+(Abscissa, Ordinate);
|
||||
|
||||
} // namespace mylib
|
||||
|
||||
namespace yourapp {
|
||||
|
||||
static_assert(is_same_v<mylib::point,
|
||||
decltype(quantity_point_kind(quantity_kind<mylib::width_kind, metre, int>(1 * m)) +
|
||||
quantity_point_kind(quantity_kind<mylib::height_kind, metre, int>(1 * m)))>);
|
||||
|
||||
} // namespace yourapp
|
||||
|
||||
} // namespace
|
@ -22,18 +22,20 @@
|
||||
|
||||
#include "units/quantity_point.h"
|
||||
#include "test_tools.h"
|
||||
#include "units/chrono.h"
|
||||
#include "units/math.h"
|
||||
#include "units/physical/si/derived/area.h"
|
||||
#include "units/physical/si/derived/speed.h"
|
||||
#include "units/physical/si/derived/volume.h"
|
||||
#include "units/physical/si/us/base/length.h"
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace units;
|
||||
using namespace units::physical::si;
|
||||
using namespace physical::si;
|
||||
using namespace unit_constants;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// class invariants
|
||||
|
||||
@ -55,9 +57,17 @@ static_assert(is_same_v<quantity_point<dim_length, metre, int>::rep, int>);
|
||||
static_assert(is_same_v<quantity_point<dim_length, metre, double>::rep, double>);
|
||||
static_assert(is_same_v<quantity_point<dim_length, metre, int>::unit, metre>);
|
||||
static_assert(is_same_v<quantity_point<dim_length, kilometre, int>::unit, kilometre>);
|
||||
static_assert(is_same_v<quantity_point<dim_length, metre, int>::dimension, dim_length>);
|
||||
static_assert(is_same_v<quantity_point<dim_length, metre, int>::quantity_type, quantity<dim_length, metre, int>>);
|
||||
|
||||
// constructors
|
||||
|
||||
static_assert(quantity_point(1).relative() == quantity(1));
|
||||
static_assert(!std::is_convertible_v<int, quantity_point<dim_one, one, int>>);
|
||||
|
||||
static_assert(quantity_point(42s).relative() == 42 * s);
|
||||
static_assert(!std::is_convertible_v<std::chrono::seconds, quantity_point<dim_time, second, std::chrono::seconds::rep>>);
|
||||
|
||||
static_assert(quantity_point<dim_length, metre, int>().relative() == 0_q_m);
|
||||
constexpr quantity_point<dim_length, metre, int> km{1000_q_m};
|
||||
static_assert(km.relative() == 1000_q_m);
|
||||
|
@ -220,7 +220,7 @@ static_assert(!std::constructible_from<length<kilometre, int>, length<metre, int
|
||||
static_assert(!std::convertible_to<length<metre, int>, length<kilometre, int>>); // truncating metre<int> -> kilometre<int> not allowed
|
||||
|
||||
// converting to double always OK
|
||||
static_assert(std::constructible_from<length<metre>, length<kilometre, int>>);
|
||||
static_assert(std::constructible_from<length<metre>, length<kilometre, int>>);
|
||||
static_assert(std::convertible_to<length<kilometre, int>, length<metre>>);
|
||||
static_assert(std::constructible_from<length<kilometre>, length<metre, int>>);
|
||||
static_assert(std::convertible_to<length<metre, int>, length<kilometre>>);
|
||||
|
@ -23,6 +23,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "units/bits/equivalent.h"
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
template<typename T, typename U>
|
||||
inline constexpr bool compare_impl = false;
|
||||
@ -36,3 +39,87 @@ inline constexpr bool compare_impl<T, U> = units::equivalent<T, U>;
|
||||
|
||||
template<typename T, typename U>
|
||||
inline constexpr bool compare = compare_impl<std::remove_cvref_t<T>, std::remove_cvref_t<U>>;
|
||||
|
||||
template<typename T, typename... Us, typename... Vs>
|
||||
constexpr bool constructible_from(Vs...) {
|
||||
return std::constructible_from<T, Us..., Vs...>;
|
||||
}
|
||||
|
||||
template<typename T, typename... Us>
|
||||
concept convertible_from_ = requires(Us... us) { [](T) {}({us...}); };
|
||||
|
||||
template<typename T, typename... Us, typename... Vs>
|
||||
constexpr bool convertible_from(Vs...) {
|
||||
if constexpr (sizeof...(Us) + sizeof...(Vs) == 1)
|
||||
return std::is_convertible_v<Us..., Vs..., T>;
|
||||
else
|
||||
return convertible_from_<T, Us..., Vs...>;
|
||||
}
|
||||
|
||||
template<typename T, typename... Us, typename... Vs>
|
||||
constexpr bool constructible_or_convertible_from(Vs...) {
|
||||
return constructible_from<T, Us..., Vs...>() || convertible_from<T, Us..., Vs...>();
|
||||
}
|
||||
|
||||
template<typename T, typename... Us, typename... Vs>
|
||||
constexpr bool constructible_and_convertible_from(Vs...) {
|
||||
return constructible_from<T, Us..., Vs...>() && convertible_from<T, Us..., Vs...>();
|
||||
}
|
||||
|
||||
template<typename T, typename... Us>
|
||||
requires (constructible_from<T, Us...>())
|
||||
constexpr T construct_from(Us... us) {
|
||||
return T(us...);
|
||||
}
|
||||
|
||||
template<typename T, typename... Us>
|
||||
requires (convertible_from<T, Us...>())
|
||||
constexpr T convert_from(Us... us) {
|
||||
if constexpr (sizeof...(Us) == 1)
|
||||
return [](T t) { return t; }(us...);
|
||||
else
|
||||
return {us...};
|
||||
}
|
||||
|
||||
template<std::equality_comparable T, typename... Us>
|
||||
requires (constructible_from<T, Us...>() && convertible_from<T, Us...>())
|
||||
constexpr T construct_and_convert_from(Us... us) {
|
||||
T t{construct_from<T>(us...)};
|
||||
assert(t == convert_from<T>(us...));
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T, typename... Us>
|
||||
requires (constructible_from<T, Us...>() && !convertible_from<T, Us...>())
|
||||
constexpr T construct_from_only(Us... us) {
|
||||
return construct_from<T>(us...);
|
||||
}
|
||||
|
||||
#if !defined(COMP_GCC)
|
||||
template<template<typename...> typename T, typename = std::void_t<>, typename... Us>
|
||||
concept ctad_constructible_from_ = requires(Us... us) { T(us...); };
|
||||
#else
|
||||
template<template<typename...> typename T, typename = std::void_t<>, typename... Us>
|
||||
inline constexpr bool ctad_constructible_from_ = false;
|
||||
|
||||
template<template<typename...> typename T, typename... Us>
|
||||
inline constexpr bool ctad_constructible_from_<T, std::void_t<decltype(T(Us{}...))>, Us...> = true;
|
||||
#endif
|
||||
|
||||
template<template<typename...> typename T, typename... Us, typename... Vs>
|
||||
constexpr bool ctad_constructible_from(Vs...) {
|
||||
return ctad_constructible_from_<T, void, Us..., Vs...>;
|
||||
}
|
||||
|
||||
#if UNITS_DOWNCAST_MODE
|
||||
constexpr auto same = []<std::equality_comparable T>(T l, T r) { return l == r; };
|
||||
#else
|
||||
constexpr auto same = []<typename T, typename U>(T l, U r)
|
||||
requires requires { l == r; }
|
||||
// requires std::equality_comparable_with<T, U> // TODO: Fix #205
|
||||
{ return l == r; };
|
||||
#endif
|
||||
|
||||
template<auto F>
|
||||
requires requires { F(); }
|
||||
inline constexpr bool require_constant_invocation = requires { new int[1][(F(), 1)]; };
|
||||
|
@ -51,6 +51,8 @@ struct metre_per_second : unit<metre_per_second> {};
|
||||
struct dim_speed : derived_dimension<dim_speed, metre_per_second, units::exponent<dim_length, 1>, units::exponent<dim_time, -1>> {};
|
||||
struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {};
|
||||
|
||||
static_assert(equivalent<metre::named_unit, metre>);
|
||||
static_assert(equivalent<metre::scaled_unit, metre>);
|
||||
static_assert(compare<downcast<scaled_unit<ratio(1), metre>>, metre>);
|
||||
static_assert(compare<downcast<scaled_unit<ratio(1, 1, -2), metre>>, centimetre>);
|
||||
static_assert(compare<downcast<scaled_unit<ratio(yard::ratio.num, yard::ratio.den, yard::ratio.exp), metre>>, yard>);
|
||||
|
Reference in New Issue
Block a user