feat: quantity_kind and quantity_point_kind

This commit is contained in:
Johel Ernesto Guerrero Peña
2021-01-04 18:36:26 -04:00
committed by Mateusz Pusz
parent 384f4b2624
commit 6bf09aa646
32 changed files with 2757 additions and 202 deletions

View File

View File

@ -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

View File

@ -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

View File

@ -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"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -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

View File

@ -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.

View File

@ -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`)::

View File

@ -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
---------------

View 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)`

View File

@ -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)

View File

@ -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.

View 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.
- *.*

View File

@ -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::

View File

@ -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) {

View File

@ -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
/**

View File

@ -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>;
};
}

View File

@ -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>

View File

@ -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
View 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

View File

@ -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

View 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

View File

@ -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 {

View 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

View File

@ -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

View 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

View 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

View 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

View File

@ -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);

View File

@ -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>>);

View File

@ -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)]; };

View File

@ -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>);