refactor: 💥 basic_concepts, quantity and quantity_cast refactored

BREAKING CHANGE:  ScalableNumber renamed to QuantityValue

Resolves #107
This commit is contained in:
Mateusz Pusz
2020-10-06 18:17:52 +02:00
parent 40f205b381
commit cfc90f4aac
96 changed files with 1620 additions and 1017 deletions

View File

@ -1 +0,0 @@
docs/CHANGELOG.md

0
CHANGELOG.md Normal file
View File

View File

@ -11,7 +11,7 @@ Interface
The difference is that it uses ``double`` as a default representation and has The difference is that it uses ``double`` as a default representation and has
a few additional member types and functions:: a few additional member types and functions::
template<Dimension D, UnitOf<D> U, ScalableNumber Rep = double> template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity { class quantity {
public: public:
using dimension = D; using dimension = D;
@ -27,7 +27,7 @@ a few additional member types and functions::
[[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs, [[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs); const quantity<D2, U2, Rep2>& rhs);
template<ScalableNumber Value, typename D, typename U, typename Rep> template<QuantityValue Value, typename D, typename U, typename Rep>
requires std::magma<std::ranges::divided_by, Value, Rep> requires std::magma<std::ranges::divided_by, Value, Rep>
[[nodiscard]] constexpr Quantity auto operator/(const Value& v, [[nodiscard]] constexpr Quantity auto operator/(const Value& v,
const quantity<D, U, Rep>& q); const quantity<D, U, Rep>& q);

View File

@ -18,7 +18,7 @@ For example the speed of light constant in :term:`SI` is defined as::
namespace si::si2019 { namespace si::si2019 {
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<metre_per_second, Rep>(299792458); inline constexpr auto speed_of_light = speed<metre_per_second, Rep>(299792458);
} }
@ -27,7 +27,7 @@ The same constant defined for natural units may be provided as::
namespace natural { namespace natural {
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<one, Rep>(1); inline constexpr auto speed_of_light = speed<one, Rep>(1);
} }

View File

@ -58,8 +58,8 @@ dimension, than we will end up with just a scalable number type:
Time auto dur1 = 10_q_s; Time auto dur1 = 10_q_s;
Time auto dur2 = 2_q_s; Time auto dur2 = 2_q_s;
Frequency auto fr1 = 5_q_Hz; Frequency auto fr1 = 5_q_Hz;
ScalableNumber auto v1 = dur1 / dur2; // 5 QuantityValue auto v1 = dur1 / dur2; // 5
ScalableNumber auto v2 = dur1 * fr1; // 50 QuantityValue auto v2 = dur1 * fr1; // 50
Quantity points have a more restricted set of operations. Quantity points have a more restricted set of operations.
Quantity points can't be added together, Quantity points can't be added together,

View File

@ -34,7 +34,7 @@ type to ``double`` by default::
namespace si { namespace si {
template<Unit U, ScalableNumber Rep = double> template<Unit U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
} }
@ -75,7 +75,7 @@ be used::
All instances of `quantity` class always match the `Quantity` concept. All instances of `quantity` class always match the `Quantity` concept.
All other regular types that are not quantities are called All other regular types that are not quantities are called
:term:`scalable numbers <scalable number>` by the library and match the :term:`scalable numbers <scalable number>` by the library and match the
`ScalableNumber` concept. `QuantityValue` concept.
However, the above is not the most important usage of those concepts. Let's However, the above is not the most important usage of those concepts. Let's
assume that the user wants to implement an ``avg_speed`` function that will assume that the user wants to implement an ``avg_speed`` function that will
@ -185,7 +185,7 @@ are provided::
template<typename T> template<typename T>
concept Dimensionless = QuantityOf<T, dim_one>; concept Dimensionless = QuantityOf<T, dim_one>;
template<Unit U, ScalableNumber Rep = double> template<Unit U, QuantityValue Rep = double>
using dimensionless = quantity<dim_one, U, Rep>; using dimensionless = quantity<dim_one, U, Rep>;
There are two special units provided for usage with such a quantity: There are two special units provided for usage with such a quantity:

View File

@ -63,7 +63,7 @@ Concepts
satisfy :expr:`Quantity<typename T::value_type>` recursively satisfy :expr:`Quantity<typename T::value_type>` recursively
(i.e. ``std::optional<si::length<si::metre>>``). (i.e. ``std::optional<si::length<si::metre>>``).
.. concept:: template<typename T> ScalableNumber .. concept:: template<typename T> QuantityValue
A concept matching non-Quantity types. Satisfied by types that match A concept matching non-Quantity types. Satisfied by types that match
:expr:`(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>` and satisfy one of the :expr:`(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>` and satisfy one of the

View File

@ -9,11 +9,11 @@ its own custom logic for it (i.e. use a complex number or a measurement class th
not only a value but also a measurement error). not only a value but also a measurement error).
A `ScalableNumber` concept A `QuantityValue` concept
-------------------------- --------------------------
To support a minimum set of `quantity` operations all custom representation types have to To support a minimum set of `quantity` operations all custom representation types have to
satisfy at least the `ScalableNumber` concept. Which means that they: satisfy at least the `QuantityValue` concept. Which means that they:
- cannot be quantities by themselves, - cannot be quantities by themselves,
- cannot be wrappers over the `quantity` type (i.e. ``std::optional<si::length<si::metre>>``), - cannot be wrappers over the `quantity` type (i.e. ``std::optional<si::length<si::metre>>``),
@ -101,7 +101,7 @@ The only difference here is that in this case we have to explicitly cast the `qu
Additional Requirements Additional Requirements
----------------------- -----------------------
As noted in the previous chapter, the `ScalableNumber` concept guarantees us the possibility As noted in the previous chapter, the `QuantityValue` concept guarantees us the possibility
to construct quantities, convert between the units of the same dimension, and compare them to construct quantities, convert between the units of the same dimension, and compare them
for equality. To provide additional `quantity` operations the custom representation type for equality. To provide additional `quantity` operations the custom representation type
have to satisfy more requirements. have to satisfy more requirements.
@ -200,7 +200,7 @@ The `quantity` class template has a few static member functions: `quantity::zero
representation type. The default implementation is provided through the `quantity_values` class representation type. The default implementation is provided through the `quantity_values` class
template:: template::
template<ScalableNumber Rep> template<QuantityValue Rep>
struct quantity_values { struct quantity_values {
static constexpr Rep zero() noexcept { return Rep(0); } static constexpr Rep zero() noexcept { return Rep(0); }
static constexpr Rep one() noexcept { return Rep(1); } static constexpr Rep one() noexcept { return Rep(1); }
@ -220,7 +220,7 @@ library's framework treat floating-point representation types differently than t
ones. This behavior can also be extended to the custom representation types with ones. This behavior can also be extended to the custom representation types with
`treat_as_floating_point` customization point which default definition is:: `treat_as_floating_point` customization point which default definition is::
template<ScalableNumber Rep> template<QuantityValue Rep>
inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>; inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
If our representation type should have a floating-point semantics or if it is a class If our representation type should have a floating-point semantics or if it is a class

View File

@ -121,7 +121,7 @@ coherent unit::
struct desk_per_hour : deduced_unit<desk_per_hour, dim_desk_rate, desk, si::hour> {}; struct desk_per_hour : deduced_unit<desk_per_hour, dim_desk_rate, desk, si::hour> {};
// a quantity of our dimension // a quantity of our dimension
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_desk_rate> U, QuantityValue Rep = double>
using desk_rate = quantity<dim_desk_rate, U, Rep>; using desk_rate = quantity<dim_desk_rate, U, Rep>;
// a concept matching the above quantity // a concept matching the above quantity
@ -150,7 +150,7 @@ define a new base dimension, its units, quantity helper, concept, and UDLs::
struct person : named_unit<person, "person", no_prefix> {}; struct person : named_unit<person, "person", no_prefix> {};
struct dim_people : base_dimension<"people", person> {}; struct dim_people : base_dimension<"people", person> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_people> U, QuantityValue Rep = double>
using people = quantity<dim_people, U, Rep>; using people = quantity<dim_people, U, Rep>;
template<typename T> template<typename T>
@ -169,7 +169,7 @@ With the above we can now define a new derived dimension::
struct person_per_desk : deduced_unit<person_per_desk, dim_occupancy_rate, person, desk> {}; struct person_per_desk : deduced_unit<person_per_desk, dim_occupancy_rate, person, desk> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_occupancy_rate> U, QuantityValue Rep = double>
using occupancy_rate = quantity<dim_occupancy_rate, U, Rep>; using occupancy_rate = quantity<dim_occupancy_rate, U, Rep>;
template<typename T> template<typename T>
@ -216,7 +216,7 @@ Such units do not share their references with base units of other systems:
struct dim_length : base_dimension<"L", foot> {}; struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
} // namespace fps } // namespace fps
@ -236,7 +236,7 @@ different systems:
struct dim_length : base_dimension<"L", metre> {}; struct dim_length : base_dimension<"L", metre> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
namespace fps { namespace fps {
@ -246,7 +246,7 @@ different systems:
struct dim_length : base_dimension<"L", foot> {}; struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
} // namespace fps } // namespace fps

View File

@ -33,7 +33,7 @@ struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(3), foot> {};
struct dim_length : base_dimension<"L", foot> {}; struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
} // namespace fps } // namespace fps
@ -45,7 +45,7 @@ struct kilometre : prefixed_unit<kilometre, units::physical::si::kilo, metre> {}
struct dim_length : base_dimension<"L", metre> {}; struct dim_length : base_dimension<"L", metre> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
namespace fps { namespace fps {
@ -55,7 +55,7 @@ struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(3), foot> {};
struct dim_length : base_dimension<"L", foot> {}; struct dim_length : base_dimension<"L", foot> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
} // namespace fps } // namespace fps

View File

@ -87,7 +87,7 @@ public:
} }
template<typename V> template<typename V>
requires (ScalableNumber<V> || Dimensionless<V>) requires (QuantityValue<V> || Dimensionless<V>)
[[nodiscard]] friend constexpr auto operator*(const vector& lhs, const V& value) [[nodiscard]] friend constexpr auto operator*(const vector& lhs, const V& value)
requires requires { lhs.magnitude() * value; } requires requires { lhs.magnitude() * value; }
{ {
@ -95,7 +95,7 @@ public:
} }
template<typename V> template<typename V>
requires (ScalableNumber<V> || Dimensionless<V>) requires (QuantityValue<V> || Dimensionless<V>)
[[nodiscard]] friend constexpr auto operator*(const V& value, const vector& rhs) [[nodiscard]] friend constexpr auto operator*(const V& value, const vector& rhs)
requires requires { value * rhs.magnitude(); } requires requires { value * rhs.magnitude(); }
{ {

View File

@ -200,10 +200,10 @@ void matrix_of_quantity_tests()
matrix_of_quantity_divide_by_scalar(); matrix_of_quantity_divide_by_scalar();
} }
template<units::Unit U = si::metre, units::ScalableNumber Rep = double> template<units::Unit U = si::metre, units::QuantityValue Rep = double>
using length_v = si::length<U, vector<Rep>>; using length_v = si::length<U, vector<Rep>>;
template<units::Unit U = si::newton, units::ScalableNumber Rep = double> template<units::Unit U = si::newton, units::QuantityValue Rep = double>
using force_v = si::force<U, vector<Rep>>; using force_v = si::force<U, vector<Rep>>;
void quantity_of_vector_add() void quantity_of_vector_add()
@ -273,7 +273,7 @@ void quantity_of_vector_tests()
quantity_of_vector_divide_by_scalar(); quantity_of_vector_divide_by_scalar();
} }
template<units::Unit U = si::metre, units::ScalableNumber Rep = double> template<units::Unit U = si::metre, units::QuantityValue Rep = double>
using length_m = si::length<U, matrix<Rep>>; using length_m = si::length<U, matrix<Rep>>;
void quantity_of_matrix_add() void quantity_of_matrix_add()

View File

@ -114,15 +114,13 @@ private:
value_type uncertainty_{}; value_type uncertainty_{};
}; };
static_assert(units::ScalableNumber<measurement<double>>);
} // namespace } // namespace
template<typename T>
inline constexpr bool units::treat_as_floating_point<measurement<T>> = std::is_floating_point_v<T>;
namespace { namespace {
static_assert(units::QuantityValue<measurement<double>>);
void example() void example()
{ {
using namespace units::physical; using namespace units::physical;

View File

@ -25,6 +25,7 @@
#include <units/bits/external/downcasting.h> #include <units/bits/external/downcasting.h>
#include <units/bits/external/fixed_string.h> #include <units/bits/external/fixed_string.h>
#include <units/bits/external/hacks.h> #include <units/bits/external/hacks.h>
#include <units/customization_points.h>
#include <units/ratio.h> #include <units/ratio.h>
#include <units/bits/external/type_traits.h> #include <units/bits/external/type_traits.h>
#include <functional> #include <functional>
@ -229,6 +230,34 @@ template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>; concept QuantityPoint = detail::is_quantity_point<T>;
// QuantityValue
template<typename T, typename U>
concept common_type_with_ =
std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>> &&
std::constructible_from<std::common_type_t<T, U>, T> &&
std::constructible_from<std::common_type_t<T, U>, U>;
template<typename T, typename U = T>
concept scalable_number_ = // exposition only
std::regular_invocable<std::multiplies<>, T, U> &&
std::regular_invocable<std::divides<>, T, U>;
template<typename T>
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>
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 From, typename To>
concept scalable_with_ = // exposition only
common_type_with_<From, To> &&
scalable_<std::common_type_t<From, To>>;
// WrappedQuantity // WrappedQuantity
namespace detail { namespace detail {
@ -248,31 +277,8 @@ inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::value_type>
* recursively (i.e. `std::optional<si::length<si::metre>>`). * recursively (i.e. `std::optional<si::length<si::metre>>`).
*/ */
template<typename T> template<typename T>
concept WrappedQuantity = detail::is_wrapped_quantity<T>; concept wrapped_quantity_ = // exposition only
detail::is_wrapped_quantity<T>;
// ScalableNumber
namespace detail {
template<typename T>
concept constructible_from_integral =
// construction from an integral type
std::constructible_from<T, std::int64_t> &&
// unit scaling
std::regular_invocable<std::multiplies<>, T, T> &&
std::regular_invocable<std::divides<>, T, T>;
template<typename T>
concept not_constructible_from_integral =
// not construction from an integral type
(!std::constructible_from<T, std::int64_t>) &&
// scaling by the value from ratio
std::regular_invocable<std::multiplies<>, T, std::int64_t> &&
std::regular_invocable<std::multiplies<>, std::int64_t, T>; // &&
// std::regular_invocable<std::divides<>, T, std::int64_t>; // TODO Uncomment when a bug in LA is fixed
} // namespace detail
/** /**
* @brief A concept matching non-Quantity types. * @brief A concept matching non-Quantity types.
@ -280,10 +286,10 @@ concept not_constructible_from_integral =
* Satisfied by types that satisfy `(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>`. * Satisfied by types that satisfy `(!Quantity<T>) && (!WrappedQuantity<T>) && std::regular<T>`.
*/ */
template<typename T> template<typename T>
concept ScalableNumber = concept QuantityValue =
(!Quantity<T>) && (!Quantity<T>) &&
(!WrappedQuantity<T>) && (!wrapped_quantity_<T>) &&
std::regular<T> && std::regular<T> &&
(detail::constructible_from_integral<T> || detail::not_constructible_from_integral<T>); scalable_<T>;
} // namespace units } // namespace units

View File

@ -28,10 +28,10 @@
namespace units { namespace units {
template<Dimension D, UnitOf<D> U, ScalableNumber Rep> template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity; class quantity;
template<Dimension D, UnitOf<D> U, ScalableNumber Rep> template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity_point; class quantity_point;
namespace detail { namespace detail {
@ -70,8 +70,7 @@ quantity_point<D, U, Rep> common_quantity_point_impl(quantity<D, U, Rep>);
} // namespace detail } // namespace detail
template<Quantity Q1, Quantity Q2, ScalableNumber Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>> template<Quantity Q1, QuantityEquivalentTo<Q1> Q2, QuantityValue Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
requires equivalent<typename Q1::dimension, typename Q2::dimension>
using common_quantity = TYPENAME detail::common_quantity_impl<Q1, Q2, Rep>::type; using common_quantity = TYPENAME detail::common_quantity_impl<Q1, Q2, Rep>::type;
template<QuantityPoint QP1, QuantityPoint QP2> template<QuantityPoint QP1, QuantityPoint QP2>

View File

@ -73,7 +73,10 @@ concept QuantityOfT = Quantity<Q> && DimensionOfT<typename Q::dimension, DimTemp
* Satisfied by all quantities with a dimension being the instantiation derived from * Satisfied by all quantities with a dimension being the instantiation derived from
* the provided dimension type. * the provided dimension type.
*/ */
template<typename T, typename Dim> template<typename Q, typename Dim>
concept QuantityOf = Quantity<T> && Dimension<Dim> && equivalent<typename T::dimension, Dim>; concept QuantityOf = Quantity<Q> && Dimension<Dim> && equivalent<typename Q::dimension, Dim>;
template<typename Q1, typename Q2>
concept QuantityEquivalentTo = Quantity<Q1> && QuantityOf<Q2, typename Q1::dimension>;
} // namespace units } // namespace units

View File

@ -22,7 +22,6 @@
#pragma once #pragma once
#include <units/concepts.h>
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
@ -37,9 +36,13 @@ namespace units {
* *
* @tparam Rep a representation type for which a type trait is defined * @tparam Rep a representation type for which a type trait is defined
*/ */
template<ScalableNumber Rep> template<typename Rep>
inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>; inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
template<typename T>
requires requires { typename T::value_type; }
inline constexpr bool treat_as_floating_point<T> = treat_as_floating_point<typename T::value_type>;
/** /**
* @brief A type trait that defines zero, one, min, and max for a representation type * @brief A type trait that defines zero, one, min, and max for a representation type
* *
@ -49,7 +52,7 @@ inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
* *
* @tparam Rep a representation type for which a type trait is defined * @tparam Rep a representation type for which a type trait is defined
*/ */
template<ScalableNumber Rep> template<typename Rep>
struct quantity_values { struct quantity_values {
static constexpr Rep zero() noexcept { return Rep(0); } static constexpr Rep zero() noexcept { return Rep(0); }
static constexpr Rep one() noexcept { return Rep(1); } static constexpr Rep one() noexcept { return Rep(1); }

View File

@ -48,7 +48,7 @@ struct dim_information : base_dimension<"information", bit> {};
template<typename T> template<typename T>
concept Information = QuantityOf<T, dim_information>; concept Information = QuantityOf<T, dim_information>;
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_information> U, QuantityValue Rep = double>
using information = quantity<dim_information, U, Rep>; using information = quantity<dim_information, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -41,7 +41,7 @@ struct pebibit_per_second : deduced_unit<pebibit_per_second, dim_bitrate, pebibi
template<typename T> template<typename T>
concept Bitrate = QuantityOf<T, dim_bitrate>; concept Bitrate = QuantityOf<T, dim_bitrate>;
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_bitrate> U, QuantityValue Rep = double>
using bitrate = quantity<dim_bitrate, U, Rep>; using bitrate = quantity<dim_bitrate, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -35,7 +35,7 @@ struct dim_angle : base_dimension<"A", U> {};
template<typename T> template<typename T>
concept Angle = QuantityOfT<T, dim_angle>; concept Angle = QuantityOfT<T, dim_angle>;
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_angle<>> U, QuantityValue Rep = double>
using angle = quantity<dim_angle<>, U, Rep>; using angle = quantity<dim_angle<>, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -40,7 +40,7 @@ struct dim_one : derived_dimension<dim_one, one> {};
template<typename T> template<typename T>
concept Dimensionless = QuantityOf<T, dim_one>; concept Dimensionless = QuantityOf<T, dim_one>;
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_one> U, QuantityValue Rep = double>
using dimensionless = quantity<dim_one, U, Rep>; using dimensionless = quantity<dim_one, U, Rep>;
} // namespace units } // namespace units

View File

@ -29,35 +29,35 @@
namespace units::physical::natural { namespace units::physical::natural {
struct dim_length : physical::dim_length<inverted_gigaelectronvolt> {}; struct dim_length : physical::dim_length<inverted_gigaelectronvolt> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
struct dim_time : physical::dim_time<inverted_gigaelectronvolt> {}; struct dim_time : physical::dim_time<inverted_gigaelectronvolt> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_time> U, QuantityValue Rep = double>
using time = quantity<dim_time, U, Rep>; using time = quantity<dim_time, U, Rep>;
struct dim_mass : physical::dim_mass<gigaelectronvolt> {}; struct dim_mass : physical::dim_mass<gigaelectronvolt> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>; using mass = quantity<dim_mass, U, Rep>;
struct dim_speed : physical::dim_speed<dim_speed, one, dim_length, dim_time> {}; struct dim_speed : physical::dim_speed<dim_speed, one, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>; using speed = quantity<dim_speed, U, Rep>;
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, gigaelectronvolt, dim_length, dim_time> {}; struct dim_acceleration : physical::dim_acceleration<dim_acceleration, gigaelectronvolt, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>; using acceleration = quantity<dim_acceleration, U, Rep>;
struct dim_force : physical::dim_force<dim_force, square_gigaelectronvolt, dim_mass, dim_acceleration> {}; struct dim_force : physical::dim_force<dim_force, square_gigaelectronvolt, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>; using force = quantity<dim_force, U, Rep>;
struct dim_momentum : physical::dim_momentum<dim_momentum, gigaelectronvolt, dim_mass, dim_speed> {}; struct dim_momentum : physical::dim_momentum<dim_momentum, gigaelectronvolt, dim_mass, dim_speed> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_momentum> U, QuantityValue Rep = double>
using momentum = quantity<dim_momentum, U, Rep>; using momentum = quantity<dim_momentum, U, Rep>;
struct dim_energy : physical::dim_energy<dim_energy, gigaelectronvolt, dim_force, dim_length> {}; struct dim_energy : physical::dim_energy<dim_energy, gigaelectronvolt, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_force> U, QuantityValue Rep = double>
using energy = quantity<dim_force, U, Rep>; using energy = quantity<dim_force, U, Rep>;
// Typical UDLs will not work here as the same units are reused by many quantities. // Typical UDLs will not work here as the same units are reused by many quantities.

View File

@ -26,7 +26,7 @@
namespace units::physical::natural { namespace units::physical::natural {
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<one, Rep>(1); inline constexpr auto speed_of_light = speed<one, Rep>(1);
} // namespace units::physical::natural } // namespace units::physical::natural

View File

@ -32,7 +32,7 @@ struct mole : named_unit<mole, "mol", prefix> {};
struct dim_amount_of_substance : physical::dim_amount_of_substance<mole> {}; struct dim_amount_of_substance : physical::dim_amount_of_substance<mole> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_amount_of_substance> U, QuantityValue Rep = double>
using amount_of_substance = quantity<dim_amount_of_substance, U, Rep>; using amount_of_substance = quantity<dim_amount_of_substance, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -52,7 +52,7 @@ struct yottaampere : prefixed_unit<yottaampere, yotta, ampere> {};
struct dim_electric_current : physical::dim_electric_current<ampere> {}; struct dim_electric_current : physical::dim_electric_current<ampere> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_electric_current> U, QuantityValue Rep = double>
using electric_current = quantity<dim_electric_current, U, Rep>; using electric_current = quantity<dim_electric_current, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -54,7 +54,7 @@ struct astronomical_unit : named_scaled_unit<astronomical_unit, "au", no_prefix,
struct dim_length : physical::dim_length<metre> {}; struct dim_length : physical::dim_length<metre> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -52,7 +52,7 @@ struct yottacandela : prefixed_unit<yottacandela, yotta, candela> {};
struct dim_luminous_intensity : physical::dim_luminous_intensity<candela> {}; struct dim_luminous_intensity : physical::dim_luminous_intensity<candela> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_luminous_intensity> U, QuantityValue Rep = double>
using luminous_intensity = quantity<dim_luminous_intensity, U, Rep>; using luminous_intensity = quantity<dim_luminous_intensity, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -76,7 +76,7 @@ struct dalton : named_scaled_unit<dalton, "Da", no_prefix, ratio(16'605'390'666'
struct dim_mass : physical::dim_mass<kilogram> {}; struct dim_mass : physical::dim_mass<kilogram> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>; using mass = quantity<dim_mass, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -31,7 +31,7 @@ struct kelvin : named_unit<kelvin, "K", no_prefix> {};
struct dim_thermodynamic_temperature : physical::dim_thermodynamic_temperature<kelvin> {}; struct dim_thermodynamic_temperature : physical::dim_thermodynamic_temperature<kelvin> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_thermodynamic_temperature> U, QuantityValue Rep = double>
using thermodynamic_temperature = quantity<dim_thermodynamic_temperature, U, Rep>; using thermodynamic_temperature = quantity<dim_thermodynamic_temperature, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -43,7 +43,7 @@ struct day : named_scaled_unit<day, "d", no_prefix, ratio(24), hour> {};
struct dim_time : physical::dim_time<second> {}; struct dim_time : physical::dim_time<second> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_time> U, QuantityValue Rep = double>
using time = quantity<dim_time, U, Rep>; using time = quantity<dim_time, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -31,7 +31,7 @@ using si::centimetre;
struct dim_length : physical::dim_length<centimetre> {}; struct dim_length : physical::dim_length<centimetre> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -31,7 +31,7 @@ using si::gram;
struct dim_mass : physical::dim_mass<gram> {}; struct dim_mass : physical::dim_mass<gram> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>; using mass = quantity<dim_mass, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -31,7 +31,7 @@ namespace units::physical::si::cgs {
struct gal : named_unit<gal, "Gal", si::prefix> {}; struct gal : named_unit<gal, "Gal", si::prefix> {};
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, gal, dim_length, dim_time> {}; struct dim_acceleration : physical::dim_acceleration<dim_acceleration, gal, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>; using acceleration = quantity<dim_acceleration, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ using si::square_centimetre;
struct dim_area : physical::dim_area<dim_area, square_centimetre, dim_length> {}; struct dim_area : physical::dim_area<dim_area, square_centimetre, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_area> U, QuantityValue Rep = double>
using area = quantity<dim_area, U, Rep>; using area = quantity<dim_area, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -33,7 +33,7 @@ struct erg : named_unit<erg, "erg", si::prefix> {};
struct dim_energy : physical::dim_energy<dim_energy, erg, dim_force, dim_length> {}; struct dim_energy : physical::dim_energy<dim_energy, erg, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_energy> U, QuantityValue Rep = double>
using energy = quantity<dim_energy, U, Rep>; using energy = quantity<dim_energy, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct dyne : named_unit<dyne, "dyn", si::prefix> {};
struct dim_force : physical::dim_force<dim_force, dyne, dim_mass, dim_acceleration> {}; struct dim_force : physical::dim_force<dim_force, dyne, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>; using force = quantity<dim_force, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -33,7 +33,7 @@ struct erg_per_second : unit<erg_per_second> {};
struct dim_power : physical::dim_power<dim_power, erg_per_second, dim_energy, dim_time> {}; struct dim_power : physical::dim_power<dim_power, erg_per_second, dim_energy, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_power> U, QuantityValue Rep = double>
using power = quantity<dim_power, U, Rep>; using power = quantity<dim_power, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct barye : named_unit<barye, "Ba", si::prefix> {};
struct dim_pressure : physical::dim_pressure<dim_pressure, barye, dim_force, dim_area> {}; struct dim_pressure : physical::dim_pressure<dim_pressure, barye, dim_force, dim_area> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_pressure> U, QuantityValue Rep = double>
using pressure = quantity<dim_pressure, U, Rep>; using pressure = quantity<dim_pressure, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si::cgs {
struct centimetre_per_second : unit<centimetre_per_second> {}; struct centimetre_per_second : unit<centimetre_per_second> {};
struct dim_speed : physical::dim_speed<dim_speed, centimetre_per_second, dim_length, dim_time> {}; struct dim_speed : physical::dim_speed<dim_speed, centimetre_per_second, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>; using speed = quantity<dim_speed, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,31 +32,31 @@
namespace units::physical::si::si2019 { namespace units::physical::si::si2019 {
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto planck_constant = energy<joule, Rep>(6.62607015e-34) * time<second, Rep>(1); inline constexpr auto planck_constant = energy<joule, Rep>(6.62607015e-34) * time<second, Rep>(1);
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto reduced_planck_constant = energy<gigaelectronvolt, Rep>(6.582119569e-10) * time<second, Rep>(1); inline constexpr auto reduced_planck_constant = energy<gigaelectronvolt, Rep>(6.582119569e-10) * time<second, Rep>(1);
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto elementary_charge = electric_charge<coulomb, Rep>(1.602176634e-19); inline constexpr auto elementary_charge = electric_charge<coulomb, Rep>(1.602176634e-19);
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto boltzmann_constant = energy<joule, Rep>(1.380649e-23) / temperature<kelvin, Rep>(1); inline constexpr auto boltzmann_constant = energy<joule, Rep>(1.380649e-23) / temperature<kelvin, Rep>(1);
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto avogadro_constant = Rep(6.02214076e23) / substance<mole, Rep>(1); inline constexpr auto avogadro_constant = Rep(6.02214076e23) / substance<mole, Rep>(1);
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto speed_of_light = speed<metre_per_second, Rep>(299'792'458); inline constexpr auto speed_of_light = speed<metre_per_second, Rep>(299'792'458);
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto hyperfine_structure_transition_frequency = frequency<hertz, Rep>(9'192'631'770); inline constexpr auto hyperfine_structure_transition_frequency = frequency<hertz, Rep>(9'192'631'770);
// template<ScalableNumber Rep = double> // template<QuantityValue Rep = double>
// inline constexpr auto luminous_efficacy = 683_q_lm / 1_q_W; // inline constexpr auto luminous_efficacy = 683_q_lm / 1_q_W;
template<ScalableNumber Rep = double> template<QuantityValue Rep = double>
inline constexpr auto standard_gravity = acceleration<metre_per_second_sq, Rep>(9.80665); inline constexpr auto standard_gravity = acceleration<metre_per_second_sq, Rep>(9.80665);
} // namespace units::physical::si::si2019 } // namespace units::physical::si::si2019

View File

@ -53,7 +53,7 @@ struct yottagray : prefixed_unit<yottagray, yotta, gray> {};
struct dim_absorbed_dose : physical::dim_absorbed_dose<dim_absorbed_dose, gray, dim_energy, dim_mass> {}; struct dim_absorbed_dose : physical::dim_absorbed_dose<dim_absorbed_dose, gray, dim_energy, dim_mass> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_absorbed_dose> U, QuantityValue Rep = double>
using absorbed_dose = quantity<dim_absorbed_dose, U, Rep>; using absorbed_dose = quantity<dim_absorbed_dose, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -31,7 +31,7 @@ namespace units::physical::si {
struct metre_per_second_sq : unit<metre_per_second_sq> {}; struct metre_per_second_sq : unit<metre_per_second_sq> {};
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, metre_per_second_sq, dim_length, dim_time> {}; struct dim_acceleration : physical::dim_acceleration<dim_acceleration, metre_per_second_sq, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>; using acceleration = quantity<dim_acceleration, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct radian_per_second : named_unit<radian_per_second, basic_symbol_text{"ω",
struct dim_angular_velocity : physical::dim_angular_velocity<dim_angular_velocity, radian_per_second, dim_angle<>, dim_time> {}; struct dim_angular_velocity : physical::dim_angular_velocity<dim_angular_velocity, radian_per_second, dim_angle<>, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_angular_velocity> U, QuantityValue Rep = double>
using angular_velocity = quantity<dim_angular_velocity, U, Rep>; using angular_velocity = quantity<dim_angular_velocity, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -54,7 +54,7 @@ struct square_yottametre : deduced_unit<square_yottametre, dim_area, yottametre>
struct hectare : alias_unit<square_hectometre, "ha", no_prefix> {}; struct hectare : alias_unit<square_hectometre, "ha", no_prefix> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_area> U, QuantityValue Rep = double>
using area = quantity<dim_area, U, Rep>; using area = quantity<dim_area, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottafarad : prefixed_unit<yottafarad, yotta, farad> {};
struct dim_capacitance : physical::dim_capacitance<dim_capacitance, farad, dim_electric_charge, dim_voltage> {}; struct dim_capacitance : physical::dim_capacitance<dim_capacitance, farad, dim_electric_charge, dim_voltage> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_capacitance> U, QuantityValue Rep = double>
using capacitance = quantity<dim_capacitance, U, Rep>; using capacitance = quantity<dim_capacitance, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -56,7 +56,7 @@ struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", prefix, ratio(1, 60, -6
struct dim_catalytic_activity : physical::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_amount_of_substance> {}; struct dim_catalytic_activity : physical::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_amount_of_substance> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_catalytic_activity> U, QuantityValue Rep = double>
using catalytic_activity = quantity<dim_catalytic_activity, U, Rep>; using catalytic_activity = quantity<dim_catalytic_activity, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -36,10 +36,10 @@ struct coulomb_per_metre_sq : unit<coulomb_per_metre_sq> {};
struct dim_charge_density : physical::dim_charge_density<dim_charge_density, coulomb_per_metre_cub, dim_electric_charge, dim_length> {}; struct dim_charge_density : physical::dim_charge_density<dim_charge_density, coulomb_per_metre_cub, dim_electric_charge, dim_length> {};
struct dim_surface_charge_density : physical::dim_surface_charge_density<dim_surface_charge_density, coulomb_per_metre_sq, dim_electric_charge, dim_length> {}; struct dim_surface_charge_density : physical::dim_surface_charge_density<dim_surface_charge_density, coulomb_per_metre_sq, dim_electric_charge, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_charge_density> U, QuantityValue Rep = double>
using charge_density = quantity<dim_charge_density, U, Rep>; using charge_density = quantity<dim_charge_density, U, Rep>;
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_surface_charge_density> U, QuantityValue Rep = double>
using surface_charge_density = quantity<dim_surface_charge_density, U, Rep>; using surface_charge_density = quantity<dim_surface_charge_density, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct mol_per_metre_cub : unit<mol_per_metre_cub> {}; struct mol_per_metre_cub : unit<mol_per_metre_cub> {};
struct dim_concentration : physical::dim_concentration<dim_concentration, mol_per_metre_cub, dim_amount_of_substance, dim_length> {}; struct dim_concentration : physical::dim_concentration<dim_concentration, mol_per_metre_cub, dim_amount_of_substance, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_concentration> U, QuantityValue Rep = double>
using concentration = quantity<dim_concentration, U, Rep>; using concentration = quantity<dim_concentration, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -49,7 +49,7 @@ struct yottasiemens : prefixed_unit<yottasiemens, yotta, siemens> {};
struct dim_conductance : physical::dim_conductance<dim_conductance, siemens, dim_resistance> {}; struct dim_conductance : physical::dim_conductance<dim_conductance, siemens, dim_resistance> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_conductance> U, QuantityValue Rep = double>
using conductance = quantity<dim_conductance, U, Rep>; using conductance = quantity<dim_conductance, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct ampere_per_metre_sq : unit<ampere_per_metre_sq> {};
struct dim_current_density : physical::dim_current_density<dim_current_density, ampere_per_metre_sq, dim_electric_current, dim_length> {}; struct dim_current_density : physical::dim_current_density<dim_current_density, ampere_per_metre_sq, dim_electric_current, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_current_density> U, QuantityValue Rep = double>
using current_density = quantity<dim_current_density, U, Rep>; using current_density = quantity<dim_current_density, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct kilogram_per_metre_cub : unit<kilogram_per_metre_cub> {};
struct dim_density : physical::dim_density<dim_density, kilogram_per_metre_cub, dim_mass, dim_length> {}; struct dim_density : physical::dim_density<dim_density, kilogram_per_metre_cub, dim_mass, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_density> U, QuantityValue Rep = double>
using density = quantity<dim_density, U, Rep>; using density = quantity<dim_density, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct pascal_second : unit<pascal_second> {}; struct pascal_second : unit<pascal_second> {};
struct dim_dynamic_viscosity : physical::dim_dynamic_viscosity<dim_dynamic_viscosity, pascal_second, dim_pressure, dim_time> {}; struct dim_dynamic_viscosity : physical::dim_dynamic_viscosity<dim_dynamic_viscosity, pascal_second, dim_pressure, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_dynamic_viscosity> U, QuantityValue Rep = double>
using dynamic_viscosity = quantity<dim_dynamic_viscosity, U, Rep>; using dynamic_viscosity = quantity<dim_dynamic_viscosity, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -33,7 +33,7 @@ struct coulomb : named_unit<coulomb, "C", prefix> {};
struct dim_electric_charge : physical::dim_electric_charge<dim_electric_charge, coulomb, dim_time, dim_electric_current> {}; struct dim_electric_charge : physical::dim_electric_charge<dim_electric_charge, coulomb, dim_time, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_electric_charge> U, QuantityValue Rep = double>
using electric_charge = quantity<dim_electric_charge, U, Rep>; using electric_charge = quantity<dim_electric_charge, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -31,7 +31,7 @@ namespace units::physical::si {
struct volt_per_metre : unit<volt_per_metre> {}; struct volt_per_metre : unit<volt_per_metre> {};
struct dim_electric_field_strength : physical::dim_electric_field_strength<dim_electric_field_strength, volt_per_metre, dim_voltage, dim_length> {}; struct dim_electric_field_strength : physical::dim_electric_field_strength<dim_electric_field_strength, volt_per_metre, dim_voltage, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_electric_field_strength> U, QuantityValue Rep = double>
using electric_field_strength = quantity<dim_electric_field_strength, U, Rep>; using electric_field_strength = quantity<dim_electric_field_strength, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -52,7 +52,7 @@ struct gigaelectronvolt : prefixed_unit<gigaelectronvolt, giga, electronvolt> {}
struct dim_energy : physical::dim_energy<dim_energy, joule, dim_force, dim_length> {}; struct dim_energy : physical::dim_energy<dim_energy, joule, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_energy> U, QuantityValue Rep = double>
using energy = quantity<dim_energy, U, Rep>; using energy = quantity<dim_energy, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottanewton : prefixed_unit<yottanewton, yotta, newton> {};
struct dim_force : physical::dim_force<dim_force, newton, dim_mass, dim_acceleration> {}; struct dim_force : physical::dim_force<dim_force, newton, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>; using force = quantity<dim_force, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -48,7 +48,7 @@ struct yottahertz : prefixed_unit<yottahertz, yotta, hertz> {};
struct dim_frequency : physical::dim_frequency<dim_frequency, hertz, dim_time> {}; struct dim_frequency : physical::dim_frequency<dim_frequency, hertz, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_frequency> U, QuantityValue Rep = double>
using frequency = quantity<dim_frequency, U, Rep>; using frequency = quantity<dim_frequency, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -39,13 +39,13 @@ struct dim_heat_capacity : physical::dim_heat_capacity<dim_heat_capacity, joule_
struct dim_specific_heat_capacity : physical::dim_specific_heat_capacity<dim_specific_heat_capacity, joule_per_kilogram_kelvin, dim_heat_capacity, dim_mass> {}; struct dim_specific_heat_capacity : physical::dim_specific_heat_capacity<dim_specific_heat_capacity, joule_per_kilogram_kelvin, dim_heat_capacity, dim_mass> {};
struct dim_molar_heat_capacity : physical::dim_molar_heat_capacity<dim_molar_heat_capacity, joule_per_mole_kelvin, dim_heat_capacity, dim_amount_of_substance> {}; struct dim_molar_heat_capacity : physical::dim_molar_heat_capacity<dim_molar_heat_capacity, joule_per_mole_kelvin, dim_heat_capacity, dim_amount_of_substance> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_heat_capacity> U, QuantityValue Rep = double>
using heat_capacity = quantity<dim_heat_capacity, U, Rep>; using heat_capacity = quantity<dim_heat_capacity, U, Rep>;
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_specific_heat_capacity> U, QuantityValue Rep = double>
using specific_heat_capacity = quantity<dim_specific_heat_capacity, U, Rep>; using specific_heat_capacity = quantity<dim_specific_heat_capacity, U, Rep>;
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_molar_heat_capacity> U, QuantityValue Rep = double>
using molar_heat_capacity = quantity<dim_molar_heat_capacity, U, Rep>; using molar_heat_capacity = quantity<dim_molar_heat_capacity, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -50,7 +50,7 @@ struct yottahenry : prefixed_unit<yottahenry, yotta, henry> {};
struct dim_inductance : physical::dim_inductance<dim_inductance, henry, dim_magnetic_flux, dim_electric_current> {}; struct dim_inductance : physical::dim_inductance<dim_inductance, henry, dim_magnetic_flux, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_inductance> U, QuantityValue Rep = double>
using inductance = quantity<dim_inductance, U, Rep>; using inductance = quantity<dim_inductance, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct candela_per_metre_sq : unit<candela_per_metre_sq> {}; struct candela_per_metre_sq : unit<candela_per_metre_sq> {};
struct dim_luminance : physical::dim_luminance<dim_luminance, candela_per_metre_sq, dim_luminous_intensity, dim_length> {}; struct dim_luminance : physical::dim_luminance<dim_luminance, candela_per_metre_sq, dim_luminous_intensity, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_luminance> U, QuantityValue Rep = double>
using luminance = quantity<dim_luminance, U, Rep>; using luminance = quantity<dim_luminance, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -50,7 +50,7 @@ struct yottaweber : prefixed_unit<yottaweber, yotta, weber> {};
struct dim_magnetic_flux : physical::dim_magnetic_flux<dim_magnetic_flux, weber, dim_magnetic_induction, dim_area> {}; struct dim_magnetic_flux : physical::dim_magnetic_flux<dim_magnetic_flux, weber, dim_magnetic_induction, dim_area> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_magnetic_flux> U, QuantityValue Rep = double>
using magnetic_flux = quantity<dim_magnetic_flux, U, Rep>; using magnetic_flux = quantity<dim_magnetic_flux, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -54,7 +54,7 @@ struct gauss : named_scaled_unit<gauss, "G", prefix, ratio(1, 10'000), tesla> {}
struct dim_magnetic_induction : physical::dim_magnetic_induction<dim_magnetic_induction, tesla, dim_voltage, dim_time, dim_length> {}; struct dim_magnetic_induction : physical::dim_magnetic_induction<dim_magnetic_induction, tesla, dim_voltage, dim_time, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_magnetic_induction> U, QuantityValue Rep = double>
using magnetic_induction = quantity<dim_magnetic_induction, U, Rep>; using magnetic_induction = quantity<dim_magnetic_induction, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct joule_per_mole : unit<joule_per_mole> {};
struct dim_molar_energy : physical::dim_molar_energy<dim_molar_energy, joule_per_mole, dim_energy, dim_amount_of_substance> {}; struct dim_molar_energy : physical::dim_molar_energy<dim_molar_energy, joule_per_mole, dim_energy, dim_amount_of_substance> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_molar_energy> U, QuantityValue Rep = double>
using molar_energy = quantity<dim_molar_energy, U, Rep>; using molar_energy = quantity<dim_molar_energy, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ namespace units::physical::si {
struct kilogram_metre_per_second : unit<kilogram_metre_per_second> {}; struct kilogram_metre_per_second : unit<kilogram_metre_per_second> {};
struct dim_momentum : physical::dim_momentum<dim_momentum, kilogram_metre_per_second, dim_mass, dim_speed> {}; struct dim_momentum : physical::dim_momentum<dim_momentum, kilogram_metre_per_second, dim_mass, dim_speed> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_momentum> U, QuantityValue Rep = double>
using momentum = quantity<dim_momentum, U, Rep>; using momentum = quantity<dim_momentum, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -33,7 +33,7 @@ struct henry_per_metre : unit<henry_per_metre> {};
struct dim_permeability : physical::dim_permeability<dim_permeability, henry_per_metre, dim_inductance, dim_length> {}; struct dim_permeability : physical::dim_permeability<dim_permeability, henry_per_metre, dim_inductance, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_permeability> U, QuantityValue Rep = double>
using permeability = quantity<dim_permeability, U, Rep>; using permeability = quantity<dim_permeability, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -33,7 +33,7 @@ struct farad_per_metre : unit<farad_per_metre> {};
struct dim_permittivity : physical::dim_permittivity<dim_permittivity, farad_per_metre, dim_capacitance, dim_length> {}; struct dim_permittivity : physical::dim_permittivity<dim_permittivity, farad_per_metre, dim_capacitance, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_permittivity> U, QuantityValue Rep = double>
using permittivity = quantity<dim_permittivity, U, Rep>; using permittivity = quantity<dim_permittivity, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -49,7 +49,7 @@ struct yottawatt : prefixed_unit<yottawatt, yotta, watt> {};
struct dim_power : physical::dim_power<dim_power, watt, dim_energy, dim_time> {}; struct dim_power : physical::dim_power<dim_power, watt, dim_energy, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_power> U, QuantityValue Rep = double>
using power = quantity<dim_power, U, Rep>; using power = quantity<dim_power, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottapascal : prefixed_unit<yottapascal, yotta, pascal> {};
struct dim_pressure : physical::dim_pressure<dim_pressure, pascal, dim_force, dim_area> {}; struct dim_pressure : physical::dim_pressure<dim_pressure, pascal, dim_force, dim_area> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_pressure> U, QuantityValue Rep = double>
using pressure = quantity<dim_pressure, U, Rep>; using pressure = quantity<dim_pressure, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -50,7 +50,7 @@ struct yottaohm : prefixed_unit<yottaohm, yotta, ohm> {};
struct dim_resistance : physical::dim_resistance<dim_resistance, ohm, dim_voltage, dim_electric_current> {}; struct dim_resistance : physical::dim_resistance<dim_resistance, ohm, dim_voltage, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_resistance> U, QuantityValue Rep = double>
using resistance = quantity<dim_resistance, U, Rep>; using resistance = quantity<dim_resistance, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct dim_speed : physical::dim_speed<dim_speed, metre_per_second, dim_length,
struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {}; struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>; using speed = quantity<dim_speed, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ struct newton_per_metre : unit<newton_per_metre> {};
struct dim_surface_tension : physical::dim_surface_tension<dim_surface_tension, newton_per_metre, dim_force, dim_length> {}; struct dim_surface_tension : physical::dim_surface_tension<dim_surface_tension, newton_per_metre, dim_force, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_surface_tension> U, QuantityValue Rep = double>
using surface_tension = quantity<dim_surface_tension, U, Rep>; using surface_tension = quantity<dim_surface_tension, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -33,7 +33,7 @@ struct watt_per_metre_kelvin : unit<watt_per_metre_kelvin> {};
struct dim_thermal_conductivity : physical::dim_thermal_conductivity<dim_thermal_conductivity, watt_per_metre_kelvin, dim_power, dim_length, dim_thermodynamic_temperature> {}; struct dim_thermal_conductivity : physical::dim_thermal_conductivity<dim_thermal_conductivity, watt_per_metre_kelvin, dim_power, dim_length, dim_thermodynamic_temperature> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_thermal_conductivity> U, QuantityValue Rep = double>
using thermal_conductivity = quantity<dim_thermal_conductivity, U, Rep>; using thermal_conductivity = quantity<dim_thermal_conductivity, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct newton_metre : named_unit<newton_metre, "Nm", prefix> {};
struct dim_torque : physical::dim_torque<dim_torque, newton_metre, dim_energy, dim_angle<>> {}; struct dim_torque : physical::dim_torque<dim_torque, newton_metre, dim_energy, dim_angle<>> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_torque> U, QuantityValue Rep = double>
using torque = quantity<dim_torque, U, Rep>; using torque = quantity<dim_torque, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -54,7 +54,7 @@ struct yottavolt : prefixed_unit<yottavolt, yotta, volt> {};
struct dim_voltage : physical::dim_voltage<dim_voltage, volt, dim_power, dim_electric_current> {}; struct dim_voltage : physical::dim_voltage<dim_voltage, volt, dim_power, dim_electric_current> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_voltage> U, QuantityValue Rep = double>
using voltage = quantity<dim_voltage, U, Rep>; using voltage = quantity<dim_voltage, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -74,7 +74,7 @@ struct exalitre : prefixed_unit<petalitre, exa, litre> {};
struct zettalitre : prefixed_alias_unit<cubic_megametre, zetta, litre> {}; struct zettalitre : prefixed_alias_unit<cubic_megametre, zetta, litre> {};
struct yottalitre : prefixed_unit<yottalitre, yotta, litre> {}; struct yottalitre : prefixed_unit<yottalitre, yotta, litre> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_volume> U, QuantityValue Rep = double>
using volume = quantity<dim_volume, U, Rep>; using volume = quantity<dim_volume, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -51,7 +51,7 @@ struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", no_prefix, r
struct dim_length : physical::dim_length<foot> {}; struct dim_length : physical::dim_length<foot> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_length> U, QuantityValue Rep = double>
using length = quantity<dim_length, U, Rep>; using length = quantity<dim_length, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ struct pound : named_scaled_unit<pound, "lb", no_prefix, ratio(45'359'237, 100'0
struct dim_mass : physical::dim_mass<pound> {}; struct dim_mass : physical::dim_mass<pound> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_mass> U, QuantityValue Rep = double>
using mass = quantity<dim_mass, U, Rep>; using mass = quantity<dim_mass, U, Rep>;
struct grain : named_scaled_unit<grain, "gr", no_prefix, ratio(1, 7000), pound>{}; struct grain : named_scaled_unit<grain, "gr", no_prefix, ratio(1, 7000), pound>{};

View File

@ -31,7 +31,7 @@ namespace units::physical::si::fps {
struct foot_per_second_sq : unit<foot_per_second_sq> {}; struct foot_per_second_sq : unit<foot_per_second_sq> {};
struct dim_acceleration : physical::dim_acceleration<dim_acceleration, foot_per_second_sq, dim_length, dim_time> {}; struct dim_acceleration : physical::dim_acceleration<dim_acceleration, foot_per_second_sq, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_acceleration> U, QuantityValue Rep = double>
using acceleration = quantity<dim_acceleration, U, Rep>; using acceleration = quantity<dim_acceleration, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -32,7 +32,7 @@ struct square_foot : unit<square_foot> {};
struct dim_area : physical::dim_area<dim_area, square_foot, dim_length> {}; struct dim_area : physical::dim_area<dim_area, square_foot, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_area> U, QuantityValue Rep = double>
using area = quantity<dim_area, U, Rep>; using area = quantity<dim_area, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -33,7 +33,7 @@ struct pound_per_foot_cub : unit<pound_per_foot_cub> {};
struct dim_density : physical::dim_density<dim_density, pound_per_foot_cub, dim_mass, dim_length> {}; struct dim_density : physical::dim_density<dim_density, pound_per_foot_cub, dim_mass, dim_length> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_density> U, QuantityValue Rep = double>
using density = quantity<dim_density, U, Rep>; using density = quantity<dim_density, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -39,7 +39,7 @@ struct foot_pound_force : noble_deduced_unit<foot_pound_force, dim_energy, pound
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_energy> U, QuantityValue Rep = double>
using energy = quantity<dim_energy, U, Rep>; using energy = quantity<dim_energy, U, Rep>;

View File

@ -43,7 +43,7 @@ struct kip : alias_unit<kilopound_force, "klbf", no_prefix> {};
struct dim_force : physical::dim_force<dim_force, poundal, dim_mass, dim_acceleration> {}; struct dim_force : physical::dim_force<dim_force, poundal, dim_mass, dim_acceleration> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_force> U, QuantityValue Rep = double>
using force = quantity<dim_force, U, Rep>; using force = quantity<dim_force, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -37,7 +37,7 @@ struct foot_pound_force_per_second : deduced_unit<foot_pound_force_per_second, d
struct horse_power : named_scaled_unit<horse_power, "hp", no_prefix, ratio(550), foot_pound_force_per_second> {}; struct horse_power : named_scaled_unit<horse_power, "hp", no_prefix, ratio(550), foot_pound_force_per_second> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_power> U, QuantityValue Rep = double>
using power = quantity<dim_power, U, Rep>; using power = quantity<dim_power, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,7 +34,7 @@ struct poundal_per_foot_sq : unit<poundal_per_foot_sq> {};
struct dim_pressure : physical::dim_pressure<dim_pressure, poundal_per_foot_sq, dim_force, dim_area> {}; struct dim_pressure : physical::dim_pressure<dim_pressure, poundal_per_foot_sq, dim_force, dim_area> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_pressure> U, QuantityValue Rep = double>
using pressure = quantity<dim_pressure, U, Rep>; using pressure = quantity<dim_pressure, U, Rep>;
struct pound_force_per_foot_sq : named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", si::prefix, ratio(32'174'049, 1'000'000), poundal_per_foot_sq> {}; struct pound_force_per_foot_sq : named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", si::prefix, ratio(32'174'049, 1'000'000), poundal_per_foot_sq> {};

View File

@ -32,7 +32,7 @@ namespace units::physical::si::fps {
struct foot_per_second : unit<foot_per_second> {}; struct foot_per_second : unit<foot_per_second> {};
struct dim_speed : physical::dim_speed<dim_speed, foot_per_second, dim_length, dim_time> {}; struct dim_speed : physical::dim_speed<dim_speed, foot_per_second, dim_length, dim_time> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_speed> U, QuantityValue Rep = double>
using speed = quantity<dim_speed, U, Rep>; using speed = quantity<dim_speed, U, Rep>;
struct mile_per_hour : deduced_unit<mile_per_hour, dim_speed, mile, hour>{}; struct mile_per_hour : deduced_unit<mile_per_hour, dim_speed, mile, hour>{};

View File

@ -33,7 +33,7 @@ struct dim_volume : physical::dim_volume<dim_volume, cubic_foot, dim_length> {};
struct cubic_yard : deduced_unit<cubic_yard, dim_volume, yard> {}; struct cubic_yard : deduced_unit<cubic_yard, dim_volume, yard> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_volume> U, QuantityValue Rep = double>
using volume = quantity<dim_volume, U, Rep>; using volume = quantity<dim_volume, U, Rep>;
inline namespace literals { inline namespace literals {

View File

@ -34,19 +34,52 @@
namespace units { namespace units {
namespace detail { template<typename T>
concept floating_point_ = // exposition only
(Quantity<T> && treat_as_floating_point<typename T::rep>) ||
(!Quantity<T> && treat_as_floating_point<T>);
template<typename From, typename To> template<typename From, typename To>
concept safe_convertible = // exposition only concept safe_convertible_to_ = // exposition only
!(Quantity<From>) &&
!(Quantity<To>) &&
std::convertible_to<From, To> && std::convertible_to<From, To> &&
(treat_as_floating_point<To> || (!treat_as_floating_point<From>)); (floating_point_<To> || (!floating_point_<From>));
template<typename Rep, typename QuantityFrom, typename QuantityTo> // QFrom ratio is an exact multiple of QTo
concept safe_divisible = // exposition only template<typename QFrom, typename QTo>
treat_as_floating_point<Rep> || concept harmonic_ = // exposition only
is_integral(quantity_ratio(QuantityFrom{}) / quantity_ratio(QuantityTo{})); Quantity<QFrom> &&
Quantity<QTo> &&
requires(QFrom from, QTo to) { requires is_integral(detail::quantity_ratio(from) / detail::quantity_ratio(to)); };
} // namespace detail template<typename QFrom, typename QTo>
concept safe_castable_to_ = // exposition only
Quantity<QFrom> &&
QuantityOf<QTo, typename QFrom::dimension> &&
scalable_with_<typename QFrom::rep, typename QTo::rep> &&
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
template<typename Func, typename T, typename U>
concept quantity_value_for_ =
std::regular_invocable<Func, T, U> &&
QuantityValue<std::invoke_result_t<Func, T, U>>;
template<typename T, typename Func, typename U, typename V>
concept invoke_result_convertible_to_ =
QuantityValue<T> &&
quantity_value_for_<Func, U, V> &&
safe_convertible_to_<T, std::invoke_result_t<Func, U, V>>;
template<typename Func, typename Q, typename V>
concept have_quantity_for_ =
Quantity<Q> &&
(!Quantity<V>) &&
quantity_value_for_<Func, typename Q::rep, V>;
template<typename Func, Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<Func, typename Q1::rep, typename Q2::rep>
using common_quantity_for = common_quantity<Q1, Q2, std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
/** /**
* @brief A quantity * @brief A quantity
@ -58,63 +91,69 @@ concept safe_divisible = // exposition only
* @tparam U a measurement unit of the quantity * @tparam U a measurement unit of the quantity
* @tparam Rep a type to be used to represent values of a quantity * @tparam Rep a type to be used to represent values of a quantity
*/ */
template<Dimension D, UnitOf<D> U, ScalableNumber Rep = double> template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity { class quantity {
Rep value_; Rep value_;
public: public:
// member types
using dimension = D; using dimension = D;
using unit = U; using unit = U;
using rep = Rep; using rep = Rep;
// static member functions
[[nodiscard]] static constexpr quantity zero() noexcept
requires requires { quantity_values<rep>::zero(); }
{
return quantity(quantity_values<rep>::zero());
}
[[nodiscard]] static constexpr quantity one() noexcept
requires requires { quantity_values<rep>::one(); }
{
return quantity(quantity_values<rep>::one());
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<rep>::min(); }
{
return quantity(quantity_values<rep>::min());
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<rep>::max(); }
{
return quantity(quantity_values<rep>::max());
}
// construction, assignment, destruction
quantity() = default; quantity() = default;
quantity(const quantity&) = default; quantity(const quantity&) = default;
quantity(quantity&&) = default; quantity(quantity&&) = default;
template<ScalableNumber Value> template<safe_convertible_to_<rep> Value>
requires detail::safe_convertible<Value, rep> explicit(!(equivalent<quantity, dimensionless<::units::one, rep>>)) constexpr quantity(const Value& v) : value_(static_cast<rep>(v)) {}
constexpr explicit(!(equivalent<quantity, dimensionless<one, Rep>>)) quantity(const Value& v) : value_{static_cast<rep>(v)} {}
template<Quantity Q2> template<safe_castable_to_<quantity> Q2>
requires equivalent<D, typename Q2::dimension> && constexpr quantity(const Q2& q) : value_(quantity_cast<quantity>(q).count()) {}
detail::safe_convertible<typename Q2::rep, rep> &&
detail::safe_divisible<rep, Q2, quantity>
constexpr quantity(const Q2& q) : value_{quantity_cast<quantity>(q).count()} {}
quantity& operator=(const quantity&) = default; quantity& operator=(const quantity&) = default;
quantity& operator=(quantity&&) = default; quantity& operator=(quantity&&) = default;
// data access
[[nodiscard]] constexpr rep count() const noexcept { return value_; } [[nodiscard]] constexpr rep count() const noexcept { return value_; }
[[nodiscard]] static constexpr quantity zero() noexcept // member unary operators
requires requires { quantity_values<Rep>::zero(); } [[nodiscard]] constexpr quantity operator+() const
requires requires(rep v) { { +v } -> std::same_as<rep>; }
{ {
return quantity(quantity_values<Rep>::zero()); return *this;
} }
[[nodiscard]] static constexpr quantity one() noexcept [[nodiscard]] constexpr Quantity auto operator-() const
requires requires { quantity_values<Rep>::one(); }
{
return quantity(quantity_values<Rep>::one());
}
[[nodiscard]] static constexpr quantity min() noexcept
requires requires { quantity_values<Rep>::min(); }
{
return quantity(quantity_values<Rep>::min());
}
[[nodiscard]] static constexpr quantity max() noexcept
requires requires { quantity_values<Rep>::max(); }
{
return quantity(quantity_values<Rep>::max());
}
[[nodiscard]] constexpr quantity operator+() const { return *this; }
[[nodiscard]] constexpr quantity operator-() const
requires std::regular_invocable<std::negate<>, rep> requires std::regular_invocable<std::negate<>, rep>
{ {
return quantity(-count()); using ret = quantity<D, U, decltype(-count())>;
return ret(-count());
} }
constexpr quantity& operator++() constexpr quantity& operator++()
@ -143,51 +182,45 @@ public:
return quantity(value_--); return quantity(value_--);
} }
template<typename Rep2> constexpr quantity& operator+=(const quantity& q)
requires detail::safe_convertible<Rep2, rep> requires requires(rep a, rep b) { { a += b } -> std::same_as<rep&>; }
constexpr quantity& operator+=(const quantity<D, U, Rep2>& q)
{ {
value_ += q.count(); value_ += q.count();
return *this; return *this;
} }
template<typename Rep2> constexpr quantity& operator-=(const quantity& q)
requires detail::safe_convertible<Rep2, rep> requires requires(rep a, rep b) { { a -= b } -> std::same_as<rep&>; }
constexpr quantity& operator-=(const quantity<D, U, Rep2>& q)
{ {
value_ -= q.count(); value_ -= q.count();
return *this; return *this;
} }
template<typename Rep2> constexpr quantity& operator*=(const rep& rhs)
requires detail::safe_convertible<Rep2, rep> requires requires(rep a, rep b) { { a *= b } -> std::same_as<rep&>; }
constexpr quantity& operator*=(const Rep2& rhs)
{ {
value_ *= rhs; value_ *= rhs;
return *this; return *this;
} }
template<typename Rep2> constexpr quantity& operator/=(const rep& rhs)
requires detail::safe_convertible<Rep2, rep> requires requires(rep a, rep b) { { a /= b } -> std::same_as<rep&>; }
constexpr quantity& operator/=(const Rep2& rhs)
{ {
value_ /= rhs; value_ /= rhs;
return *this; return *this;
} }
template<ScalableNumber Value> constexpr quantity& operator%=(const rep& rhs)
requires (!treat_as_floating_point<rep>) && requires (!floating_point_<rep>) &&
(!treat_as_floating_point<Value>) requires(rep a, rep b) { { a %= b } -> std::same_as<rep&>; }
constexpr quantity& operator%=(const Value& rhs)
requires requires(rep v1, Value v2) { { v1 %= v2 } -> std::same_as<rep&>; }
{ {
value_ %= rhs; value_ %= rhs;
return *this; return *this;
} }
constexpr quantity& operator%=(const quantity& q) constexpr quantity& operator%=(const quantity& q)
requires (!treat_as_floating_point<rep>) && requires (!floating_point_<rep>) &&
requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as<rep&>; } requires(rep a, rep b) { { a %= b } -> std::same_as<rep&>; }
{ {
value_ %= q.count(); value_ %= q.count();
return *this; return *this;
@ -195,143 +228,88 @@ public:
// Hidden Friends // Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only // Below friend functions are to be found via argument-dependent lookup only
[[nodiscard]] friend constexpr quantity operator+(const quantity& lhs, const quantity& rhs) [[nodiscard]] friend constexpr quantity operator+(const quantity& lhs, const quantity& rhs)
requires std::regular_invocable<std::plus<>, Rep, Rep> requires invoke_result_convertible_to_<rep, std::plus<>, rep, rep>
{ {
return quantity(lhs.count() + rhs.count()); return quantity(lhs.count() + rhs.count());
} }
template<typename D2, typename U2, typename Rep2>
requires std::regular_invocable<std::plus<>, Rep, Rep2> && equivalent<D, D2>
[[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using common_rep = decltype(lhs.count() + rhs.count());
using ret = common_quantity<quantity, quantity<D2, U2, Rep2>, common_rep>;
return ret(ret(lhs).count() + ret(rhs).count());
}
[[nodiscard]] friend constexpr quantity operator-(const quantity& lhs, const quantity& rhs) [[nodiscard]] friend constexpr quantity operator-(const quantity& lhs, const quantity& rhs)
requires std::regular_invocable<std::minus<>, Rep, Rep> requires invoke_result_convertible_to_<rep, std::minus<>, rep, rep>
{ {
return quantity(lhs.count() - rhs.count()); return quantity(lhs.count() - rhs.count());
} }
template<typename D2, typename U2, typename Rep2> template<typename Value>
requires std::regular_invocable<std::minus<>, Rep, Rep2> && equivalent<D, D2> requires (!Quantity<Value>) &&
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) invoke_result_convertible_to_<rep, std::multiplies<>, rep, Value>
{
using common_rep = decltype(lhs.count() - rhs.count());
using ret = common_quantity<quantity, quantity<D2, U2, Rep2>, common_rep>;
return ret(ret(lhs).count() - ret(rhs).count());
}
template<ScalableNumber Value>
requires std::regular_invocable<std::multiplies<>, Rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v) [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v)
{ {
using common_rep = decltype(q.count() * v); using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, rep, Value>>;
using ret = quantity<D, U, common_rep>;
return ret(q.count() * v); return ret(q.count() * v);
} }
template<ScalableNumber Value> template<typename Value>
requires std::regular_invocable<std::multiplies<>, Value, Rep> requires (!Quantity<Value>) &&
invoke_result_convertible_to_<rep, std::multiplies<>, rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q) [[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q)
{ {
return q * v; return q * v;
} }
template<typename D2, typename U2, typename Rep2> template<typename Value>
requires std::regular_invocable<std::multiplies<>, Rep, Rep2> requires (!Quantity<Value>) &&
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) invoke_result_convertible_to_<rep, std::divides<>, rep, Value>
{
using dim = dimension_multiply<D, D2>;
using ret_unit = downcast_unit<dim, (U::ratio / dimension_unit<D>::ratio) * (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using common_rep = decltype(lhs.count() * rhs.count());
using ret = quantity<dim, ret_unit, common_rep>;
return ret(lhs.count() * rhs.count());
}
template<ScalableNumber Value>
requires std::regular_invocable<std::divides<>, Value, Rep>
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
// Expects(q.count() != zero().count());
using dim = dim_invert<D>;
using ret_unit = downcast_unit<dim, ratio(U::ratio.den, U::ratio.num, -U::ratio.exp)>;
using common_rep = decltype(v / q.count());
using ret = quantity<dim, ret_unit, common_rep>;
return ret(v / q.count());
}
template<ScalableNumber Value>
requires std::regular_invocable<std::divides<>, Rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v) [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v)
{ {
// Expects(v != zero().count()); // Expects(v != zero().count());
using ret = quantity<D, U, std::invoke_result_t<std::divides<>, rep, Value>>;
using common_rep = decltype(q.count() / v);
using ret = quantity<D, U, common_rep>;
return ret(q.count() / v); return ret(q.count() / v);
} }
template<typename D2, typename U2, typename Rep2> template<typename Value>
requires std::regular_invocable<std::divides<>, Rep, Rep2> requires (!Quantity<Value>) &&
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) invoke_result_convertible_to_<rep, std::divides<>, Value, rep>
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{ {
// Expects(rhs.count() != zero().count()); // Expects(q.count() != zero().count());
using dim = dim_invert<D>;
using common_rep = decltype(lhs.count() / rhs.count()); using ret_unit = downcast_unit<dim, inverse(U::ratio)>;
using dim = dimension_divide<D, D2>; using ret = quantity<dim, ret_unit, std::invoke_result_t<std::divides<>, Value, rep>>;
using ret_unit = downcast_unit<dim, (U::ratio / dimension_unit<D>::ratio) / (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>; return ret(v / q.count());
using ret = quantity<dim, ret_unit, common_rep>;
return ret(lhs.count() / rhs.count());
} }
template<ScalableNumber Value> template<typename Value>
requires (!treat_as_floating_point<Rep>) && requires (!Quantity<Value>) && (!floating_point_<rep>) && (!floating_point_<Value>) &&
(!treat_as_floating_point<Value>) && invoke_result_convertible_to_<rep, std::modulus<>, rep, Value>
std::regular_invocable<std::modulus<>, Rep, Value>
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v) [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v)
{ {
using common_rep = decltype(q.count() % v); using ret = quantity<D, U, std::invoke_result_t<std::modulus<>, rep, Value>>;
using ret = quantity<D, U, common_rep>;
return ret(q.count() % v); return ret(q.count() % v);
} }
template<typename U2, typename Rep2> [[nodiscard]] friend constexpr quantity operator%(const quantity& lhs, const quantity& rhs)
requires (!treat_as_floating_point<Rep>) && requires (!floating_point_<rep>) &&
(!treat_as_floating_point<Rep2>) && invoke_result_convertible_to_<rep, std::modulus<>, rep, rep>
std::regular_invocable<std::modulus<>, Rep, Rep2>
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity<D, U2, Rep2>& rhs)
{ {
using common_rep = decltype(lhs.count() % rhs.count()); return quantity(lhs.count() % rhs.count());
using ret = common_quantity<quantity, quantity<D, U2, Rep2>, common_rep>;
return ret(ret(lhs).count() % ret(rhs).count());
} }
template<typename D2, typename U2, typename Rep2> [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs)
requires equivalent<D, D2> && requires std::three_way_comparable<rep>
std::three_way_comparable_with<Rep, Rep2> #if COMP_GCC == 10 && COMP_GCC_MINOR >= 2
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) = default;
#else
{ {
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>; return lhs.count() <=> rhs.count();
return cq(lhs).count() <=> cq(rhs).count();
} }
#endif
template<typename D2, typename U2, typename Rep2> [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) = default;
requires equivalent<D, D2> &&
std::equality_comparable_with<Rep, Rep2>
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
return cq(lhs).count() == cq(rhs).count();
}
template<class CharT, class Traits> template<class CharT, class Traits>
friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity& q) friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const quantity& q)
requires requires { os << q.count(); }
{ {
if(os.width()) { if(os.width()) {
// std::setw() applies to the whole quantity output so it has to be first put into std::string // std::setw() applies to the whole quantity output so it has to be first put into std::string
@ -345,9 +323,82 @@ public:
} }
}; };
template<ScalableNumber V> // CTAD
template<QuantityValue V>
/* implicit */ quantity(V) -> quantity<dim_one, one, V>; /* implicit */ quantity(V) -> quantity<dim_one, one, V>;
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::plus<>, Q1, Q2>;
return ret(ret(lhs).count() + ret(rhs).count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::minus<>, Q1, Q2>;
return ret(ret(lhs).count() - ret(rhs).count());
}
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires quantity_value_for_<std::multiplies<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
{
using dim = dimension_multiply<D1, D2>;
using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) * (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ret = quantity<dim, unit, std::invoke_result_t<std::multiplies<>, Rep1, Rep2>>;
return ret(lhs.count() * rhs.count());
}
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires quantity_value_for_<std::divides<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
{
// Expects(rhs.count() != zero().count());
using dim = dimension_divide<D1, D2>;
using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) / (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ret = quantity<dim, unit, std::invoke_result_t<std::divides<>, Rep1, Rep2>>;
return ret(lhs.count() / rhs.count());
}
template<typename D1, typename U1, typename Rep1, typename U2, typename Rep2>
requires (!floating_point_<Rep1>) && (!floating_point_<Rep2>) &&
quantity_value_for_<std::modulus<>, Rep1, Rep2>
[[nodiscard]] constexpr Quantity auto operator%(const quantity<D1, U1, Rep1>& lhs, const quantity<dim_one, U2, Rep2>& rhs)
{
using unit = downcast_unit<D1, U1::ratio * U2::ratio>;
using ret = quantity<D1, unit, std::invoke_result_t<std::modulus<>, Rep1, Rep2>>;
return ret(lhs.count() % rhs.count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires (!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
{
using ret = common_quantity_for<std::modulus<>, Q1, Q2>;
return ret(ret(lhs).count() % ret(rhs).count());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
{
using cq = common_quantity<Q1, Q2>;
return cq(lhs).count() <=> cq(rhs).count();
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
{
using cq = common_quantity<Q1, Q2>;
return cq(lhs).count() == cq(rhs).count();
}
namespace detail { namespace detail {
template<typename D, typename U, typename Rep> template<typename D, typename U, typename Rep>

View File

@ -36,10 +36,10 @@
namespace units { namespace units {
template<Dimension D, UnitOf<D> U, ScalableNumber Rep> template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity; class quantity;
template<Dimension D, UnitOf<D> U, ScalableNumber Rep> template<Dimension D, UnitOf<D> U, QuantityValue Rep>
class quantity_point; class quantity_point;
namespace detail { namespace detail {
@ -55,232 +55,6 @@ constexpr auto quantity_ratio(const quantity<D, U, Rep>&)
} }
} }
} // namespace detail
// quantity_cast
namespace detail {
template<typename To, ratio CRatio, typename CRep, bool NumIsOne, bool DenIsOne, bool ExpIsZero>
struct quantity_cast_impl;
template<typename To, ratio CRatio, typename CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count()));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::ipow10(CRatio.exp))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(detail::ipow10(-CRatio.exp))));
}
}
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, true> {
template<typename Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(CRatio.num) /
static_cast<CRep>(CRatio.den))));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
template<typename Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)) *
(static_cast<CRep>(CRatio.num) /
static_cast<CRep>(CRatio.den)))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(CRatio.num) *
static_cast<CRep>(detail::ipow10(CRatio.exp)) /
static_cast<CRep>(CRatio.den))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(CRatio.num) /
(static_cast<CRep>(CRatio.den) *
static_cast<CRep>(detail::ipow10(-CRatio.exp))))));
}
}
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(CRatio.den)));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)) * (CRep{1} / static_cast<CRep>(CRatio.den)))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(detail::ipow10(CRatio.exp)) / static_cast<CRep>(CRatio.den))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / (static_cast<CRep>(detail::ipow10(-CRatio.exp)) * static_cast<CRep>(CRatio.den))));
}
}
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num)));
}
};
template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(CRatio.num) * static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(CRatio.num) * static_cast<CRep>(detail::ipow10(CRatio.exp)))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * (static_cast<CRep>(CRatio.num) / static_cast<CRep>(detail::ipow10(-CRatio.exp)))));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * detail::fpow10<CRep>(CRatio.exp)));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * detail::ipow10(CRatio.exp)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() / detail::ipow10(-CRatio.exp)));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, true> {
template<typename Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num / CRatio.den)));
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
template<typename Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * (detail::fpow10<CRep>(CRatio.exp) * (CRatio.num / CRatio.den))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num * detail::ipow10(CRatio.exp) / CRatio.den)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count()) * (CRatio.num / (CRatio.den * detail::ipow10(-CRatio.exp))));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count() / CRatio.den));
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * (detail::fpow10<CRep>(CRatio.exp) / CRatio.den)));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * (detail::ipow10(CRatio.exp) / CRatio.den)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() / (detail::ipow10(-CRatio.exp) * CRatio.den)));
}
}
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, true> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num));
}
};
template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
template<Quantity Q>
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num * detail::fpow10<CRep>(CRatio.exp))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num * detail::ipow10(CRatio.exp))));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() * (CRatio.num / detail::ipow10(-CRatio.exp))));
}
}
}
};
template<typename Q1, typename Q2> template<typename Q1, typename Q2>
constexpr ratio cast_ratio(const Q1& from, const Q2& to) constexpr ratio cast_ratio(const Q1& from, const Q2& to)
{ {
@ -294,6 +68,26 @@ constexpr ratio cast_ratio(const Q1& from, const Q2& to)
} }
} }
template<typename From, typename To>
struct cast_traits;
template<typename From, typename To>
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
using rep_type = ratio_type;
};
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> &&
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> {
using ratio_type = std::common_type_t<typename std::common_type_t<From, To>::value_type, std::intmax_t>;
using rep_type = std::common_type_t<From, To>;
};
} // namespace detail } // namespace detail
/** /**
@ -308,16 +102,33 @@ constexpr ratio cast_ratio(const Q1& from, const Q2& to)
* *
* @tparam To a target quantity type to cast to * @tparam To a target quantity type to cast to
*/ */
template<Quantity To, typename D, typename U, typename Rep> template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep>
requires QuantityOf<To, D> requires QuantityOf<To, D>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{ {
using c_ratio = std::integral_constant<ratio, detail::cast_ratio(quantity<D, U, Rep>(), To())>;
using c_rep = std::common_type_t<typename To::rep, Rep>;
using ret_unit = downcast_unit<typename To::dimension, To::unit::ratio>; using ret_unit = downcast_unit<typename To::dimension, To::unit::ratio>;
using ret = quantity<typename To::dimension, ret_unit, typename To::rep>; using ret = quantity<typename To::dimension, ret_unit, typename To::rep>;
using cast = detail::quantity_cast_impl<ret, c_ratio::value, c_rep, c_ratio::value.num == 1, c_ratio::value.den == 1, c_ratio::value.exp == 0>; using traits = detail::cast_traits<Rep, typename To::rep>;
return cast::cast(q); using ratio_type = TYPENAME traits::ratio_type;
using rep_type = TYPENAME traits::rep_type;
constexpr auto c_ratio = detail::cast_ratio(quantity<D, U, Rep>(), To());
if constexpr (treat_as_floating_point<rep_type>) {
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
(static_cast<ratio_type>(c_ratio.num) * detail::fpow10<ratio_type>(c_ratio.exp) / static_cast<ratio_type>(c_ratio.den))));
}
else {
if constexpr (c_ratio.exp > 0) {
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
(static_cast<ratio_type>(c_ratio.num) * static_cast<ratio_type>(detail::ipow10(c_ratio.exp))) /
static_cast<ratio_type>(c_ratio.den)));
}
else {
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
static_cast<ratio_type>(c_ratio.num) /
(static_cast<ratio_type>(c_ratio.den) * static_cast<ratio_type>(detail::ipow10(-c_ratio.exp)))));
}
}
} }
/** /**
@ -393,7 +204,7 @@ template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
* *
* @tparam ToRep a representation type to use for a target quantity * @tparam ToRep a representation type to use for a target quantity
*/ */
template<ScalableNumber ToRep, typename D, typename U, typename Rep> template<QuantityValue ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
{ {
return quantity_cast<quantity<D, U, ToRep>>(q); return quantity_cast<quantity<D, U, ToRep>>(q);

View File

@ -37,7 +37,7 @@ namespace units {
* @tparam U a measurement unit of the quantity point * @tparam U a measurement unit of the quantity point
* @tparam Rep a type to be used to represent values of a quantity point * @tparam Rep a type to be used to represent values of a quantity point
*/ */
template<Dimension D, UnitOf<D> U, ScalableNumber Rep = double> template<Dimension D, UnitOf<D> U, QuantityValue Rep = double>
class quantity_point { class quantity_point {
public: public:
using quantity_type = quantity<D, U, Rep>; using quantity_type = quantity<D, U, Rep>;

View File

@ -24,7 +24,9 @@ cmake_minimum_required(VERSION 3.12)
add_library(unit_tests_static add_library(unit_tests_static
cgs_test.cpp cgs_test.cpp
custom_rep_min_req_test.cpp concepts_test.cpp
custom_rep_test_min_expl.cpp
custom_rep_test_min_impl.cpp
custom_unit_test.cpp custom_unit_test.cpp
data_test.cpp data_test.cpp
dimension_op_test.cpp dimension_op_test.cpp

View File

@ -0,0 +1,130 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "units/physical/si/cgs/derived/speed.h"
#include "units/physical/si/derived/speed.h"
#include "units/physical/si/fps/derived/speed.h"
#include "units/quantity_point.h"
#include <chrono>
#include <complex>
#include <mutex>
#include <optional>
#include <string>
namespace {
using namespace units;
using namespace units::physical;
// Prefix family
static_assert(PrefixFamily<si::prefix>);
static_assert(!PrefixFamily<si::kilo>);
// Prefix
static_assert(Prefix<si::kilo>);
static_assert(!Prefix<si::prefix>);
static_assert(!Prefix<std::kilo>);
// UnitRatio
static_assert(UnitRatio<ratio(1000)>);
static_assert(!UnitRatio<ratio(0)>);
// static_assert(UnitRatio<ratio(1000, 0)>); // static_assert in ratio
static_assert(UnitRatio<ratio(-1000, -1)>);
static_assert(!UnitRatio<ratio(-1000, 1)>);
static_assert(!UnitRatio<ratio(1, -1000)>);
// BaseDimension
static_assert(BaseDimension<si::dim_length>);
static_assert(!BaseDimension<si::dim_speed>);
static_assert(!BaseDimension<int>);
// DerivedDimension
static_assert(DerivedDimension<si::dim_speed>);
static_assert(!DerivedDimension<si::dim_length>);
static_assert(!DerivedDimension<int>);
// Dimension
static_assert(Dimension<si::dim_length>);
static_assert(Dimension<si::dim_speed>);
static_assert(!Dimension<si::metre>);
static_assert(!Dimension<int>);
static_assert(!Dimension<std::chrono::seconds>);
// Unit
static_assert(Unit<si::metre>);
static_assert(Unit<si::kilometre>);
static_assert(Unit<si::fps::mile>);
static_assert(Unit<si::metre_per_second>);
static_assert(!Unit<si::dim_length>);
static_assert(!Unit<int>);
static_assert(!Unit<std::chrono::seconds>);
// UnitOf
static_assert(UnitOf<si::metre, si::dim_length>);
static_assert(UnitOf<si::kilometre, si::dim_length>);
static_assert(UnitOf<si::fps::mile, si::dim_length>);
static_assert(!UnitOf<si::second, si::dim_length>);
// QuantityValue
static_assert(QuantityValue<int>);
static_assert(QuantityValue<std::complex<double>>);
static_assert(!QuantityValue<si::length<si::metre>>);
static_assert(!QuantityValue<std::optional<si::length<si::metre>>>);
static_assert(!QuantityValue<std::mutex>);
static_assert(!QuantityValue<std::string>);
// Quantity
static_assert(Quantity<si::length<si::metre>>);
static_assert(!Quantity<std::chrono::seconds>);
static_assert(!Quantity<quantity_point<si::dim_length, si::metre>>);
// QuantityPoint
static_assert(QuantityPoint<quantity_point<si::dim_length, si::metre>>);
static_assert(!QuantityPoint<si::length<si::metre>>);
static_assert(!QuantityPoint<std::chrono::seconds>);
// WrappedQuantity
static_assert(wrapped_quantity_<std::optional<si::length<si::metre>>>);
static_assert(!wrapped_quantity_<std::pair<si::length<si::metre>, si::length<si::metre>>>);
// QuantityOf
static_assert(QuantityOf<si::length<si::metre>, si::dim_length>);
// TODO it seems `QuantityOf` is a bad name if `si::cgs::length<si::cgs::centimetre>` matches `si::fps::dim_length`
static_assert(QuantityOf<si::cgs::length<si::cgs::centimetre>, si::dim_length>);
static_assert(QuantityOf<si::cgs::length<si::metre>, si::dim_length>);
static_assert(QuantityOf<si::cgs::length<si::cgs::centimetre>, si::fps::dim_length>);
static_assert(!QuantityOf<si::cgs::length<si::cgs::centimetre>, si::dim_time>);
} // namespace

View File

@ -1,251 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "units/math.h"
#include "units/physical/si/si.h"
#include <chrono>
#include <type_traits>
#include <utility>
using namespace units;
namespace {
template<typename T>
struct equality_ops {
[[nodiscard]] friend constexpr bool operator==(T lhs, T rhs) { return lhs.value_ == rhs.value_; }
[[nodiscard]] friend constexpr bool operator!=(T lhs, T rhs) { return !(lhs == rhs); }
};
template<typename T>
struct scaling_ops {
[[nodiscard]] friend constexpr T operator*(T lhs, T rhs) {
return T(lhs.value_ * rhs.value_);
}
[[nodiscard]] friend constexpr T operator/(T lhs, T rhs) {
return T(lhs.value_ / rhs.value_);
}
};
template<typename T>
struct scalar_ops : equality_ops<T>, scaling_ops<T> {};
template<typename T>
struct impl_constructible : scalar_ops<impl_constructible<T>> {
T value_{};
impl_constructible() = default;
constexpr impl_constructible(T v) : value_(std::move(v)) {}
// no conversion to fundamental arithmetic types
};
template<typename T>
using impl = impl_constructible<T>;
template<typename T>
struct expl_constructible : scalar_ops<expl_constructible<T>> {
T value_{};
expl_constructible() = default;
constexpr explicit expl_constructible(T v) : value_(std::move(v)) {}
// no conversion to fundamental arithmetic types
};
template<typename T>
using expl = expl_constructible<T>;
template<typename T>
struct impl_constructible_impl_convertible : scalar_ops<impl_constructible_impl_convertible<T>> /*, int_scaling_ops<impl_constructible_impl_convertible<T>> */ {
T value_{};
impl_constructible_impl_convertible() = default;
constexpr impl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
constexpr operator const T&() const& { return value_; }
};
template<typename T>
using impl_impl = impl_constructible_impl_convertible<T>;
static_assert(std::convertible_to<float, impl_impl<float>>);
static_assert(std::convertible_to<impl_impl<float>, float>);
static_assert(units::ScalableNumber<impl_impl<float>>);
template<typename T>
struct expl_constructible_impl_convertible : scalar_ops<expl_constructible_impl_convertible<T>> {
T value_{};
expl_constructible_impl_convertible() = default;
constexpr explicit expl_constructible_impl_convertible(T v) : value_(std::move(v)) {}
constexpr operator const T&() const& { return value_; }
};
template<typename T>
using expl_impl = expl_constructible_impl_convertible<T>;
static_assert(!std::convertible_to<float, expl_impl<float>>);
static_assert(std::convertible_to<expl_impl<float>, float>);
static_assert(units::ScalableNumber<expl_impl<float>>);
template<typename T>
struct impl_constructible_expl_convertible : scalar_ops<impl_constructible_expl_convertible<T>> {
T value_{};
impl_constructible_expl_convertible() = default;
constexpr impl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
constexpr explicit operator const T&() const& { return value_; }
};
template<typename T>
using impl_expl = impl_constructible_expl_convertible<T>;
static_assert(std::convertible_to<float, impl_expl<float>>);
static_assert(!std::convertible_to<impl_expl<float>, float>);
static_assert(units::ScalableNumber<impl_expl<float>>);
template<typename T>
struct expl_constructible_expl_convertible : scalar_ops<expl_constructible_expl_convertible<T>> {
T value_{};
expl_constructible_expl_convertible() = default;
constexpr explicit expl_constructible_expl_convertible(T v) : value_(std::move(v)) {}
constexpr explicit operator const T&() const& { return value_; }
};
template<typename T>
using expl_expl = expl_constructible_expl_convertible<T>;
static_assert(!std::convertible_to<float, expl_expl<float>>);
static_assert(!std::convertible_to<expl_expl<float>, float>);
static_assert(units::ScalableNumber<expl_expl<float>>);
} // namespace
namespace units {
template<typename T>
inline constexpr bool treat_as_floating_point<impl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<expl_constructible<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<impl_impl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<expl_impl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<impl_expl<T>> = std::is_floating_point_v<T>;
template<typename T>
inline constexpr bool treat_as_floating_point<expl_expl<T>> = std::is_floating_point_v<T>;
template<typename T>
struct quantity_values<impl<T>> {
static constexpr impl<T> zero() { return 0; }
static constexpr impl<T> max() { return std::numeric_limits<T>::max(); }
static constexpr impl<T> min() { return std::numeric_limits<T>::lowest(); }
};
} // namespace units
namespace {
using namespace units::physical::si;
// constructors
// Quantity from ScalableNumber
// int <- int
static_assert(length<metre, int>(expl_impl<int>(1)).count() == 1);
static_assert(!std::is_constructible_v<length<metre, int>, impl_expl<int>>);
static_assert(length<metre, int>(int(impl_expl<int>(1))).count() == 1);
static_assert(!std::is_constructible_v<length<metre, expl_impl<int>>, int>);
static_assert(length<metre, expl_impl<int>>(expl_impl<int>(1)).count() == expl_impl<int>{1});
static_assert(length<metre, impl_expl<int>>(1).count() == impl_expl<int>{1});
// double <- double
static_assert(length<metre, double>(expl_impl<double>(1.0)).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, impl_expl<double>>);
static_assert(length<metre, double>(double(impl_expl<double>(1.0))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, double>);
static_assert(length<metre, expl_impl<double>>(expl_impl<double>(1.0)).count() == expl_impl<double>{1.0});
static_assert(length<metre, impl_expl<double>>(1.0).count() == impl_expl<double>{1.0});
// double <- int
static_assert(length<metre, double>(expl_impl<int>(1)).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, impl_expl<int>>);
static_assert(length<metre, double>(int(impl_expl<int>(1))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, int>);
static_assert(length<metre, expl_impl<double>>(expl_impl<double>(1)).count() == expl_impl<double>{1});
static_assert(length<metre, impl_expl<double>>(1).count() == impl_expl<double>{1.0});
// int <- double
static_assert(!std::is_constructible_v<length<metre, int>, expl_impl<double>>);
static_assert(!std::is_constructible_v<length<metre, impl_expl<int>>, double>);
// Quantity from other Quantity with different Rep
// int <- int
static_assert(length<metre, int>(length<metre, expl_impl<int>>(expl_impl<int>(1))).count() == 1);
static_assert(!std::is_constructible_v<length<metre, int>, length<metre, impl_expl<int>>>);
static_assert(length<metre, int>(quantity_cast<int>(length<metre, impl_expl<int>>(1))).count() == 1);
static_assert(!std::is_constructible_v<length<metre, expl_impl<int>>, length<metre, int>>);
static_assert(length<metre, expl_impl<int>>(quantity_cast<expl_impl<int>>(length<metre, int>(1))).count() == expl_impl<int>{1});
static_assert(length<metre, impl_expl<int>>(length<metre, int>(1)).count() == impl_expl<int>{1});
// double <- double
static_assert(length<metre, double>(length<metre, expl_impl<double>>(expl_impl<double>(1.0))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, length<metre, impl_expl<double>>>);
static_assert(length<metre, double>(quantity_cast<double>(length<metre, impl_expl<double>>(1.0))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, length<metre, double>>);
static_assert(length<metre, expl_impl<double>>(quantity_cast<expl_impl<double>>(length<metre>(1.0))).count() == expl_impl<double>{1.0});
static_assert(length<metre, impl_expl<double>>(length<metre>(1.0)).count() == impl_expl<double>{1.0});
// double <- int
static_assert(length<metre, double>(length<metre, expl_impl<int>>(expl_impl<int>(1))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, double>, length<metre, impl_expl<int>>>);
static_assert(length<metre, double>(quantity_cast<int>(length<metre, impl_expl<int>>(1))).count() == 1.0);
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, length<metre, int>>);
static_assert(length<metre, expl_impl<double>>(quantity_cast<expl_impl<double>>(length<metre, int>(1))).count() == expl_impl<double>{1});
static_assert(length<metre, impl_expl<double>>(length<metre, int>(1)).count() == impl_expl<double>{1.0});
// int <- double
static_assert(!std::is_constructible_v<length<metre, int>, length<metre, expl_impl<double>>>);
static_assert(!std::is_constructible_v<length<metre, impl_expl<int>>, length<metre, double>>);
// unit conversions
static_assert(length<metre, impl<int>>(length<kilometre, impl<int>>(1)).count() == impl<int>(1000));
static_assert(length<metre, expl<int>>(length<kilometre, expl<int>>(expl<int>(1))).count() == expl<int>(1000));
static_assert(length<metre, impl_impl<int>>(length<kilometre, impl_impl<int>>(1)).count() == impl_impl<int>(1000));
static_assert(length<metre, impl_expl<int>>(length<kilometre, impl_expl<int>>(1)).count() == impl_expl<int>(1000));
static_assert(length<metre, expl_impl<int>>(length<kilometre, expl_impl<int>>(expl_impl<int>(1))).count() == expl_impl<int>(1000));
static_assert(length<metre, expl_expl<int>>(length<kilometre, expl_expl<int>>(expl_expl<int>(1))).count() == expl_expl<int>(1000));
static_assert(!std::is_constructible_v<length<kilometre, impl<int>>, length<metre, impl<int>>>);
static_assert(length<kilometre, impl<int>>(quantity_cast<kilometre>(length<metre, impl<int>>(2000))).count() == impl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, expl<int>>, length<metre, expl<int>>>);
static_assert(length<kilometre, expl<int>>(quantity_cast<kilometre>(length<metre, expl<int>>(expl<int>(2000)))).count() == expl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, impl_impl<int>>, length<metre, impl_impl<int>>>);
static_assert(length<kilometre, impl_impl<int>>(quantity_cast<kilometre>(length<metre, impl_impl<int>>(2000))).count() == impl_impl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, impl_expl<int>>, length<metre, impl_expl<int>>>);
static_assert(length<kilometre, impl_expl<int>>(quantity_cast<kilometre>(length<metre, impl_expl<int>>(2000))).count() == impl_expl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, expl_impl<int>>, length<metre, expl_impl<int>>>);
static_assert(length<kilometre, expl_impl<int>>(quantity_cast<kilometre>(length<metre, expl_impl<int>>(expl_impl<int>(2000)))).count() == expl_impl<int>(2));
static_assert(!std::is_constructible_v<length<kilometre, expl_expl<int>>, length<metre, expl_expl<int>>>);
static_assert(length<kilometre, expl_expl<int>>(quantity_cast<kilometre>(length<metre, expl_expl<int>>(expl_expl<int>(2000)))).count() == expl_expl<int>(2));
} // namespace

View File

@ -0,0 +1,199 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "units/physical/si/base/length.h"
namespace {
/**
* @brief Representation type meeting minimum requirements
*
* This type with a default Mode = 0 provides the minimum set of requirements to
* satisfy @c QuantityValue concept which is used for quantity's representation type.
*
* In case of Mode != 0 only one of mandatory operation is removed which should
* result in @c QuantityValue concept not being satisfied.
*
* @tparam Mode a flag to disable specific type's operations
*/
template<int Mode = 0>
class min_expl {
std::intmax_t value_;
public:
// default construction
min_expl() requires (Mode != 1) = default;
// construction from std::int64_t
explicit constexpr min_expl(std::intmax_t v) noexcept requires (Mode != 2) : value_(v) {}
// copy construction
min_expl(const min_expl&) requires (Mode != 3) = default;
// move construction
min_expl(min_expl&&) requires (Mode != 4) = default;
min_expl(min_expl&&) requires (Mode == 4) = delete;
// copy assignment
min_expl& operator=(const min_expl&) requires (Mode != 5) = default;
// move assignment
min_expl& operator=(min_expl&&) requires (Mode != 6) = default;
min_expl& operator=(min_expl&&) requires (Mode == 6) = delete;
// equality
bool operator==(const min_expl&) const requires (Mode != 7) = default;
// scalability - multiplication
friend constexpr min_expl operator*(const min_expl& lhs, const min_expl& rhs) requires (Mode != 8)
{ return min_expl(lhs.value_ * rhs.value_); }
// scalability - division
friend constexpr min_expl operator/(const min_expl& lhs, const min_expl& rhs) requires (Mode != 9)
{ return min_expl(lhs.value_ / rhs.value_); }
};
}
template<int Mode>
struct std::common_type<std::intmax_t, min_expl<Mode>> : std::type_identity<min_expl<Mode>> {};
template<int Mode>
struct std::common_type<min_expl<Mode>, std::intmax_t> : std::type_identity<min_expl<Mode>> {};
namespace {
using namespace units;
using namespace units::physical::si;
// quantity explicitly constructible (not convertible) from the representation type
static_assert(std::constructible_from<length<metre, min_expl<>>, min_expl<>>);
static_assert(!std::convertible_to<min_expl<>, length<metre, min_expl<>>>);
// not constructible from an underlying type
static_assert(!std::constructible_from<length<metre, min_expl<>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_expl<>>>);
// dimensionless quantity implicitly convertible from the representation type
static_assert(std::constructible_from<dimensionless<one, min_expl<>>, min_expl<>>);
static_assert(std::convertible_to<min_expl<>, dimensionless<one, min_expl<>>>);
// but not from an underlying type
static_assert(!std::constructible_from<dimensionless<one, min_expl<>>, int>);
static_assert(!std::convertible_to<int, dimensionless<one, min_expl<>>>);
// or for ratio != 1
static_assert(std::constructible_from<dimensionless<percent, min_expl<>>, min_expl<>>);
static_assert(!std::convertible_to<min_expl<>, dimensionless<percent, min_expl<>>>);
// quantity convertible from itself
static_assert(std::constructible_from<length<metre, min_expl<>>, length<metre, min_expl<>>>);
static_assert(std::convertible_to<length<metre, min_expl<>>, length<metre, min_expl<>>>);
// not convertible from an underlying type
static_assert(!std::constructible_from<length<metre, min_expl<>>, length<metre, int>>);
static_assert(!std::convertible_to<length<metre, int>, length<metre, min_expl<>>>);
// quantity convertible from another non truncating unit
static_assert(std::constructible_from<length<metre, min_expl<>>, length<kilometre, min_expl<>>>);
static_assert(std::convertible_to<length<kilometre, min_expl<>>, length<metre, min_expl<>>>);
// quantity not convertible from another truncating unit
static_assert(!std::constructible_from<length<kilometre, min_expl<>>, length<metre, min_expl<>>>);
static_assert(!std::convertible_to<length<metre, min_expl<>>, length<kilometre, min_expl<>>>);
// rep type with explicit constructor - implicit construction of rep not allowed
static_assert(!std::constructible_from<length<metre, min_expl<>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_expl<>>>);
static_assert(!std::constructible_from<length<metre, min_expl<>>, length<metre, int>>);
static_assert(!std::convertible_to<length<metre, int>, length<metre, min_expl<>>>);
static_assert(!std::constructible_from<length<metre, int>, min_expl<>>);
static_assert(!std::convertible_to<min_expl<>, length<metre, int>>);
static_assert(!std::constructible_from<length<metre, int>, length<metre, min_expl<>>>);
static_assert(!std::convertible_to<length<metre, min_expl<>>, length<metre, int>>);
// all operations needed to satisfy concept
static_assert(QuantityValue<min_expl<>>);
static_assert(!QuantityValue<min_expl<1>>);
static_assert(!QuantityValue<min_expl<2>>);
static_assert(!QuantityValue<min_expl<3>>);
static_assert(!QuantityValue<min_expl<4>>);
static_assert(!QuantityValue<min_expl<5>>);
static_assert(!QuantityValue<min_expl<6>>);
static_assert(!QuantityValue<min_expl<7>>);
static_assert(!QuantityValue<min_expl<8>>);
static_assert(!QuantityValue<min_expl<9>>);
// quantity's operators should mirror the representation type capabilities
template<typename Rep>
concept invalid_member_operations = requires(length<metre, Rep> lhs) {
requires !requires { +lhs; };
requires !requires { -lhs; };
requires !requires { ++lhs; };
requires !requires { lhs++; };
requires !requires { --lhs; };
requires !requires { lhs--; };
requires !requires(length<metre, Rep> rhs) { lhs += rhs; };
requires !requires(length<metre, Rep> rhs) { lhs -= rhs; };
requires !requires(Rep rhs) { lhs *= rhs; };
requires !requires(Rep rhs) { lhs /= rhs; };
requires !requires(Rep rhs) { lhs %= rhs; };
requires !requires(length<metre, Rep> rhs) { lhs %= rhs; };
requires !requires(length<metre, Rep> rhs) { lhs + rhs; };
requires !requires(length<metre, Rep> rhs) { lhs - rhs; };
requires !requires(Rep rhs) { lhs % rhs; };
requires !requires(length<metre, Rep> rhs) { lhs % rhs; };
requires !requires(length<metre, Rep> rhs) { lhs < rhs; };
requires !requires(length<metre, Rep> rhs) { lhs > rhs; };
requires !requires(length<metre, Rep> rhs) { lhs <= rhs; };
requires !requires(length<metre, Rep> rhs) { lhs >= rhs; };
requires !requires(length<metre, int> rhs) { lhs + rhs; };
requires !requires(length<metre, int> rhs) { lhs - rhs; };
requires !requires(int rhs) { lhs % rhs; };
requires !requires(length<metre, int> rhs) { lhs % rhs; };
requires !requires(length<metre, int> rhs) { lhs == rhs; };
requires !requires(length<metre, int> rhs) { lhs != rhs; };
requires !requires(length<metre, int> rhs) { lhs < rhs; };
requires !requires(length<metre, int> rhs) { lhs > rhs; };
requires !requires(length<metre, int> rhs) { lhs <= rhs; };
requires !requires(length<metre, int> rhs) { lhs >= rhs; };
requires !requires(std::ostream os) { os << lhs; };
};
static_assert(invalid_member_operations<min_expl<>>);
// equality
static_assert(length<kilometre, min_expl<>>(min_expl<>(2)) == length<metre, min_expl<>>(min_expl<>(2000)));
static_assert(length<metre, min_expl<>>(min_expl<>(123)) * min_expl<>(2) == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(length<metre, min_expl<>>(min_expl<>(123)) * quantity{min_expl<>(2)} == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(min_expl<>(2) * length<metre, min_expl<>>(min_expl<>(123)) == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(quantity{min_expl<>(2)} * length<metre, min_expl<>>(min_expl<>(123)) == length<metre, min_expl<>>(min_expl<>(246)));
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / min_expl<>(2) == length<metre, min_expl<>>(min_expl<>(123)));
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / quantity{min_expl<>(2)} == length<metre, min_expl<>>(min_expl<>(123)));
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / length<metre, min_expl<>>(min_expl<>(2)) == quantity{min_expl<>(123)});
static_assert(length<metre, min_expl<>>(min_expl<>(246)) / length<metre, min_expl<>>(min_expl<>(2)) == min_expl<>(123));
} // namespace

View File

@ -0,0 +1,317 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "units/math.h"
#include "units/physical/si/base/length.h"
#include <chrono>
#include <type_traits>
#include <utility>
namespace {
/**
* @brief Implicitly constructible and convertible representation type
*
* A wrapper type that is implicitly convertible from and to the contained type.
*
* @tparam T element type
*/
template<typename T>
class min_impl {
T value_;
public:
using value_type = T;
min_impl() = default;
constexpr min_impl(T v) noexcept : value_(v) {}
template<typename U>
constexpr min_impl(min_impl<U> i) noexcept : value_(static_cast<T>(i.value_)) {}
constexpr operator T() const noexcept { return value_; }
};
}
template<typename T, typename U>
struct std::common_type<min_impl<T>, min_impl<U>> : std::common_type<T, U> {};
template<typename T, typename U>
struct std::common_type<min_impl<T>, U> : std::common_type<T, U> {};
template<typename U, typename T>
struct std::common_type<U, min_impl<T>> : std::common_type<T, U> {};
namespace {
using namespace units;
using namespace units::physical::si;
static_assert(QuantityValue<min_impl<int>>);
static_assert(QuantityValue<min_impl<double>>);
// construction from a value
static_assert(std::constructible_from<length<metre, min_impl<int>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, length<metre, min_impl<int>>>);
// construction from an underlying type
static_assert(std::constructible_from<length<metre, min_impl<int>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, double>);
static_assert(!std::convertible_to<double, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, int>);
static_assert(!std::convertible_to<int, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, double>); // narrowing conversion
static_assert(!std::convertible_to<double, length<metre, min_impl<int>>>);
// dimensionless quantity is convertible from a value
static_assert(std::constructible_from<dimensionless<one, min_impl<int>>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, min_impl<int>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, min_impl<double>>);
static_assert(std::convertible_to<min_impl<double>, dimensionless<one, min_impl<double>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, min_impl<double>>>);
static_assert(!std::constructible_from<dimensionless<one, min_impl<int>>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<one, min_impl<int>>>);
// and underlying type
static_assert(std::constructible_from<dimensionless<one, min_impl<int>>, int>);
static_assert(std::convertible_to<int, dimensionless<one, min_impl<int>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, double>);
static_assert(std::convertible_to<double, dimensionless<one, min_impl<double>>>);
static_assert(std::constructible_from<dimensionless<one, min_impl<double>>, int>);
static_assert(std::convertible_to<int, dimensionless<one, min_impl<double>>>);
static_assert(!std::constructible_from<dimensionless<one, min_impl<int>>, double>); // narrowing conversion
static_assert(!std::convertible_to<double, dimensionless<one, min_impl<int>>>);
// but only for ratio(1), otherwise not convertible
static_assert(std::constructible_from<dimensionless<percent, min_impl<int>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, min_impl<int>>>);
static_assert(std::constructible_from<dimensionless<percent, min_impl<double>>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, min_impl<double>>>);
static_assert(std::constructible_from<dimensionless<percent, min_impl<double>>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, min_impl<double>>>);
static_assert(!std::constructible_from<dimensionless<percent, min_impl<int>>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, min_impl<int>>>);
// implicit conversion tests
static_assert(std::constructible_from<length<metre, int>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, int>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, length<metre, double>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, double>>);
static_assert(!std::constructible_from<length<metre, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, length<metre, int>>);
// construction from an underlying type
static_assert(std::constructible_from<length<metre, int>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, int>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, length<metre, double>>);
static_assert(std::constructible_from<length<metre, double>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, length<metre, double>>);
static_assert(!std::constructible_from<length<metre, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, length<metre, int>>);
// dimensionless quantity is convertible from a value
static_assert(std::constructible_from<dimensionless<one, int>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, int>>);
static_assert(std::constructible_from<dimensionless<one, double>, min_impl<double>>);
static_assert(std::convertible_to<min_impl<double>, dimensionless<one, double>>);
static_assert(std::constructible_from<dimensionless<one, double>, min_impl<int>>);
static_assert(std::convertible_to<min_impl<int>, dimensionless<one, double>>);
static_assert(!std::constructible_from<dimensionless<one, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<one, int>>);
// but only for ratio(1), otherwise not convertible
static_assert(std::constructible_from<dimensionless<percent, int>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, int>>);
static_assert(std::constructible_from<dimensionless<percent, double>, min_impl<double>>);
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, double>>);
static_assert(std::constructible_from<dimensionless<percent, double>, min_impl<int>>);
static_assert(!std::convertible_to<min_impl<int>, dimensionless<percent, double>>);
static_assert(!std::constructible_from<dimensionless<percent, int>, min_impl<double>>); // narrowing conversion
static_assert(!std::convertible_to<min_impl<double>, dimensionless<percent, int>>);
// construction from a quantity
// min_impl<T> -> min_impl<T>
static_assert(std::constructible_from<length<metre, min_impl<int>>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, min_impl<double>>>);
static_assert(std::convertible_to<length<metre, min_impl<double>>, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, length<metre, min_impl<double>>>); // narrowing conversion
static_assert(!std::convertible_to<length<metre, min_impl<double>>, length<metre, min_impl<int>>>);
// T -> min_impl<T>
static_assert(std::constructible_from<length<metre, min_impl<int>>, length<metre, int>>);
static_assert(std::convertible_to<length<metre, int>, length<metre, min_impl<int>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, double>>);
static_assert(std::convertible_to<length<metre, double>, length<metre, min_impl<double>>>);
static_assert(std::constructible_from<length<metre, min_impl<double>>, length<metre, int>>);
static_assert(std::convertible_to<length<metre, int>, length<metre, min_impl<double>>>);
static_assert(!std::constructible_from<length<metre, min_impl<int>>, length<metre, double>>); // narrowing conversion
static_assert(!std::convertible_to<length<metre, double>, length<metre, min_impl<int>>>);
// min_impl<T> -> T
static_assert(std::constructible_from<length<metre, int>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, int>>);
static_assert(std::constructible_from<length<metre, double>, length<metre, min_impl<double>>>);
static_assert(std::convertible_to<length<metre, min_impl<double>>, length<metre, double>>);
static_assert(std::constructible_from<length<metre, double>, length<metre, min_impl<int>>>);
static_assert(std::convertible_to<length<metre, min_impl<int>>, length<metre, double>>);
static_assert(!std::constructible_from<length<metre, int>, length<metre, min_impl<double>>>); // narrowing conversion
static_assert(!std::convertible_to<length<metre, min_impl<double>>, length<metre, int>>);
// arithmetic operators
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(2));
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(2.5));
static_assert(length<metre, int>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(2));
static_assert(length<metre, int>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(2.5));
static_assert(length<metre, min_impl<int>>(1) + length<metre, int>(1) == length<metre, min_impl<int>>(2));
static_assert(length<metre, min_impl<int>>(1) + length<metre, double>(1.5) == length<metre, min_impl<double>>(2.5));
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, int>(2));
static_assert(length<metre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, double>(2.5));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1001.5));
static_assert(length<kilometre, int>(1) + length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<kilometre, int>(1) + length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1001.5));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, int>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, double>(1.5) == length<metre, min_impl<double>>(1001.5));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<int>>(1) == length<metre, int>(1001));
static_assert(length<kilometre, min_impl<int>>(1) + length<metre, min_impl<double>>(1.5) == length<metre, double>(1001.5));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1501));
static_assert(length<metre, int>(1) + length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<metre, int>(1) + length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1501));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, int>(1) == length<metre, min_impl<int>>(1001));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, double>(1.5) == length<metre, min_impl<double>>(1501));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<int>>(1) == length<metre, int>(1001));
static_assert(length<metre, min_impl<int>>(1) + length<kilometre, min_impl<double>>(1.5) == length<metre, double>(1501));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(0.5));
static_assert(length<metre, int>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1));
static_assert(length<metre, int>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(0.5));
static_assert(length<metre, min_impl<int>>(2) - length<metre, int>(1) == length<metre, min_impl<int>>(1));
static_assert(length<metre, min_impl<int>>(2) - length<metre, double>(1.5) == length<metre, min_impl<double>>(0.5));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, int>(1));
static_assert(length<metre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, double>(0.5));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1999));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1998.5));
static_assert(length<kilometre, int>(2) - length<metre, min_impl<int>>(1) == length<metre, min_impl<int>>(1999));
static_assert(length<kilometre, int>(2) - length<metre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(1998.5));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, int>(1) == length<metre, min_impl<int>>(1999));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, double>(1.5) == length<metre, min_impl<double>>(1998.5));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<int>>(1) == length<metre, int>(1999));
static_assert(length<kilometre, min_impl<int>>(2) - length<metre, min_impl<double>>(1.5) == length<metre, double>(1998.5));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1000));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(500));
static_assert(length<metre, int>(2000) - length<kilometre, min_impl<int>>(1) == length<metre, min_impl<int>>(1000));
static_assert(length<metre, int>(2000) - length<kilometre, min_impl<double>>(1.5) == length<metre, min_impl<double>>(500));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, int>(1) == length<metre, min_impl<int>>(1000));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, double>(1.5) == length<metre, min_impl<double>>(500));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<int>>(1) == length<metre, int>(1000));
static_assert(length<metre, min_impl<int>>(2000) - length<kilometre, min_impl<double>>(1.5) == length<metre, double>(500));
static_assert(length<metre, min_impl<int>>(123) * min_impl<double>(1.5) == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) * 1.5 == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, int>(123) * min_impl<double>(1.5) == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) * quantity{min_impl<double>(1.5)} == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) * quantity{1.5} == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, int>(123) * quantity{min_impl<double>(1.5)} == length<metre, min_impl<double>>(184.5));
static_assert(min_impl<double>(1.5) * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(min_impl<double>(1.5) * length<metre, int>(123) == length<metre, min_impl<double>>(184.5));
static_assert(1.5 * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(quantity{min_impl<double>(1.5)} * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(quantity{min_impl<double>(1.5)} * length<metre, int>(123) == length<metre, min_impl<double>>(184.5));
static_assert(quantity{1.5} * length<metre, min_impl<int>>(123) == length<metre, min_impl<double>>(184.5));
static_assert(length<metre, min_impl<int>>(123) / min_impl<double>(2.) == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / 2. == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, int>(123) / min_impl<double>(2.) == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / quantity{min_impl<double>(2.)} == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / quantity{2.} == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, int>(123) / quantity{min_impl<double>(2.)} == length<metre, min_impl<double>>(61.5));
static_assert(length<metre, min_impl<int>>(123) / length<metre, min_impl<double>>(2.) == 61.5);
static_assert(length<metre, min_impl<int>>(123) / length<metre, double>(2.) == 61.5);
static_assert(length<metre, int>(123) / length<metre, min_impl<double>>(2.) == 61.5);
static_assert(length<metre, min_impl<int>>(123) % min_impl<int>(100) == length<metre, int>(23));
static_assert(length<metre, min_impl<int>>(123) % 100 == length<metre, int>(23));
static_assert(length<metre, int>(123) % min_impl<int>(100) == length<metre, int>(23));
static_assert(length<metre, min_impl<int>>(123) % quantity{min_impl<int>(100)} == length<metre, int>(23));
static_assert(length<metre, min_impl<int>>(123) % quantity{100} == length<metre, int>(23));
static_assert(length<metre, int>(123) % quantity{min_impl<int>(100)} == length<metre, int>(23));
} // namespace

View File

@ -36,14 +36,14 @@ using namespace units::physical::si;
struct sq_volt_per_hertz : unit<sq_volt_per_hertz> {}; struct sq_volt_per_hertz : unit<sq_volt_per_hertz> {};
struct dim_power_spectral_density : derived_dimension<dim_power_spectral_density, sq_volt_per_hertz, units::exponent<dim_voltage, 2>, units::exponent<dim_frequency, -1>> {}; struct dim_power_spectral_density : derived_dimension<dim_power_spectral_density, sq_volt_per_hertz, units::exponent<dim_voltage, 2>, units::exponent<dim_frequency, -1>> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_power_spectral_density> U, QuantityValue Rep = double>
using power_spectral_density = quantity<dim_power_spectral_density, U, Rep>; using power_spectral_density = quantity<dim_power_spectral_density, U, Rep>;
// amplitude spectral density // amplitude spectral density
struct volt_per_sqrt_hertz : unit<volt_per_sqrt_hertz> {}; struct volt_per_sqrt_hertz : unit<volt_per_sqrt_hertz> {};
struct dim_amplitude_spectral_density : derived_dimension<dim_amplitude_spectral_density, volt_per_sqrt_hertz, units::exponent<dim_voltage, 1>, units::exponent<dim_frequency, -1, 2>> {}; struct dim_amplitude_spectral_density : derived_dimension<dim_amplitude_spectral_density, volt_per_sqrt_hertz, units::exponent<dim_voltage, 1>, units::exponent<dim_frequency, -1, 2>> {};
template<Unit U, ScalableNumber Rep = double> template<UnitOf<dim_amplitude_spectral_density> U, QuantityValue Rep = double>
using amplitude_spectral_density = quantity<dim_amplitude_spectral_density, U, Rep>; using amplitude_spectral_density = quantity<dim_amplitude_spectral_density, U, Rep>;
} }

View File

@ -22,9 +22,16 @@
#include "test_tools.h" #include "test_tools.h"
#include "units/math.h" #include "units/math.h"
#include "units/physical/si/si.h" #include "units/physical/si/cgs/derived/speed.h"
#include "units/physical/si/us/us.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/derived/volume.h"
#include "units/physical/si/fps/derived/speed.h"
#include <chrono> #include <chrono>
#include <complex>
#include <mutex>
#include <string>
#include <utility> #include <utility>
namespace { namespace {
@ -32,54 +39,67 @@ namespace {
using namespace units; using namespace units;
using namespace units::physical::si; using namespace units::physical::si;
// class invariants
template<typename DimLength> //////////////////////////////
concept invalid_types = requires // quantity class invariants
{ //////////////////////////////
requires !requires { typename quantity<DimLength, second, int>; }; // unit of a different dimension
requires !requires { typename quantity<DimLength, metre, quantity<DimLength, metre, int>>; }; // quantity used as Rep static_assert(sizeof(length<metre>) == sizeof(double));
requires !requires { typename quantity<metre, DimLength, double>; }; // reordered arguments static_assert(sizeof(length<metre, short>) == sizeof(short));
#if COMP_GCC != 10 || COMP_GCC_MINOR != 2 // TODO remove when gcc 10.3 releases
template<template<typename, typename, typename> typename Q>
concept invalid_types = requires {
requires !requires { typename Q<dim_length, second, int>; }; // unit of a different dimension
requires !requires { typename Q<dim_length, metre, length<metre>>; }; // quantity used as Rep
requires !requires { typename Q<metre, dim_length, double>; }; // reordered arguments
requires !requires { typename Q<metre, double, dim_length>; }; // reordered arguments
}; };
static_assert(invalid_types<quantity>);
#endif
static_assert(invalid_types<dim_length>); static_assert(std::is_trivially_default_constructible_v<length<metre>>);
static_assert(std::is_trivially_copy_constructible_v<length<metre>>);
static_assert(std::is_trivially_move_constructible_v<length<metre>>);
static_assert(std::is_trivially_copy_assignable_v<length<metre>>);
static_assert(std::is_trivially_move_assignable_v<length<metre>>);
static_assert(std::is_trivially_destructible_v<length<metre>>);
static_assert(std::is_nothrow_default_constructible_v<length<metre>>);
static_assert(std::is_nothrow_copy_constructible_v<length<metre>>);
static_assert(std::is_nothrow_move_constructible_v<length<metre>>);
static_assert(std::is_nothrow_copy_assignable_v<length<metre>>);
static_assert(std::is_nothrow_move_assignable_v<length<metre>>);
static_assert(std::is_nothrow_destructible_v<length<metre>>);
static_assert(std::is_trivially_copyable_v<length<metre>>);
static_assert(std::is_standard_layout_v<length<metre>>);
static_assert(std::default_initializable<length<metre>>);
static_assert(std::move_constructible<length<metre>>);
static_assert(std::copy_constructible<length<metre>>);
static_assert(std::equality_comparable<length<metre>>);
static_assert(std::totally_ordered<length<metre>>);
static_assert(std::regular<length<metre>>);
static_assert(std::three_way_comparable<length<metre>>);
//////////////////
// member types // member types
//////////////////
static_assert(is_same_v<length<metre, int>::rep, int>); static_assert(is_same_v<length<metre, int>::dimension, dim_length>);
static_assert(is_same_v<length<metre, double>::rep, double>); static_assert(is_same_v<fps::length<fps::mile>::dimension, fps::dim_length>);
static_assert(is_same_v<length<metre, int>::unit, metre>); static_assert(is_same_v<length<metre, int>::unit, metre>);
static_assert(is_same_v<length<kilometre, int>::unit, kilometre>); static_assert(is_same_v<fps::length<fps::mile>::unit, fps::mile>);
static_assert(is_same_v<length<metre, int>::rep, int>);
static_assert(is_same_v<fps::length<fps::mile>::rep, double>);
// constructors
static_assert(length<metre, int>().count() == 0);
constexpr length<metre, int> km{1000};
static_assert(km.count() == 1000);
static_assert(length<metre, int>(km).count() == km.count());
static_assert(length<metre, int>(1).count() == 1);
static_assert(!std::is_constructible_v<length<metre, int>, double>); // truncating conversion
static_assert(length<metre, double>(1.0).count() == 1.0);
static_assert(length<metre, double>(1).count() == 1.0);
static_assert(length<metre, double>(3.14).count() == 3.14);
static_assert(length<metre, int>(km).count() == 1000);
static_assert(!std::is_constructible_v<length<metre, int>,
length<metre, double>>); // truncating conversion
static_assert(length<metre, double>(1000.0_q_m).count() == 1000.0);
static_assert(length<metre, double>(km).count() == 1000.0);
static_assert(length<metre, int>(1_q_km).count() == 1000);
static_assert(!std::is_constructible_v<length<metre, int>,
physical::si::time<second, int>>); // different dimensions
static_assert(!std::is_constructible_v<length<kilometre, int>,
length<metre, int>>); // truncating conversion
// assignment operator
static_assert([]() { length<metre, int> l1(1), l2(2); return l2 = l1; }().count() == 1);
////////////////////////////
// static member functions // static member functions
////////////////////////////
static_assert(length<metre, int>::zero().count() == 0); static_assert(length<metre, int>::zero().count() == 0);
static_assert(length<metre, int>::min().count() == std::numeric_limits<int>::lowest()); static_assert(length<metre, int>::min().count() == std::numeric_limits<int>::lowest());
@ -88,32 +108,165 @@ static_assert(length<metre, double>::zero().count() == 0.0);
static_assert(length<metre, double>::min().count() == std::numeric_limits<double>::lowest()); static_assert(length<metre, double>::min().count() == std::numeric_limits<double>::lowest());
static_assert(length<metre, double>::max().count() == std::numeric_limits<double>::max()); static_assert(length<metre, double>::max().count() == std::numeric_limits<double>::max());
// unary member operators
static_assert((+km).count() == 1000); //////////////////////////////
static_assert((-km).count() == -1000); // construction from a value
static_assert((+(-km)).count() == -1000); //////////////////////////////
static_assert((-(-km)).count() == 1000);
static_assert([](auto v) { // only explicit construction from a value
auto vv = v++; static_assert(std::constructible_from<length<metre>, double>);
return std::pair(v, vv); static_assert(!std::convertible_to<double, length<metre>>);
}(km) == std::pair(length<metre, int>(1001), length<metre, int>(1000)));
static_assert([](auto v) {
auto vv = ++v;
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(1001), length<metre, int>(1001)));
static_assert([](auto v) {
auto vv = v--;
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(999), length<metre, int>(1000)));
static_assert([](auto v) {
auto vv = --v;
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(999), length<metre, int>(999)));
static_assert(std::constructible_from<length<metre>, float>);
static_assert(!std::convertible_to<float, length<metre>>);
static_assert(std::constructible_from<length<metre, float>, double>); // truncating implicit conversions double -> float allowed
static_assert(!std::convertible_to<double, length<metre, float>>);
static_assert(std::constructible_from<length<metre>, int>);
static_assert(!std::convertible_to<int, length<metre>>);
static_assert(std::constructible_from<length<metre>, short>);
static_assert(!std::convertible_to<short, length<metre>>);
static_assert(std::constructible_from<length<metre, short>, int>); // truncating implicit conversions int -> short allowed
static_assert(!std::convertible_to<int, length<metre, short>>);
// exception, implicit construction from a value allowed for a dimensionless quantity
static_assert(std::constructible_from<dimensionless<one>, double>);
static_assert(std::convertible_to<double, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one>, float>);
static_assert(std::convertible_to<float, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one, float>, double>);
static_assert(std::convertible_to<double, dimensionless<one, float>>);
static_assert(std::constructible_from<dimensionless<one>, int>);
static_assert(std::convertible_to<int, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one>, short>);
static_assert(std::convertible_to<short, dimensionless<one>>);
static_assert(std::constructible_from<dimensionless<one, short>, int>);
static_assert(std::convertible_to<int, dimensionless<one, short>>);
// but only if a dimensionless quantity has a ratio(1)
static_assert(std::constructible_from<dimensionless<percent>, double>);
static_assert(!std::convertible_to<double, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent>, float>);
static_assert(!std::convertible_to<float, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent, float>, double>); // truncating implicit conversions double -> float allowed
static_assert(!std::convertible_to<double, dimensionless<percent, float>>);
static_assert(std::constructible_from<dimensionless<percent>, int>);
static_assert(!std::convertible_to<int, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent>, short>);
static_assert(!std::convertible_to<short, dimensionless<percent>>);
static_assert(std::constructible_from<dimensionless<percent, short>, int>); // truncating implicit conversions int -> short allowed
static_assert(!std::convertible_to<int, dimensionless<percent, short>>);
// floating-point to integral truncating conversion not allowed
static_assert(!std::constructible_from<length<metre, int>, double>);
static_assert(!std::convertible_to<double, length<metre, int>>);
static_assert(!std::constructible_from<dimensionless<one, int>, double>);
static_assert(!std::convertible_to<double, dimensionless<one, int>>);
static_assert(length<metre, int>().count() == 0); // value initialization
static_assert(length<metre, int>(1).count() == 1);
static_assert(length<metre, double>(1.0).count() == 1.0);
static_assert(length<metre, double>(1).count() == 1.0);
static_assert(length<metre, double>(3.14).count() == 3.14);
///////////////////////////////////////
// construction from another quantity
///////////////////////////////////////
// conversion only between equivalent dimensions
static_assert(std::constructible_from<length<metre>, length<metre>>);
static_assert(std::convertible_to<length<metre>, length<metre>>);
static_assert(std::constructible_from<length<centimetre>, cgs::length<cgs::centimetre>>);
static_assert(std::convertible_to<cgs::length<cgs::centimetre>, length<centimetre>>);
static_assert(std::constructible_from<fps::length<fps::foot>, cgs::length<cgs::centimetre>>);
static_assert(std::convertible_to<cgs::length<cgs::centimetre>, fps::length<fps::foot>>);
// conversion between different dimensions not allowed
static_assert(!std::constructible_from<length<metre>, physical::si::time<second>>);
static_assert(!std::convertible_to<physical::si::time<second>, length<metre>>);
static_assert(!std::constructible_from<length<metre>, speed<metre_per_second>>);
static_assert(!std::convertible_to<speed<metre_per_second>, length<metre>>);
// implicit conversion from another quantity only if non-truncating
static_assert(std::constructible_from<length<metre>, length<metre, int>>); // int -> double OK
static_assert(std::convertible_to<length<metre, int>, length<metre>>); // int -> double OK
static_assert(!std::constructible_from<length<metre, int>, length<metre>>); // truncating double -> int not allowed
static_assert(!std::convertible_to<length<metre>, length<metre, int>>); // truncating double -> int not allowed
static_assert(std::constructible_from<length<metre, int>, length<kilometre, int>>); // kilometre<int> -> metre<int> OK
static_assert(std::convertible_to<length<kilometre, int>, length<metre, int>>); // kilometre<int> -> metre<int> OK
static_assert(!std::constructible_from<length<kilometre, int>, length<metre, int>>); // truncating metre<int> -> kilometre<int> not allowed
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::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>>);
static_assert(length<metre, int>(123_q_m).count() == 123);
static_assert(length<kilometre, int>(2_q_km).count() == 2);
static_assert(length<metre, int>(2_q_km).count() == 2000);
static_assert(length<kilometre>(1500_q_m).count() == 1.5);
/////////
// CTAD
/////////
static_assert(std::is_same_v<decltype(quantity{length<metre, int>(123)}), length<metre, int>>);
static_assert(std::is_same_v<decltype(quantity{speed<metre_per_second>(123)}), speed<metre_per_second>>);
// static_assert(std::is_same_v<decltype(length{length<metre, int>(123)}), length<metre, int>>); // TODO gcc ICE
static_assert(std::is_same_v<decltype(quantity{123_q_m}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(quantity{1}), dimensionless<one, int>>);
static_assert(std::is_same_v<decltype(quantity{1.23}), dimensionless<one, double>>);
////////////////////////
// assignment operator
////////////////////////
static_assert([]() { length<metre, int> l1(1), l2(2); return l2 = l1; }().count() == 1);
static_assert([]() { length<metre, int> l1(1), l2(2); return l2 = std::move(l1); }().count() == 1);
////////////////////
// unary operators
////////////////////
static_assert((+123_q_m).count() == 123);
static_assert((-123_q_m).count() == -123);
static_assert((+(-123_q_m)).count() == -123);
static_assert((-(-123_q_m)).count() == 123);
static_assert([](auto v) { auto vv = v++; return std::pair(v, vv); }(123_q_m) == std::pair(124_q_m, 123_q_m));
static_assert([](auto v) { auto vv = ++v; return std::pair(v, vv); }(123_q_m) == std::pair(124_q_m, 124_q_m));
static_assert([](auto v) { auto vv = v--; return std::pair(v, vv); }(123_q_m) == std::pair(122_q_m, 123_q_m));
static_assert([](auto v) { auto vv = --v; return std::pair(v, vv); }(123_q_m) == std::pair(122_q_m, 122_q_m));
////////////////////////
// compound assignment // compound assignment
////////////////////////
// same type
static_assert((1_q_m += 1_q_m).count() == 2); static_assert((1_q_m += 1_q_m).count() == 2);
static_assert((2_q_m -= 1_q_m).count() == 1); static_assert((2_q_m -= 1_q_m).count() == 1);
static_assert((1_q_m *= 2).count() == 2); static_assert((1_q_m *= 2).count() == 2);
@ -121,190 +274,372 @@ static_assert((2_q_m /= 2).count() == 1);
static_assert((7_q_m %= 2).count() == 1); static_assert((7_q_m %= 2).count() == 1);
static_assert((7_q_m %= 2_q_m).count() == 1); static_assert((7_q_m %= 2_q_m).count() == 1);
// different types
static_assert((2.5_q_m += 3_q_m).count() == 5.5); static_assert((2.5_q_m += 3_q_m).count() == 5.5);
static_assert((2.5_q_m += 3.5_q_m).count() == 6); static_assert((123_q_m += 1_q_km).count() == 1123);
static_assert((5.5_q_m -= 3_q_m).count() == 2.5);
static_assert((1123_q_m -= 1_q_km).count() == 123);
static_assert((2.5_q_m *= 3).count() == 7.5); static_assert((2.5_q_m *= 3).count() == 7.5);
static_assert((2.5_q_m *= 3.5).count() == 8.75); static_assert((7.5_q_m /= 3).count() == 2.5);
static_assert((3500_q_m %= 1_q_km).count() == 500);
// operations not allowed for the respective quantities
template<typename Metre> template<typename Metre>
concept invalid_compound_assignments = requires() concept invalid_compound_assignments = requires() {
{ // truncating not allowed
requires !requires(length<Metre, int> l) { l += 2.5_q_m; };
requires !requires(length<Metre, int> l) { l -= 2.5_q_m; };
requires !requires(length<kilometre, int> l) { l += length<Metre, int>(2); };
requires !requires(length<kilometre, int> l) { l -= length<Metre, int>(2); };
requires !requires(length<kilometre, int> l) { l %= length<Metre, int>(2); };
// only quantities can be added or subtracted
requires !requires(length<Metre, int> l) { l += 2; };
requires !requires(length<Metre, int> l) { l -= 2; };
// compound multiply/divide by another quantity not allowed
requires !requires(length<Metre, int> l) { l *= 2_q_m; };
requires !requires(length<Metre, int> l) { l /= 2_q_m; };
// modulo operations on a floating point representation not allowed
requires !requires(length<Metre, double> l) { l %= 2.; }; requires !requires(length<Metre, double> l) { l %= 2.; };
requires !requires(length<Metre, double> l) { l %= 2; }; requires !requires(length<Metre, double> l) { l %= 2; };
requires !requires(length<Metre, int> l) { l %= 2.; };
requires !requires(length<Metre, double> l) { l %= 2._q_m; }; requires !requires(length<Metre, double> l) { l %= 2._q_m; };
requires !requires(length<Metre, double> l) { l %= 2_q_m; }; requires !requires(length<Metre, double> l) { l %= 2_q_m; };
requires !requires(length<Metre, int> l) { l %= 2._q_m; }; requires !requires(length<Metre, int> l) { l %= 2._q_m; };
requires !requires(length<Metre, int> l) { l += 3.5_q_m; };
requires !requires(length<Metre, int> l) { l *= 3.5_q_m; };
}; };
static_assert(invalid_compound_assignments<metre>); static_assert(invalid_compound_assignments<metre>);
// non-member arithmetic operators
static_assert(compare<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>); ////////////////////
static_assert(compare<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>); // binary operators
static_assert(compare<decltype(length<kilometre, int>() + length<metre, double>()), length<metre, double>>); ////////////////////
static_assert(compare<decltype(length<metre, double>() - length<metre, int>()), length<metre, double>>);
static_assert(compare<decltype(length<kilometre, double>() - length<metre, int>()), length<metre, double>>);
static_assert(compare<decltype(length<metre, int>() * 1.0), length<metre, double>>);
static_assert(compare<decltype(1.0 * length<metre, int>()), length<metre, double>>);
static_assert(
compare<decltype(speed<metre_per_second, int>() * physical::si::time<second, int>()), length<metre, int>>);
static_assert(
compare<decltype(speed<metre_per_second, int>() * physical::si::time<hour, int>()), length<scaled_unit<ratio(36, 1, 2), metre>, int>>);
static_assert(compare<decltype(length<metre>() * physical::si::time<minute>()),
quantity<unknown_dimension<units::exponent<dim_length, 1>, units::exponent<dim_time, 1>>, scaled_unit<ratio(6, 1, 1), unknown_coherent_unit>>>);
static_assert(compare<decltype(1 / physical::si::time<second, int>()), frequency<hertz, int>>);
static_assert(compare<decltype(1 / physical::si::time<minute, int>()), frequency<scaled_unit<ratio(1, 6, -1), hertz>, int>>);
static_assert(compare<decltype(1 / frequency<hertz, int>()), physical::si::time<second, int>>);
static_assert(compare<decltype(1 / length<kilometre>()),
quantity<unknown_dimension<units::exponent<dim_length, -1>>, scaled_unit<ratio(1, 1, -3), unknown_coherent_unit>>>);
static_assert(compare<decltype(length<metre, int>() / 1.0), length<metre, double>>);
static_assert(compare<decltype(length<metre, int>() / length<metre, double>()), dimensionless<one, double>>);
static_assert(compare<decltype(length<kilometre, int>() / length<metre, double>()), dimensionless<scaled_unit<ratio(1, 1, 3), one>, double>>);
static_assert(
compare<decltype(length<metre, int>() / physical::si::time<second, int>()), speed<metre_per_second, int>>);
static_assert(
compare<decltype(length<metre>() / physical::si::time<minute>()), speed<scaled_unit<ratio(1, 6, -1), metre_per_second>>>);
static_assert(compare<decltype(physical::si::time<minute>() / length<metre>()),
quantity<unknown_dimension<units::exponent<dim_length, -1>, units::exponent<dim_time, 1>>, scaled_unit<ratio(6 ,1 , 1), unknown_coherent_unit>>>);
static_assert(compare<decltype(length<metre, int>() % short(1)), length<metre, int>>);
static_assert(compare<decltype(length<metre, int>() % length<metre, short>(1)), length<metre, int>>);
static_assert((1_q_m + km).count() == 1001); template<typename Metre>
concept invalid_binary_operations = requires {
// no crossdimensional addition and subtraction
requires !requires { 1_q_s + length<Metre, int>(1); };
requires !requires { 1_q_s - length<Metre, int>(1); };
// no floating-point modulo
requires !requires(length<Metre, double> a) { a % 2_q_m; };
requires !requires(length<Metre, double> a) { 2_q_m % a; };
requires !requires(length<Metre, double> a) { a % 2; };
requires !requires(length<Metre, double> a, length<Metre, double> b) { a % b; };
requires !requires(length<Metre, double> a, length<Metre, int> b) { a % b; };
requires !requires(length<Metre, double> a, length<Metre, int> b) { b % a; };
};
static_assert(invalid_binary_operations<metre>);
// same representation type
static_assert(std::is_same_v<decltype(1_q_m + 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m - 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m * 1), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m * quantity{1}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1 * 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(quantity{1} * 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m / 1), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m / quantity{1}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m % 1), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m % quantity{1}), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_m % 1_q_m), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m * dimensionless<percent, std::int64_t>(1)), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) * 1_q_m), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(1_q_m / dimensionless<percent, std::int64_t>(1)), length<hectometre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % dimensionless<percent, std::int64_t>(1)), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(1_q_m * 1_q_m), area<square_metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_m), dimensionless<one, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_s), frequency<hertz, std::int64_t>>);
static_assert(compare<decltype(quantity{1} / 1_q_s), frequency<hertz, std::int64_t>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1_q_s), frequency<scaled_unit<ratio(1, 100), hertz>, std::int64_t>>);
// different representation types
static_assert(std::is_same_v<decltype(1_q_m + 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m - 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m * 1.L), length<metre, long double>>); // TODO should we address fundamental types implicit truncating conversions with concepts?
static_assert(std::is_same_v<decltype(1 * 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m * quantity{1.L}), length<metre, long double>>); // TODO should we address fundamental types implicit truncating conversions with concepts?
static_assert(std::is_same_v<decltype(quantity{1} * 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m / 1.L), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m / quantity{1.L}), length<metre, long double>>);
static_assert(compare<decltype(1_q_m * dimensionless<percent, long double>(1)), length<centimetre, long double>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) * 1._q_m), length<centimetre, long double>>);
static_assert(compare<decltype(1_q_m * 1._q_m), area<square_metre, long double>>);
static_assert(compare<decltype(1_q_m / dimensionless<percent, long double>(1)), length<hectometre, long double>>);
static_assert(compare<decltype(1_q_m / 1._q_m), dimensionless<one, long double>>);
static_assert(compare<decltype(1 / 1._q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(quantity{1} / 1._q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1._q_s), frequency<scaled_unit<ratio(1, 100), hertz>, long double>>);
static_assert(compare<decltype(1_q_m % short(1)), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % quantity{short(1)}), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % dimensionless<percent, short>(1)), length<centimetre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % length<metre, short>(1)), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_m + 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m - 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m * 1), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1.L * 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m * quantity{1}), length<metre, long double>>);
static_assert(std::is_same_v<decltype(quantity{1.L} * 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m / 1), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m / quantity{1}), length<metre, long double>>);
static_assert(compare<decltype(1._q_m * dimensionless<percent, std::int64_t>(1)), length<centimetre, long double>>);
static_assert(compare<decltype(dimensionless<percent, long double>(1) * 1_q_m), length<centimetre, long double>>);
static_assert(compare<decltype(1._q_m / dimensionless<percent, std::int64_t>(1)), length<hectometre, long double>>);
static_assert(compare<decltype(1._q_m * 1_q_m), area<square_metre, long double>>);
static_assert(compare<decltype(1._q_m / 1_q_m), dimensionless<one, long double>>);
static_assert(compare<decltype(1.L / 1_q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(quantity{1.L} / 1_q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(dimensionless<percent, long double>(1) / 1_q_s), frequency<scaled_unit<ratio(1, 100), hertz>, long double>>);
// different units
static_assert(std::is_same_v<decltype(1_q_m + 1_q_km), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_m + 1_q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m + 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m + 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km + 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_km + 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km + 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_km + 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m - 1_q_km), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_m - 1_q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m - 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_m - 1._q_km), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km - 1_q_m), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1._q_km - 1_q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_km - 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1._q_km - 1._q_m), length<metre, long double>>);
static_assert(std::is_same_v<decltype(1_q_m % 1_q_km), length<metre, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_q_km % 1_q_m), length<metre, std::int64_t>>);
// different dimensions
static_assert(compare<decltype(1_q_m_per_s * 1_q_s), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m_per_s * 1_q_h), length<scaled_unit<ratio(36, 1, 2), metre>, std::int64_t>>);
static_assert(compare<decltype(1_q_m * 1_q_min), quantity<unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, 1>>, scaled_unit<ratio(60), unknown_coherent_unit>, std::int64_t>>);
static_assert(compare<decltype(1_q_s * 1_q_Hz), dimensionless<one, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_min), frequency<scaled_unit<ratio(1, 60), hertz>, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_Hz), physical::si::time<second, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_km), quantity<unknown_dimension<exponent<dim_length, -1>>, scaled_unit<ratio(1, 1, -3), unknown_coherent_unit>, std::int64_t>>);
static_assert(compare<decltype(1_q_km / 1_q_m), dimensionless<scaled_unit<ratio(1000), one>, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_s), speed<metre_per_second, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_min), speed<scaled_unit<ratio(1, 60), metre_per_second>, std::int64_t>>);
static_assert(compare<decltype(1_q_min / 1_q_m), quantity<unknown_dimension<exponent<dim_length, -1>, exponent<dim_time, 1>>, scaled_unit<ratio(60), unknown_coherent_unit>, std::int64_t>>);
static_assert((1_q_m + 1_q_m).count() == 2);
static_assert((1_q_m + 1_q_km).count() == 1001); static_assert((1_q_m + 1_q_km).count() == 1001);
static_assert((km - 1_q_m).count() == 999); static_assert((1_q_km + 1_q_m).count() == 1001);
static_assert((2_q_m - 1_q_m).count() == 1);
static_assert((1_q_km - 1_q_m).count() == 999); static_assert((1_q_km - 1_q_m).count() == 999);
static_assert((2_q_m * 2).count() == 4); static_assert((2_q_m * 2).count() == 4);
static_assert((2_q_m * quantity{2}).count() == 4);
static_assert((2_q_m * dimensionless<percent, int>(2)).count() == 4);
static_assert((3 * 3_q_m).count() == 9); static_assert((3 * 3_q_m).count() == 9);
static_assert((quantity{3} * 3_q_m).count() == 9);
static_assert((dimensionless<percent, int>(3) * 3_q_m).count() == 9);
static_assert((4_q_m / 2).count() == 2); static_assert((4_q_m / 2).count() == 2);
static_assert((4_q_m / quantity{2}).count() == 2);
static_assert((4_q_m / dimensionless<percent, int>(2)).count() == 2);
static_assert((4_q_km / 2_q_m).count() == 2); static_assert((4_q_km / 2_q_m).count() == 2);
static_assert((4000_q_m / 2_q_m).count() == 2000); static_assert((4000_q_m / 2_q_m).count() == 2000);
static_assert((1.5_q_m + 1_q_m).count() == 2.5);
static_assert((1.5_q_m + 1_q_km).count() == 1001.5);
static_assert((1.5_q_km + 1_q_m).count() == 1501);
static_assert((2.5_q_m - 1_q_m).count() == 1.5);
static_assert((1.5_q_km - 1_q_m).count() == 1499);
static_assert((2.5_q_m * 2).count() == 5);
static_assert((2.5_q_m * quantity{2}).count() == 5);
static_assert((2.5_q_m * dimensionless<percent, int>(2)).count() == 5);
static_assert((2.5L * 2_q_m).count() == 5);
static_assert((quantity{2.5L} * 2_q_m).count() == 5);
static_assert((dimensionless<percent, long double>(2.5L) * 2_q_m).count() == 5);
static_assert((5._q_m / 2).count() == 2.5);
static_assert((5._q_m / quantity{2}).count() == 2.5);
static_assert((5._q_m / dimensionless<percent, int>(2)).count() == 2.5);
static_assert((5._q_km / 2_q_m).count() == 2.5);
static_assert((5000._q_m / 2_q_m).count() == 2500);
static_assert((1_q_m + 1.5_q_m).count() == 2.5);
static_assert((1_q_m + 1.5_q_km).count() == 1501);
static_assert((1_q_km + 1.5_q_m).count() == 1001.5);
static_assert((2_q_m - 1.5_q_m).count() == 0.5);
static_assert((1_q_km - 1.5_q_m).count() == 998.5);
static_assert((2_q_m * 2.5L).count() == 5);
static_assert((2_q_m * quantity{2.5L}).count() == 5);
static_assert((2_q_m * dimensionless<percent, long double>(2.5L)).count() == 5);
static_assert((2 * 2.5_q_m).count() == 5);
static_assert((quantity{2} * 2.5_q_m).count() == 5);
static_assert((dimensionless<percent, int>(2) * 2.5_q_m).count() == 5);
static_assert((5_q_m / 2.5L).count() == 2);
static_assert((5_q_m / quantity{2.5L}).count() == 2);
static_assert((5_q_m / dimensionless<percent, long double>(2.5L)).count() == 2);
static_assert((5_q_km / 2.5_q_m).count() == 2);
static_assert((5000_q_m / 2.5_q_m).count() == 2000);
static_assert((7_q_m % 2).count() == 1); static_assert((7_q_m % 2).count() == 1);
static_assert((7_q_m % quantity{2}).count() == 1);
static_assert((7_q_m % dimensionless<percent, int>(2)).count() == 1);
static_assert((7_q_m % 2_q_m).count() == 1); static_assert((7_q_m % 2_q_m).count() == 1);
static_assert((7_q_km % 2000_q_m).count() == 1000); static_assert((7_q_km % 2000_q_m).count() == 1000);
static_assert((10_q_km2 * 10_q_km2) / 50_q_km2 == 2_q_km2); static_assert((10_q_km2 * 10_q_km2) / 50_q_km2 == 2_q_km2);
constexpr auto q1 = 10_q_km / 5_q_m; static_assert((10_q_km / 5_q_m).count() == 2);
static_assert(compare<decltype(q1), const dimensionless<scaled_unit<ratio(1, 1, 3), one>, std::int64_t>>); static_assert(dimensionless<one>(10_q_km / 5_q_m).count() == 2000);
static_assert(q1.count() == 2);
constexpr dimensionless<one> q2 = q1;
static_assert(q2.count() == 2000);
#if UNITS_DOWNCAST_MODE == 0 #if UNITS_DOWNCAST_MODE == 0
static_assert(quantity_cast<dim_one, one>(q1).count() == 2000); static_assert(quantity_cast<dim_one, one>(10_q_km / 5_q_m).count() == 2000);
#else #else
static_assert(quantity_cast<one>(q1).count() == 2000); static_assert(quantity_cast<one>(10_q_km / 5_q_m).count() == 2000);
#endif #endif
constexpr auto q3 = 10_q_s * 2_q_kHz; static_assert((10_q_s * 2_q_kHz).count() == 20);
static_assert(compare<decltype(q3), const dimensionless<scaled_unit<ratio(1, 1, 3), one>, std::int64_t>>);
static_assert(q3.count() == 20);
// comparators
static_assert(2_q_m + 1_q_m == 3_q_m); // dimensionless
static_assert(!(2_q_m + 2_q_m == 3_q_m));
static_assert(2_q_m + 2_q_m != 3_q_m);
static_assert(!(2_q_m + 2_q_m != 4_q_m));
static_assert(2_q_m > 1_q_m);
static_assert(!(1_q_m > 1_q_m));
static_assert(1_q_m < 2_q_m);
static_assert(!(2_q_m < 2_q_m));
static_assert(2_q_m >= 1_q_m);
static_assert(2_q_m >= 2_q_m);
static_assert(!(2_q_m >= 3_q_m));
static_assert(1_q_m <= 2_q_m);
static_assert(2_q_m <= 2_q_m);
static_assert(!(3_q_m <= 2_q_m));
static_assert(3_q_m == 3.0_q_m); static_assert(quantity{1} + quantity{1} == 2);
static_assert(3_q_m != 3.14_q_m); static_assert(1 + quantity{1} == 2);
static_assert(2_q_m > 1.0_q_m); static_assert(quantity{1} + 1 == 2);
static_assert(1.0_q_m < 2_q_m); static_assert(quantity{2} - quantity{1} == 1);
static_assert(2.0_q_m >= 1_q_m); static_assert(2 - quantity{1} == 1);
static_assert(1_q_m <= 2.0_q_m); static_assert(quantity{2} - 1 == 1);
static_assert(quantity{2} * quantity{2} == 4);
static_assert(2 * quantity{2} == 4);
static_assert(quantity{2} * 2 == 4);
static_assert(quantity{4} / quantity{2} == 2);
static_assert(4 / quantity{2} == 2);
static_assert(quantity{4} / 2 == 2);
static_assert(quantity{4} % quantity{2} == 0);
static_assert(4 % quantity{2} == 0);
static_assert(quantity{4} % 2 == 0);
static_assert(1000_q_m == 1_q_km);
static_assert(1001_q_m != 1_q_km);
static_assert(1001_q_m > 1_q_km);
static_assert(999_q_m < 1_q_km);
static_assert(1000_q_m >= 1_q_km);
static_assert(1000_q_m <= 1_q_km);
// alias units ///////////////////////
// equality operators
///////////////////////
static_assert(2_q_l + 2_q_ml == 2002_q_ml); template<typename Metre>
static_assert(2_q_l + 2_q_ml == 2002_q_cm3); concept no_crossdimensional_equality = requires {
static_assert(2_q_l + 2_q_cm3 == 2002_q_ml); requires !requires { 1_q_s == length<Metre, int>(1); };
static_assert(2_q_dm3 + 2_q_cm3 == 2002_q_ml); requires !requires { 1_q_s != length<Metre, int>(1); };
};
static_assert(no_crossdimensional_equality<metre>);
// is_quantity // same type
static_assert(length<metre, int>(123) == length<metre, int>(123));
static_assert(length<metre, int>(321) != length<metre, int>(123));
static_assert(!(length<metre, int>(123) == length<metre, int>(321)));
static_assert(!(length<metre, int>(123) != length<metre, int>(123)));
static_assert(Quantity<length<millimetre, int>>); // different types
static_assert(length<metre, double>(123) == length<metre, int>(123));
static_assert(length<metre, double>(321) != length<metre, int>(123));
static_assert(!(length<metre, double>(123) == length<metre, int>(321)));
static_assert(!(length<metre, double>(123) != length<metre, int>(123)));
// common_quantity static_assert(length<kilometre, int>(123) == length<metre, int>(123000));
static_assert(length<kilometre, int>(321) != length<metre, int>(123000));
static_assert(!(length<kilometre, int>(123) == length<metre, int>(321000)));
static_assert(!(length<kilometre, int>(123) != length<metre, int>(123000)));
static_assert(compare<common_quantity<length<metre, int>, length<kilometre, int>>, length<metre, int>>); // dimensionless
static_assert(compare<common_quantity<length<kilometre, long long>, length<metre, int>>, length<metre, long long>>);
static_assert(
compare<common_quantity<length<kilometre, long long>, length<millimetre, double>>, length<millimetre, double>>);
// common_type static_assert(quantity{123} == 123);
static_assert(quantity{321} != 123);
static_assert(123 == quantity{123});
static_assert(123 != quantity{321});
using namespace units::physical::si::us::literals;
static_assert(std::equality_comparable<decltype(1_q_m)>); ///////////////////////
static_assert(std::equality_comparable_with<decltype(1_q_m), decltype(1_q_cm)>); // ordering operators
static_assert(0_q_m == 0_q_ft_us); ///////////////////////
static_assert(std::equality_comparable_with<decltype(1_q_m), decltype(1_q_ft_us)>);
template<typename Metre>
concept no_crossdimensional_ordering = requires {
requires !requires { 1_q_s < length<Metre, int>(1); };
requires !requires { 1_q_s > length<Metre, int>(1); };
requires !requires { 1_q_s <= length<Metre, int>(1); };
requires !requires { 1_q_s >= length<Metre, int>(1); };
};
static_assert(no_crossdimensional_ordering<metre>);
// same type
static_assert(length<metre, int>(123) < length<metre, int>(321));
static_assert(length<metre, int>(123) <= length<metre, int>(123));
static_assert(length<metre, int>(123) <= length<metre, int>(321));
static_assert(length<metre, int>(321) > length<metre, int>(123));
static_assert(length<metre, int>(123) >= length<metre, int>(123));
static_assert(length<metre, int>(321) >= length<metre, int>(123));
static_assert(!(length<metre, int>(321) < length<metre, int>(123)));
static_assert(!(length<metre, int>(123) < length<metre, int>(123)));
static_assert(!(length<metre, int>(321) <= length<metre, int>(123)));
static_assert(!(length<metre, int>(123) > length<metre, int>(321)));
static_assert(!(length<metre, int>(123) > length<metre, int>(123)));
static_assert(!(length<metre, int>(123) >= length<metre, int>(321)));
// different types
static_assert(length<metre, double>(123) < length<metre, int>(321));
static_assert(length<metre, double>(123) <= length<metre, int>(123));
static_assert(length<metre, double>(123) <= length<metre, int>(321));
static_assert(length<metre, double>(321) > length<metre, int>(123));
static_assert(length<metre, double>(123) >= length<metre, int>(123));
static_assert(length<metre, double>(321) >= length<metre, int>(123));
static_assert(!(length<metre, double>(321) < length<metre, int>(123)));
static_assert(!(length<metre, double>(123) < length<metre, int>(123)));
static_assert(!(length<metre, double>(321) <= length<metre, int>(123)));
static_assert(!(length<metre, double>(123) > length<metre, int>(321)));
static_assert(!(length<metre, double>(123) > length<metre, int>(123)));
static_assert(!(length<metre, double>(123) >= length<metre, int>(321)));
static_assert(length<kilometre, int>(123) < length<metre, int>(321000));
static_assert(length<kilometre, int>(123) <= length<metre, int>(123000));
static_assert(length<kilometre, int>(123) <= length<metre, int>(321000));
static_assert(length<kilometre, int>(321) > length<metre, int>(123000));
static_assert(length<kilometre, int>(123) >= length<metre, int>(123000));
static_assert(length<kilometre, int>(321) >= length<metre, int>(123000));
static_assert(!(length<kilometre, int>(321) < length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(123) < length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(321) <= length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(123) > length<metre, int>(321000)));
static_assert(!(length<kilometre, int>(123) > length<metre, int>(123000)));
static_assert(!(length<kilometre, int>(123) >= length<metre, int>(321000)));
// dimensionless
static_assert(quantity{123} < 321);
static_assert(quantity{123} <= 123);
static_assert(quantity{123} <= 321);
static_assert(quantity{321} > 123);
static_assert(quantity{123} >= 123);
static_assert(quantity{321} >= 123);
static_assert(123 < quantity{321});
static_assert(123 <= quantity{123});
static_assert(123 <= quantity{321});
static_assert(321 > quantity{123});
static_assert(123 >= quantity{123});
static_assert(321 >= quantity{123});
//////////////////
// dimensionless
//////////////////
static_assert(std::equality_comparable_with<dimensionless<one>, int>); static_assert(std::equality_comparable_with<dimensionless<one>, int>);
static_assert(std::equality_comparable_with<dimensionless<one>, double>); static_assert(std::equality_comparable_with<dimensionless<one>, double>);
static_assert(std::equality_comparable_with<dimensionless<one, int>, int>); static_assert(std::equality_comparable_with<dimensionless<one, int>, int>);
static_assert(!std::equality_comparable_with<dimensionless<one, int>, double>); static_assert(!std::equality_comparable_with<dimensionless<one, int>, double>);
// quantity_cast
static_assert(compare<decltype(quantity_cast<scaled_unit<ratio(1), metre>>(2_q_km))::unit, metre>);
static_assert(quantity_cast<length<metre, int>>(2_q_km).count() == 2000);
static_assert(quantity_cast<length<kilometre, int>>(2000_q_m).count() == 2);
static_assert(quantity_cast<length<metre, int>>(1.23_q_m).count() == 1);
static_assert(quantity_cast<metre>(2_q_km).count() == 2000);
static_assert(quantity_cast<kilometre>(2000_q_m).count() == 2);
static_assert(quantity_cast<int>(1.23_q_m).count() == 1);
static_assert(quantity_cast<dim_speed, kilometre_per_hour>(2000.0_q_m / 3600.0_q_s).count() == 2);
// dimensionless
static_assert(std::is_convertible_v<double, dimensionless<one>>);
static_assert(std::is_convertible_v<float, dimensionless<one>>);
static_assert(!std::is_convertible_v<double, dimensionless<one, int>>);
static_assert(std::is_convertible_v<int, dimensionless<one>>);
static_assert(!std::is_convertible_v<double, dimensionless<scaled_unit<ratio(1, 1, 1), one>>>);
static_assert(std::is_constructible_v<dimensionless<scaled_unit<ratio(1, 1, 1), one>>, double>);
static_assert(dimensionless<one>(1.23) + dimensionless<one>(1.23) == dimensionless<one>(2.46));
static_assert(dimensionless<one>(1.23) + dimensionless<one>(1.23) == 2.46);
static_assert(dimensionless<one>(1.23) + 1.23 == 2.46);
static_assert(1.23 + dimensionless<one>(1.23) == 2.46);
static_assert(dimensionless<one>(1) + 1 == 2);
static_assert(dimensionless<one, int>(1) + 1 == 2);
template<typename Int> template<typename Int>
concept invalid_dimensionless_operations = requires concept invalid_dimensionless_operations = requires {
{
requires !requires(dimensionless<one, Int> d) { d + 1.23; }; requires !requires(dimensionless<one, Int> d) { d + 1.23; };
requires !requires(dimensionless<one, Int> d) { 1.23 + d; }; requires !requires(dimensionless<one, Int> d) { 1.23 + d; };
requires !requires(dimensionless<scaled_unit<ratio(1, 1, 1), one>, Int> d) { 1 + d; }; requires !requires(dimensionless<percent, Int> d) { 1 + d; };
requires !requires(dimensionless<scaled_unit<ratio(1, 1, 1), one>, Int> d) { d + 1; }; requires !requires(dimensionless<percent, Int> d) { d + 1; };
}; };
static_assert(invalid_dimensionless_operations<int>); static_assert(invalid_dimensionless_operations<int>);
@ -319,36 +654,36 @@ static_assert(50._q_m / 100._q_m == dimensionless<percent>(50));
static_assert(dimensionless<one>(dimensionless<percent>(50)).count() == 0.5); static_assert(dimensionless<one>(dimensionless<percent>(50)).count() == 0.5);
static_assert(std::is_same_v<decltype(quantity{1}), dimensionless<one, int>>);
static_assert(std::is_same_v<decltype(quantity{1.23}), dimensionless<one, double>>);
// time ////////////////
// alias units
////////////////
static_assert(1_q_h == 3600_q_s); static_assert(compare<decltype(2_q_l + 2_q_ml), volume<cubic_centimetre, std::int64_t>>);
static_assert(2_q_l + 2_q_ml == 2002_q_cm3);
static_assert(2_q_l + 2_q_ml == 2002_q_ml);
static_assert(2_q_l + 2_q_cm3 == 2002_q_ml);
static_assert(2_q_dm3 + 2_q_cm3 == 2002_q_ml);
template<typename Metre>
concept no_crossdimensional_equality = !requires { 1_q_s == length<Metre, int>(1); };
static_assert(no_crossdimensional_equality<metre>); //////////////////
// quantity_cast
//////////////////
// length static_assert(compare<decltype(quantity_cast<scaled_unit<ratio(1), metre>>(2_q_km))::unit, metre>);
static_assert(1_q_km == 1000_q_m); static_assert(quantity_cast<length<metre, int>>(2_q_km).count() == 2000);
static_assert(1_q_km + 1_q_m == 1001_q_m); static_assert(quantity_cast<length<kilometre, int>>(2000_q_m).count() == 2);
static_assert(10_q_km / 5_q_km == 2); static_assert(quantity_cast<length<metre, int>>(1.23_q_m).count() == 1);
static_assert(10_q_km / 2 == 5_q_km); static_assert(quantity_cast<metre>(2_q_km).count() == 2000);
static_assert(quantity_cast<kilometre>(2000_q_m).count() == 2);
static_assert(quantity_cast<int>(1.23_q_m).count() == 1);
static_assert(quantity_cast<dim_speed, kilometre_per_hour>(2000.0_q_m / 3600.0_q_s).count() == 2);
// speed
static_assert(10_q_m / 5_q_s == 2_q_m_per_s);
static_assert(10 / 5_q_s * 1_q_m == 2_q_m_per_s);
static_assert(1_q_km / 1_q_s == 1000_q_m_per_s);
static_assert(2_q_km_per_h * 2_q_h == 4_q_km);
static_assert(2_q_km / 2_q_km_per_h == 1_q_h);
static_assert(compare<decltype(pow<2>(2_q_m)), decltype(4_q_m2)>);
////////////////
// downcasting // downcasting
////////////////
#if UNITS_DOWNCAST_MODE == 0 #if UNITS_DOWNCAST_MODE == 0

View File

@ -20,8 +20,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include "test_tools.h"
#include <units/math.h>
#include <units/physical/si/si.h> #include <units/physical/si/si.h>
#include <utility>
namespace { namespace {