refactor: implementation cleanup + support for units as references started

This commit is contained in:
Mateusz Pusz
2023-02-02 14:56:29 +01:00
parent 622b3e3cbd
commit cf3408a3c8
31 changed files with 1085 additions and 615 deletions

View File

@@ -44,15 +44,16 @@ constexpr quantity<isq::speed[m / s]> fixed_double_si_avg_speed(quantity<isq::le
return d / t;
}
constexpr quantity_of<isq::speed> auto avg_speed(quantity_of<isq::length> auto d, quantity_of<isq::time> auto t)
constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d, QuantityOf<isq::time> auto t)
{
return quantity_cast<isq::speed>(d / t);
}
template<quantity_of<isq::length> D, quantity_of<isq::time> T, quantity_of<isq::speed> V>
template<QuantityOf<isq::length> D, QuantityOf<isq::time> T, QuantityOf<isq::speed> V>
void print_result(D distance, T duration, V speed)
{
const auto result_in_kmph = quantity_cast<km / h>(speed);
constexpr auto kmph = si::kilo<si::metre> / si::hour;
const auto result_in_kmph = quantity_cast<kmph>(speed);
std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph
<< ".\n";
}

View File

@@ -53,10 +53,10 @@ public:
{
}
[[nodiscard]] constexpr quantity_of<isq::weight> auto filled_weight() const
[[nodiscard]] constexpr QuantityOf<isq::weight> auto filled_weight() const
{
const weak_quantity_of<isq::volume> auto volume = base_ * height_;
const weak_quantity_of<isq::mass> auto mass = density_ * volume;
const WeakQuantityOf<isq::volume> auto volume = base_ * height_;
const WeakQuantityOf<isq::mass> auto mass = density_ * volume;
return quantity_cast<isq::weight>(mass * g);
}
@@ -96,9 +96,9 @@ int main()
const auto spare_capacity = box.spare_capacity(measured_mass);
const auto filled_weight = box.filled_weight();
const weak_quantity_of<isq::mass_change_rate> auto input_flow_rate = measured_mass / fill_time;
const weak_quantity_of<isq::speed> auto float_rise_rate = fill_level / fill_time;
const quantity_of<isq::time> auto fill_time_left = (height / fill_level - 1) * fill_time;
const WeakQuantityOf<isq::mass_change_rate> auto input_flow_rate = measured_mass / fill_time;
const WeakQuantityOf<isq::speed> auto float_rise_rate = fill_level / fill_time;
const QuantityOf<isq::time> auto fill_time_left = (height / fill_level - 1) * fill_time;
const auto fill_percent = (fill_level / height)[percent];

View File

@@ -105,7 +105,7 @@ struct glider {
std::array<polar_point, 1> polar;
};
constexpr mp_units::weak_quantity_of<mp_units::dimensionless> auto glide_ratio(const glider::polar_point& polar)
constexpr mp_units::WeakQuantityOf<mp_units::dimensionless> auto glide_ratio(const glider::polar_point& polar)
{
return polar.v / -polar.climb;
}

View File

@@ -95,15 +95,15 @@ constexpr mp_units::quantity<mp_units::dimensionless[mp_units::one]> kalman_gain
}
// state update
template<typename Q, QuantityOrQuantityPoint QM, mp_units::quantity_of<mp_units::dimensionless> K>
template<typename Q, QuantityOrQuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K>
requires(Q::quantity_spec == QM::quantity_spec)
constexpr state<Q> state_update(const state<Q>& predicted, QM measured, K gain)
{
return {get<0>(predicted) + gain * (measured - get<0>(predicted))};
}
template<typename Q1, typename Q2, QuantityOrQuantityPoint QM, mp_units::quantity_of<mp_units::dimensionless> K,
mp_units::quantity_of<mp_units::isq::time> T>
template<typename Q1, typename Q2, QuantityOrQuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K,
mp_units::QuantityOf<mp_units::isq::time> T>
requires(Q1::quantity_spec == QM::quantity_spec)
constexpr state<Q1, Q2> state_update(const state<Q1, Q2>& predicted, QM measured, std::array<K, 2> gain, T interval)
{
@@ -113,7 +113,7 @@ constexpr state<Q1, Q2> state_update(const state<Q1, Q2>& predicted, QM measured
}
template<typename Q1, typename Q2, typename Q3, QuantityOrQuantityPoint QM,
mp_units::quantity_of<mp_units::dimensionless> K, mp_units::quantity_of<mp_units::isq::time> T>
mp_units::QuantityOf<mp_units::dimensionless> K, mp_units::QuantityOf<mp_units::isq::time> T>
requires(Q1::quantity_spec == QM::quantity_spec)
constexpr state<Q1, Q2, Q3> state_update(const state<Q1, Q2, Q3>& predicted, QM measured, std::array<K, 3> gain,
T interval)
@@ -125,14 +125,14 @@ constexpr state<Q1, Q2, Q3> state_update(const state<Q1, Q2, Q3>& predicted, QM
}
// covariance update
template<mp_units::Quantity Q, mp_units::quantity_of<mp_units::dimensionless> K>
template<mp_units::Quantity Q, mp_units::QuantityOf<mp_units::dimensionless> K>
constexpr Q covariance_update(Q uncertainty, K gain)
{
return (1 - gain) * uncertainty;
}
// state extrapolation
template<typename Q1, typename Q2, mp_units::quantity_of<mp_units::isq::time> T>
template<typename Q1, typename Q2, mp_units::QuantityOf<mp_units::isq::time> T>
constexpr state<Q1, Q2> state_extrapolation(const state<Q1, Q2>& estimated, T interval)
{
const auto q1 = get<0>(estimated) + get<1>(estimated) * interval;
@@ -140,7 +140,7 @@ constexpr state<Q1, Q2> state_extrapolation(const state<Q1, Q2>& estimated, T in
return {q1, q2};
}
template<typename Q1, typename Q2, typename Q3, mp_units::quantity_of<mp_units::isq::time> T>
template<typename Q1, typename Q2, typename Q3, mp_units::QuantityOf<mp_units::isq::time> T>
constexpr state<Q1, Q2, Q3> state_extrapolation(const state<Q1, Q2, Q3>& estimated, T interval)
{
const auto q1 = get<0>(estimated) + get<1>(estimated) * interval + get<2>(estimated) * pow<2>(interval) / 2;

View File

@@ -38,7 +38,7 @@ void print_header(const kalman::State auto& initial)
"Next Estimate");
}
void print(auto iteration, quantity_of<dimensionless> auto gain, Quantity auto measured,
void print(auto iteration, QuantityOf<dimensionless> auto gain, Quantity auto measured,
const kalman::State auto& current, const kalman::State auto& next)
{
std::cout << STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current, next);

View File

@@ -40,7 +40,7 @@ void print_header(kalman::estimation<Q> initial)
"Next Estimate");
}
template<Quantity Q, quantity_of<dimensionless> K>
template<Quantity Q, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, Q measured, kalman::estimation<Q> current, kalman::estimation<Q> next)
{
std::cout << STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured, current,
@@ -60,7 +60,7 @@ int main()
const auto measurement_uncertainty = pow<2>(5. * isq::height[m]);
auto update = [=]<Quantity Q>(const estimation<Q>& previous, const Q& measurement,
quantity_of<dimensionless> auto gain) {
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, measurement, gain), covariance_update(previous.uncertainty, gain)};
};

View File

@@ -41,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
"Next Estimate");
}
template<QuantityPoint QP, quantity_of<dimensionless> K>
template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{
std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
@@ -64,7 +64,7 @@ int main()
const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
quantity_of<dimensionless> auto gain) {
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
};

View File

@@ -41,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
"Next Estimate");
}
template<QuantityPoint QP, quantity_of<dimensionless> K>
template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{
std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
@@ -64,7 +64,7 @@ int main()
const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
quantity_of<dimensionless> auto gain) {
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
};

View File

@@ -41,7 +41,7 @@ void print_header(kalman::estimation<QP> initial)
"Next Estimate");
}
template<QuantityPoint QP, quantity_of<dimensionless> K>
template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{
std::cout << STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain,
@@ -64,7 +64,7 @@ int main()
const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
quantity_of<dimensionless> auto gain) {
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
};

View File

@@ -245,7 +245,7 @@ struct quantity_formatter {
template<std::input_iterator It, std::sentinel_for<It> S>
void on_quantity_unit(It, S)
{
out = unit_symbol_to<CharT>(out, Reference.unit, specs.unit);
out = unit_symbol_to<CharT>(out, get_unit(Reference), specs.unit);
}
};
@@ -396,9 +396,9 @@ private:
if (begin == end || *begin == '}') {
// default format should print value followed by the unit separated with 1 space
out = mp_units::detail::format_units_quantity_value<CharT>(out, q.number(), specs.rep, ctx.locale());
if constexpr (!std::derived_from<decltype(Reference.unit), mp_units::derived_unit<>>) {
if constexpr (!std::derived_from<decltype(get_unit(Reference)), mp_units::derived_unit<>>) {
*out++ = CharT(' ');
out = unit_symbol_to<CharT>(out, Reference.unit);
out = unit_symbol_to<CharT>(out, get_unit(Reference));
}
} else {
// user provided format

View File

@@ -35,9 +35,9 @@ template<typename CharT, class Traits, auto R, typename Rep>
void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
{
os << q.number();
if constexpr (!std::derived_from<decltype(R.unit), derived_unit<>>) {
if constexpr (!std::derived_from<decltype(get_unit(R)), derived_unit<>>) {
os << " ";
unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), R.unit);
unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), get_unit(R));
}
}

View File

@@ -33,6 +33,32 @@ check_libcxx_in_use(${projectPrefix}LIBCXX)
add_library(
mp-units-core
INTERFACE
include/mp_units/bits/external/fixed_string.h
include/mp_units/bits/external/hacks.h
include/mp_units/bits/external/type_list.h
include/mp_units/bits/external/type_name.h
include/mp_units/bits/external/type_traits.h
include/mp_units/bits/algorithm.h
include/mp_units/bits/dimension_concepts.h
include/mp_units/bits/expression_template.h
include/mp_units/bits/magnitude.h
include/mp_units/bits/math_concepts.h
include/mp_units/bits/prime.h
include/mp_units/bits/quantity_cast.h
include/mp_units/bits/quantity_concepts.h
include/mp_units/bits/quantity_point_concepts.h
include/mp_units/bits/quantity_spec_concepts.h
include/mp_units/bits/ratio_maths.h
include/mp_units/bits/ratio.h
include/mp_units/bits/reference_concepts.h
include/mp_units/bits/representation_concepts.h
include/mp_units/bits/symbol_text.h
include/mp_units/bits/text_tools.h
include/mp_units/bits/unit_concepts.h
include/mp_units/concepts.h
include/mp_units/customization_points.h
include/mp_units/dimension.h
include/mp_units/quantity.h

View File

@@ -0,0 +1,110 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp_units/bits/expression_template.h>
#include <mp_units/bits/external/type_traits.h>
#include <mp_units/bits/symbol_text.h>
namespace mp_units {
template<basic_symbol_text Symbol>
struct base_dimension;
namespace detail {
template<basic_symbol_text Symbol>
void to_base_base_dimension(const volatile base_dimension<Symbol>*);
template<typename T>
inline constexpr bool is_specialization_of_base_dimension = false;
template<basic_symbol_text Symbol>
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true;
} // namespace detail
/**
* @brief A concept matching all named base dimensions in the library.
*
* Satisfied by all dimension types derived from a specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension =
requires(T* t) { detail::to_base_base_dimension(t); } && (!detail::is_specialization_of_base_dimension<T>);
namespace detail {
template<typename T>
inline constexpr bool is_dimension_one = false;
template<typename T>
inline constexpr bool is_power_of_dim = requires {
requires is_specialization_of_power<T> && (BaseDimension<typename T::factor> || is_dimension_one<typename T::factor>);
};
template<typename T>
inline constexpr bool is_per_of_dims = false;
template<typename... Ts>
inline constexpr bool is_per_of_dims<per<Ts...>> =
(... && (BaseDimension<Ts> || is_dimension_one<Ts> || is_power_of_dim<Ts>));
} // namespace detail
template<typename T>
concept DerivedDimensionExpr =
BaseDimension<T> || detail::is_dimension_one<T> || detail::is_power_of_dim<T> || detail::is_per_of_dims<T>;
template<DerivedDimensionExpr... Expr>
struct derived_dimension;
namespace detail {
template<typename... Expr>
void to_base_specialization_of_derived_dimension(const volatile derived_dimension<Expr...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_derived_dimension =
requires(T* t) { to_base_specialization_of_derived_dimension(t); };
/**
* @brief A concept matching all derived dimensions in the library.
*
* Satisfied by all dimension types either being a specialization of `derived_dimension`
* or derived from it (inheritance needed to properly handle `dimension_one`).
*/
template<typename T>
concept DerivedDimension = detail::is_derived_from_specialization_of_derived_dimension<T>;
} // namespace detail
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
template<typename T>
concept Dimension = BaseDimension<T> || detail::DerivedDimension<T>;
} // namespace mp_units

View File

@@ -36,7 +36,7 @@ UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA
namespace mp_units {
template<Reference auto R, RepresentationOf<R.quantity_spec.character> Rep>
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity;
// template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
@@ -57,12 +57,12 @@ class quantity;
*/
template<Quantity To, auto R, typename Rep>
requires(interconvertible(To::reference, R)) &&
((R.unit == To::unit && std::constructible_from<typename To::rep, Rep>) ||
(R.unit != To::unit)) // && scalable_with_<typename To::rep>))
((get_unit(R) == To::unit && std::constructible_from<typename To::rep, Rep>) ||
(get_unit(R) != To::unit)) // && scalable_with_<typename To::rep>))
// TODO how to constrain the second part here?
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
if constexpr (R.unit == To::unit) {
if constexpr (get_unit(R) == To::unit) {
// no scaling of the number needed
return To(static_cast<TYPENAME To::rep>(q.number())); // this is the only (and recommended) way to do
// a truncating conversion on a number, so we are
@@ -91,7 +91,8 @@ template<Quantity To, auto R, typename Rep>
return std::intmax_t{};
}());
constexpr Magnitude auto c_mag = detail::get_canonical_unit(R.unit).mag / detail::get_canonical_unit(To::unit).mag;
constexpr Magnitude auto c_mag =
detail::get_canonical_unit(get_unit(R)).mag / detail::get_canonical_unit(To::unit).mag;
constexpr Magnitude auto num = numerator(c_mag);
constexpr Magnitude auto den = denominator(c_mag);
constexpr Magnitude auto irr = c_mag * (den / num);
@@ -101,26 +102,6 @@ template<Quantity To, auto R, typename Rep>
}
}
/**
* @brief Explicit cast of a quantity
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
*
* This cast gets a target reference to cast to. For example:
*
* auto v = quantity_cast<isq::velocity[km / h]>(q);
*
* @tparam ToR a reference to use for a target quantity
*/
template<Reference auto ToR, auto R, typename Rep>
requires(interconvertible(ToR, R))
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
return quantity_cast<quantity<ToR, Rep>>(q);
}
/**
* @brief Explicit cast of a quantity
*
@@ -134,7 +115,7 @@ template<Reference auto ToR, auto R, typename Rep>
* @tparam ToQS a quantity specification to use for a target quantity
*/
template<QuantitySpec auto ToQS, auto R, typename Rep>
requires(interconvertible(ToQS, R.quantity_spec))
requires(interconvertible(ToQS, get_quantity_spec(R)))
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
constexpr reference<ToQS, quantity<R, Rep>::unit> r;
@@ -154,7 +135,7 @@ template<QuantitySpec auto ToQS, auto R, typename Rep>
* @tparam ToU a unit to use for a target quantity
*/
template<Unit auto ToU, auto R, typename Rep>
requires(interconvertible(ToU, R.unit))
requires(interconvertible(ToU, get_unit(R)))
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
constexpr reference<quantity<R, Rep>::quantity_spec, ToU> r;
@@ -174,7 +155,7 @@ template<Unit auto ToU, auto R, typename Rep>
* @tparam ToRep a representation type to use for a target quantity
*/
template<Representation ToRep, auto R, typename Rep>
requires RepresentationOf<ToRep, R.quantity_spec.character> && std::constructible_from<ToRep, Rep>
requires RepresentationOf<ToRep, get_quantity_spec(R).character> && std::constructible_from<ToRep, Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
return quantity_cast<quantity<R, ToRep>>(q);
@@ -189,7 +170,8 @@ template<Representation ToRep, auto R, typename Rep>
// * This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example:
// *
// * auto q1 = mp_units::quantity_point_cast<decltype(quantity_point{0_q_s})>(quantity_point{1_q_ms});
// * auto q1 = mp_units::quantity_point_cast<mp_units::isq::si::time<mp_units::isq::si::second>>(quantity_point{1_q_ms});
// * auto q1 =
// mp_units::quantity_point_cast<mp_units::isq::si::time<mp_units::isq::si::second>>(quantity_point{1_q_ms});
// * auto q1 = mp_units::quantity_point_cast<mp_units::isq::si::dim_acceleration>(quantity_point{200_q_Gal});
// * auto q1 = mp_units::quantity_point_cast<mp_units::isq::si::second>(quantity_point{1_q_ms});
// * auto q1 = mp_units::quantity_point_cast<int>(quantity_point{1_q_ms});

View File

@@ -22,100 +22,14 @@
#pragma once
#include <mp_units/bits/external/type_traits.h>
#include <mp_units/bits/quantity_spec_concepts.h>
#include <mp_units/bits/reference_concepts.h>
#include <mp_units/bits/representation_concepts.h>
#include <mp_units/customization_points.h>
#include <mp_units/dimension.h>
#include <mp_units/unit.h>
namespace mp_units {
/**
* @brief Quantity character
*
* Scalars, vectors and tensors are mathematical objects that can be used to
* denote certain physical quantities and their values. They are as such
* independent of the particular choice of a coordinate system, whereas
* each scalar component of a vector or a tensor and each component vector and
* component tensor depend on that choice.
*
* A scalar is a physical quantity that has magnitude but no direction.
*
* Vectors are physical quantities that possess both magnitude and direction
* and whose operations obey the axioms of a vector space.
*
* Tensors can be used to describe more general physical quantities.
* For example, the Cauchy stress tensor possess magnitude, direction,
* and orientation qualities.
*/
enum class quantity_character { scalar, vector, tensor };
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_derived_quantity_spec = false;
}
/**
* @brief Concept matching quantity specification types
*
* Satisfied by all `derived_quantity_spec` specializations.
*/
template<typename T>
concept DerivedQuantitySpec = detail::is_specialization_of_derived_quantity_spec<T>;
template<typename T>
concept QuantitySpec = NamedQuantitySpec<T> || DerivedQuantitySpec<T>;
template<QuantitySpec auto Q, Unit auto U>
struct reference;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_reference = false;
template<auto Q, auto U>
inline constexpr bool is_specialization_of_reference<reference<Q, U>> = true;
} // namespace detail
/**
* @brief A concept matching all references in the library.
*
* Satisfied by all specializations of @c reference.
*/
template<typename T>
concept Reference = detail::is_specialization_of_reference<T>;
template<typename T, typename U>
concept common_type_with_ = // exposition only
(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>>;
// TODO Fix it according to quantity_cast implementation
template<typename T>
concept scalable_ = // exposition only
castable_number_<T> || (requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
template<typename T>
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>) && std::regular<T> && scalable_<T>;
template<typename T, quantity_character Ch>
concept RepresentationOf = Representation<T> && ((Ch == quantity_character::scalar && is_scalar<T>) ||
(Ch == quantity_character::vector && is_vector<T>) ||
(Ch == quantity_character::tensor && is_tensor<T>));
template<Reference auto R, RepresentationOf<R.quantity_spec.character> Rep>
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity;
namespace detail {
@@ -125,19 +39,14 @@ void to_base_specialization_of_quantity(const volatile quantity<R, Rep>*);
} // namespace detail
/**
* @brief A concept matching all quantities in the library
*
* Satisfied by all types being a either specialization or derived from `quantity`
*/
template<typename T>
concept Quantity = requires(T* t) { detail::to_base_specialization_of_quantity(t); };
namespace detail {
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval bool is_kind_of(Q1, Q2)
{
return std::derived_from<Q1, Q2>;
}
} // namespace detail
/**
* @brief A concept matching all quantities with provided dimension or reference
*
@@ -145,9 +54,8 @@ template<QuantitySpec Q1, QuantitySpec Q2>
* the provided dimension/reference type.
*/
template<typename Q, auto V>
concept quantity_of = Quantity<Q> &&
((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && detail::is_kind_of(Q::quantity_spec, V)) ||
concept QuantityOf = Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && Q::quantity_spec == V) ||
(Reference<std::remove_const_t<decltype(V)>> && Q::reference == V));
/**
@@ -157,90 +65,27 @@ concept quantity_of = Quantity<Q> &&
* the provided dimension/reference type.
*/
template<typename Q, auto V>
concept weak_quantity_of = Quantity<Q> &&
((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
concept WeakQuantityOf =
Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && interconvertible(Q::quantity_spec, V)) ||
(Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension &&
Q::unit == V.unit));
(Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension && Q::unit == V.unit));
/**
* @brief A concept matching all external quantities like types
*
* Satisfied by all external types (not-defined in mp-units) that via a `quantity_like_traits` provide
* all quantity-specific information.
*/
template<typename T>
concept quantity_like = requires(T q) {
concept QuantityLike = requires(T q) {
quantity_like_traits<T>::reference;
typename quantity_like_traits<T>::rep;
requires Reference<std::remove_const_t<decltype(quantity_like_traits<T>::reference)>>;
requires RepresentationOf<typename quantity_like_traits<T>::rep,
quantity_like_traits<T>::reference.quantity_spec.character>;
get_quantity_spec(quantity_like_traits<T>::reference).character>;
{
quantity_like_traits<T>::number(q)
} -> std::convertible_to<typename quantity_like_traits<T>::rep>;
};
template<QuantitySpec auto Q>
struct absolute_point_origin {
static constexpr QuantitySpec auto quantity_spec = Q;
};
namespace detail {
template<typename T>
inline constexpr bool is_quantity_point = false;
template<auto Q>
void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin<Q>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_absolute_point_origin =
requires(T * t) { to_base_specialization_of_absolute_point_origin(t); };
} // namespace detail
template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>;
template<typename T>
concept point_origin = QuantityPoint<T> || detail::is_derived_from_specialization_of_absolute_point_origin<T>;
template<typename T, auto Q>
concept point_origin_for = point_origin<T> && QuantitySpec<std::remove_const_t<decltype(Q)>> &&
detail::is_kind_of(Q, T::quantity_spec);
template<Reference auto R, point_origin_for<R.quantity_spec> auto PO, RepresentationOf<R.quantity_spec.character> Rep>
class quantity_point;
namespace detail {
template<auto R, auto PO, typename Rep>
void to_base_specialization_of_quantity_point(const volatile quantity_point<R, PO, Rep>*);
template<typename T>
requires requires(T* t) { detail::to_base_specialization_of_quantity_point(t); }
inline constexpr bool is_quantity_point<T> = true;
} // namespace detail
template<typename QP, auto V>
concept quantity_point_of =
QuantityPoint<QP> &&
((Dimension<std::remove_const_t<decltype(V)>> && QP::dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && detail::is_kind_of(QP::quantity_spec, V)) ||
(Reference<std::remove_const_t<decltype(V)>> && QP::reference == V) ||
(point_origin<std::remove_const_t<decltype(V)>> &&
std::same_as<std::remove_const_t<decltype(QP::absolute_point_origin)>, std::remove_const_t<decltype(V)>>));
template<typename T>
concept quantity_point_like = requires(T q) {
quantity_point_like_traits<T>::reference;
quantity_point_like_traits<T>::point_origin;
typename quantity_point_like_traits<T>::rep;
requires Reference<std::remove_const_t<decltype(quantity_point_like_traits<T>::reference)>>;
requires point_origin<std::remove_const_t<decltype(quantity_point_like_traits<T>::point_origin)>>;
requires RepresentationOf<typename quantity_point_like_traits<T>::rep,
quantity_point_like_traits<T>::reference.quantity_spec.character>;
requires std::constructible_from<
typename quantity_point<quantity_point_like_traits<T>::reference, quantity_point_like_traits<T>::point_origin,
typename quantity_point_like_traits<T>::rep>::quantity_type,
decltype(quantity_point_like_traits<T>::relative(q))>;
};
} // namespace mp_units

View File

@@ -0,0 +1,124 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp_units/bits/quantity_spec_concepts.h>
#include <mp_units/bits/reference_concepts.h>
#include <mp_units/bits/representation_concepts.h>
#include <mp_units/customization_points.h>
namespace mp_units {
template<QuantitySpec auto Q>
struct absolute_point_origin;
namespace detail {
template<typename T>
inline constexpr bool is_quantity_point = false;
template<auto Q>
void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin<Q>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_absolute_point_origin =
requires(T* t) { to_base_specialization_of_absolute_point_origin(t); };
} // namespace detail
/**
* @brief A concept matching all quantity points in the library
*
* Satisfied by all types being a either specialization or derived from `quantity_point`
*/
template<typename T>
concept QuantityPoint = detail::is_quantity_point<T>;
/**
* @brief A concept matching all quantity point origins in the library
*
* Satisfied by either quantity points or by all types derived from `absolute_point_origin` class template.
*/
template<typename T>
concept PointOrigin = QuantityPoint<T> || detail::is_derived_from_specialization_of_absolute_point_origin<T>;
/**
* @brief A concept matching all quantity point origins for a specified quantity type in the library
*
* Satisfied by all quantity point origins that are defined using a provided quantity specification.
*/
template<typename T, auto Q>
concept PointOriginFor = PointOrigin<T> && QuantitySpec<std::remove_const_t<decltype(Q)>> && Q == T::quantity_spec;
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO,
RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity_point;
namespace detail {
template<auto R, auto PO, typename Rep>
void to_base_specialization_of_quantity_point(const volatile quantity_point<R, PO, Rep>*);
template<typename T>
requires requires(T* t) { detail::to_base_specialization_of_quantity_point(t); }
inline constexpr bool is_quantity_point<T> = true;
} // namespace detail
/**
* @brief A concept matching all quantity points with provided dimension or reference
*
* Satisfied by all quantity points with a dimension/reference being the instantiation derived from
* the provided dimension/reference type.
*/
template<typename QP, auto V>
concept QuantityPointOf =
QuantityPoint<QP> &&
((Dimension<std::remove_const_t<decltype(V)>> && QP::dimension == V) ||
(QuantitySpec<std::remove_const_t<decltype(V)>> && QP::quantity_spec == V) ||
(Reference<std::remove_const_t<decltype(V)>> && QP::reference == V) ||
(PointOrigin<std::remove_const_t<decltype(V)>> &&
std::same_as<std::remove_const_t<decltype(QP::absolute_point_origin)>, std::remove_const_t<decltype(V)>>));
/**
* @brief A concept matching all external quantity point like types
*
* Satisfied by all external types (not-defined in mp-units) that via a `quantity_point_like_traits` provide
* all quantity_point-specific information.
*/
template<typename T>
concept QuantityPointLike = requires(T q) {
quantity_point_like_traits<T>::reference;
quantity_point_like_traits<T>::point_origin;
typename quantity_point_like_traits<T>::rep;
requires Reference<std::remove_const_t<decltype(quantity_point_like_traits<T>::reference)>>;
requires PointOrigin<std::remove_const_t<decltype(quantity_point_like_traits<T>::point_origin)>>;
requires RepresentationOf<typename quantity_point_like_traits<T>::rep,
get_quantity_spec(quantity_point_like_traits<T>::reference).character>;
requires std::constructible_from<
typename quantity_point<quantity_point_like_traits<T>::reference, quantity_point_like_traits<T>::point_origin,
typename quantity_point_like_traits<T>::rep>::quantity_type,
decltype(quantity_point_like_traits<T>::relative(q))>;
};
} // namespace mp_units

View File

@@ -0,0 +1,137 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp_units/bits/dimension_concepts.h>
#include <mp_units/bits/expression_template.h>
namespace mp_units {
#ifdef __cpp_explicit_this_parameter
template<auto...>
#else
template<typename, auto...>
#endif
struct quantity_spec;
namespace detail {
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
#else
template<typename T, auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<T, Args...>*);
#endif
#ifdef __cpp_explicit_this_parameter
template<BaseDimension auto Dim, auto... Args>
template<auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Dim, Args...>*);
#else
template<typename Self, BaseDimension auto Dim, auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Self, Dim, Args...>*);
#endif
template<typename T>
inline constexpr bool is_specialization_of_quantity_spec = false;
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true;
#else
template<typename T, auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<T, Args...>> = true;
#endif
/**
* @brief Concept matching all named quantity specification types
*
* Satisfied by all types that derive from `quantity_spec`.
*/
template<typename T>
concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_quantity_spec(t); } &&
(!detail::is_specialization_of_quantity_spec<T>);
} // namespace detail
/**
* @brief Concept matching all named base quantity specification types
*
* Satisfied by all types that derive from `quantity_spec` taking a base dimension
* as a template parameter.
*/
template<typename T>
concept BaseQuantitySpec =
detail::NamedQuantitySpec<T> && requires(T* t) { detail::to_base_specialization_of_base_quantity_spec(t); };
namespace detail {
template<typename T>
inline constexpr bool is_dimensionless = false;
template<typename T>
inline constexpr bool is_power_of_quantity_spec = requires {
requires is_specialization_of_power<T> &&
(NamedQuantitySpec<typename T::factor> || is_dimensionless<typename T::factor>);
};
template<typename T>
inline constexpr bool is_per_of_quantity_specs = false;
template<typename... Ts>
inline constexpr bool is_per_of_quantity_specs<per<Ts...>> =
(... && (NamedQuantitySpec<Ts> || is_dimensionless<Ts> || is_power_of_quantity_spec<Ts>));
} // namespace detail
template<typename T>
concept DerivedQuantitySpecExpr = detail::NamedQuantitySpec<T> || detail::is_dimensionless<T> ||
detail::is_power_of_quantity_spec<T> || detail::is_per_of_quantity_specs<T>;
template<DerivedQuantitySpecExpr... Expr>
struct derived_quantity_spec;
namespace detail {
template<typename T>
inline constexpr bool is_derived_from_specialization_of_derived_quantity_spec = false;
template<typename... Args>
inline constexpr bool is_derived_from_specialization_of_derived_quantity_spec<derived_quantity_spec<Args...>> = true;
/**
* @brief Concept matching all derived quantity specification types
*
* Satisfied by all `derived_quantity_spec` specializations.
*/
template<typename T>
concept DerivedQuantitySpec = detail::is_derived_from_specialization_of_derived_quantity_spec<T>;
} // namespace detail
template<typename T>
concept QuantitySpec = detail::NamedQuantitySpec<T> || detail::DerivedQuantitySpec<T>;
} // namespace mp_units

View File

@@ -0,0 +1,51 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp_units/bits/quantity_spec_concepts.h>
#include <mp_units/bits/unit_concepts.h>
namespace mp_units {
template<QuantitySpec auto Q, Unit auto U>
struct reference;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_reference = false;
template<auto Q, auto U>
inline constexpr bool is_specialization_of_reference<reference<Q, U>> = true;
} // namespace detail
/**
* @brief A concept matching all references in the library.
*
* Satisfied by all specializations of @c reference.
*/
template<typename T>
concept Reference = AssociatedUnit<T> || detail::is_specialization_of_reference<T>;
} // namespace mp_units

View File

@@ -0,0 +1,80 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp_units/customization_points.h>
#include <concepts>
#include <cstdint>
#include <functional>
#include <type_traits>
namespace mp_units {
/**
* @brief Quantity character
*
* Scalars, vectors and tensors are mathematical objects that can be used to
* denote certain physical quantities and their values. They are as such
* independent of the particular choice of a coordinate system, whereas
* each scalar component of a vector or a tensor and each component vector and
* component tensor depend on that choice.
*
* A scalar is a physical quantity that has magnitude but no direction.
*
* Vectors are physical quantities that possess both magnitude and direction
* and whose operations obey the axioms of a vector space.
*
* Tensors can be used to describe more general physical quantities.
* For example, the Cauchy stress tensor possess magnitude, direction,
* and orientation qualities.
*/
enum class quantity_character { scalar, vector, tensor };
template<typename T, typename U>
concept common_type_with_ = // exposition only
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>>;
// TODO Fix it according to quantity_cast implementation
template<typename T>
concept scalable_ = // exposition only
castable_number_<T> || (requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
template<typename T>
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && scalable_<T>;
template<typename T, quantity_character Ch>
concept RepresentationOf = Representation<T> && ((Ch == quantity_character::scalar && is_scalar<T>) ||
(Ch == quantity_character::vector && is_vector<T>) ||
(Ch == quantity_character::tensor && is_tensor<T>));
} // namespace mp_units

View File

@@ -0,0 +1,159 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp_units/bits/expression_template.h>
#include <mp_units/bits/magnitude.h>
#include <mp_units/bits/symbol_text.h>
namespace mp_units {
namespace detail {
template<typename T>
inline constexpr bool is_unit = false;
} // namespace detail
/**
* @brief A concept matching all unit types in the library
*
* Satisfied by all unit types provided by the library.
*/
template<typename T>
concept Unit = detail::is_unit<T>;
template<Magnitude auto M, Unit U>
struct scaled_unit;
template<basic_symbol_text Symbol, auto...>
struct named_unit;
namespace detail {
template<basic_symbol_text Symbol, auto... Args>
void to_base_specialization_of_named_unit(const volatile named_unit<Symbol, Args...>*);
template<typename T>
inline constexpr bool is_specialization_of_named_unit = false;
template<basic_symbol_text Symbol, auto... Args>
inline constexpr bool is_specialization_of_named_unit<named_unit<Symbol, Args...>> = true;
} // namespace detail
/**
* @brief A concept matching all units with special names
*
* Satisfied by all unit types derived from the specialization of `named_unit`.
*/
template<typename T>
concept NamedUnit = Unit<T> && requires(T* t) { detail::to_base_specialization_of_named_unit(t); } &&
(!detail::is_specialization_of_named_unit<T>);
/**
* @brief Prevents assignment of a prefix to specific units
*
* By default all named units allow assigning a prefix for them. There are some notable exceptions like
* `hour` or `degree_Celsius`. For those a partial specialization with the value `false` should be
* provided.
*/
template<NamedUnit auto V>
inline constexpr bool unit_can_be_prefixed = true;
/**
* @brief A concept to be used to define prefixes for a unit
*/
template<typename T>
concept PrefixableUnit = NamedUnit<T> && unit_can_be_prefixed<T{}>;
namespace detail {
template<typename T>
inline constexpr bool is_power_of_unit =
requires { requires is_specialization_of_power<T> && Unit<typename T::factor>; };
template<typename T>
inline constexpr bool is_per_of_units = false;
template<typename... Ts>
inline constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_power_of_unit<Ts>));
} // namespace detail
template<typename T>
concept DerivedUnitExpr = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;
template<DerivedUnitExpr... Expr>
struct derived_unit;
namespace detail {
template<auto M, typename U>
void is_unit_impl(const scaled_unit<M, U>*);
template<basic_symbol_text Symbol, auto... Args>
void is_unit_impl(const named_unit<Symbol, Args...>*);
template<typename... Expr>
void is_unit_impl(const derived_unit<Expr...>*);
template<typename T>
requires requires(T* t) { is_unit_impl(t); }
inline constexpr bool is_unit<T> = true;
template<Unit U>
[[nodiscard]] consteval bool has_associated_quantity(U);
template<typename U, auto... Vs>
[[nodiscard]] consteval bool has_associated_quantity(power<U, Vs...>)
{
return has_associated_quantity(U{});
}
template<typename... Us>
[[nodiscard]] consteval bool has_associated_quantity(type_list<Us...>)
{
return (... && has_associated_quantity(Us{}));
}
template<Unit U>
[[nodiscard]] consteval bool has_associated_quantity(U)
{
if constexpr (requires { U::reference_unit; })
return has_associated_quantity(U::reference_unit);
else if constexpr (requires { typename U::_num_; })
return has_associated_quantity(typename U::_num_{}) && has_associated_quantity(typename U::_den_{});
else
return requires { U::base_quantity; };
}
} // namespace detail
/**
* @brief A concept matching all units that can be used as quantity references
*/
template<typename U>
concept AssociatedUnit = Unit<U> && detail::has_associated_quantity(U{});
} // namespace mp_units

View File

@@ -0,0 +1,31 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp_units/bits/dimension_concepts.h>
#include <mp_units/bits/quantity_concepts.h>
#include <mp_units/bits/quantity_point_concepts.h>
#include <mp_units/bits/quantity_spec_concepts.h>
#include <mp_units/bits/reference_concepts.h>
#include <mp_units/bits/representation_concepts.h>
#include <mp_units/bits/unit_concepts.h>

View File

@@ -22,6 +22,7 @@
#pragma once
#include <mp_units/bits/dimension_concepts.h>
#include <mp_units/bits/expression_template.h>
#include <mp_units/bits/external/type_traits.h>
#include <mp_units/bits/symbol_text.h>
@@ -63,55 +64,14 @@ struct base_dimension {
namespace detail {
template<basic_symbol_text Symbol>
void to_base_base_dimension(const volatile base_dimension<Symbol>*);
template<typename T>
inline constexpr bool is_specialization_of_base_dimension = false;
template<basic_symbol_text Symbol>
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true;
} // namespace detail
/**
* @brief A concept matching all named base dimensions in the library.
*
* Satisfied by all dimension types derived from a specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); } &&
(!detail::is_specialization_of_base_dimension<T>);
namespace detail {
template<BaseDimension Lhs, BaseDimension Rhs>
struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {};
template<typename T1, typename T2>
using type_list_of_base_dimension_less = expr_less<T1, T2, base_dimension_less>;
template<typename T>
inline constexpr bool is_dimension_one = false;
template<typename T>
inline constexpr bool is_power_of_dim = requires {
requires is_specialization_of_power<T> && (BaseDimension<typename T::factor> || is_dimension_one<typename T::factor>);
};
template<typename T>
inline constexpr bool is_per_of_dims = false;
template<typename... Ts>
inline constexpr bool is_per_of_dims<per<Ts...>> =
(... && (BaseDimension<Ts> || is_dimension_one<Ts> || is_power_of_dim<Ts>));
} // namespace detail
template<typename T>
concept DerivedDimensionExpr =
BaseDimension<T> || detail::is_dimension_one<T> || detail::is_power_of_dim<T> || detail::is_per_of_dims<T>;
/**
* @brief A dimension of a derived quantity
*
@@ -154,8 +114,8 @@ concept DerivedDimensionExpr =
* @note User should not instantiate this type! It is not exported from the C++ module. The library will
* instantiate this type automatically based on the dimensional arithmetic equation provided by the user.
*/
template<DerivedDimensionExpr... Ds>
struct derived_dimension : detail::expr_fractions<derived_dimension<>, Ds...> {};
template<DerivedDimensionExpr... Expr>
struct derived_dimension : detail::expr_fractions<derived_dimension<>, Expr...> {};
/**
* @brief Dimension one
@@ -172,33 +132,8 @@ namespace detail {
template<>
inline constexpr bool is_dimension_one<struct dimension_one> = true;
template<typename... Ds>
void to_base_specialization_of_derived_dimension(const volatile derived_dimension<Ds...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_derived_dimension =
requires(T * t) { to_base_specialization_of_derived_dimension(t); };
} // namespace detail
/**
* @brief A concept matching all derived dimensions in the library.
*
* Satisfied by all dimension types either being a specialization of `derived_dimension`
* or derived from it (inheritance needed to properly handle `dimension_one`).
*/
template<typename T>
concept DerivedDimension = detail::is_derived_from_specialization_of_derived_dimension<T>;
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
template<typename T>
concept Dimension = BaseDimension<T> || DerivedDimension<T>;
// Operators
template<Dimension Lhs, Dimension Rhs>

View File

@@ -23,8 +23,13 @@
#pragma once
#include <mp_units/bits/dimension_concepts.h>
#include <mp_units/bits/quantity_cast.h>
#include <mp_units/bits/quantity_concepts.h>
#include <mp_units/bits/quantity_spec_concepts.h>
#include <mp_units/bits/reference_concepts.h>
#include <mp_units/bits/representation_concepts.h>
#include <mp_units/bits/unit_concepts.h>
#include <mp_units/customization_points.h>
#include <mp_units/dimension.h>
#include <mp_units/quantity_spec.h>
@@ -40,32 +45,31 @@ namespace mp_units {
namespace detail {
template<typename T>
concept quantity_one = quantity_of<T, dimensionless[one]>;
concept QuantityOne = QuantityOf<T, dimensionless[one]>;
template<quantity_like Q>
template<QuantityLike Q>
using quantity_like_type = quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>;
} // namespace detail
template<typename T, typename Arg>
concept rep_safe_constructible_from_ = // exposition only
concept RepSafeConstructibleFrom = // exposition only
std::constructible_from<T, Arg> && (treat_as_floating_point<T> || !treat_as_floating_point<Arg>);
// QFrom ratio is an exact multiple of QTo
template<typename QFrom, typename QTo>
concept harmonic_ = // exposition only
concept Harmonic = // exposition only
Quantity<QFrom> && Quantity<QTo> &&
is_integral(detail::get_canonical_unit(QFrom::unit).mag / detail::get_canonical_unit(QTo::unit).mag);
is_integral(get_canonical_unit(QFrom::unit).mag / get_canonical_unit(QTo::unit).mag);
template<typename QFrom, typename QTo>
concept quantity_convertible_to_ = // exposition only
concept QuantityConvertibleTo = // exposition only
Quantity<QFrom> && Quantity<QTo> && requires(QFrom q) { quantity_cast<QTo>(q); } &&
(treat_as_floating_point<typename QTo::rep> ||
(!treat_as_floating_point<typename QFrom::rep> && harmonic_<QFrom, QTo>));
(!treat_as_floating_point<typename QFrom::rep> && Harmonic<QFrom, QTo>));
template<quantity_character Ch, typename Func, typename T, typename U>
concept invoke_result_of_ =
std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, Ch>;
concept InvokeResultOf = std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, Ch>;
} // namespace detail
/**
* @brief A quantity
@@ -76,16 +80,16 @@ concept invoke_result_of_ =
* @tparam R a reference of the quantity providing all information about quantity properties
* @tparam Rep a type to be used to represent values of a quantity
*/
template<Reference auto R, RepresentationOf<R.quantity_spec.character> Rep = double>
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep = double>
class quantity {
public:
Rep number_; // needs to be public for a structural type
// member types and values
static constexpr Reference auto reference = R;
static constexpr QuantitySpec auto quantity_spec = reference.quantity_spec;
static constexpr Dimension auto dimension = reference.dimension;
static constexpr Unit auto unit = reference.unit;
static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference);
static constexpr Dimension auto dimension = quantity_spec.dimension;
static constexpr Unit auto unit = get_unit(reference);
using rep = Rep;
// static member functions
@@ -119,18 +123,18 @@ public:
quantity(quantity&&) = default;
template<typename Value>
requires rep_safe_constructible_from_<rep, Value>
constexpr explicit(!detail::quantity_one<quantity>) quantity(Value&& v) : number_(std::forward<Value>(v))
requires detail::RepSafeConstructibleFrom<rep, Value>
constexpr explicit(!detail::QuantityOne<quantity>) quantity(Value&& v) : number_(std::forward<Value>(v))
{
}
template<quantity_convertible_to_<quantity> Q>
template<detail::QuantityConvertibleTo<quantity> Q>
constexpr explicit(false) quantity(const Q& q) : number_(quantity_cast<quantity>(q).number())
{
}
template<quantity_like Q>
requires quantity_convertible_to_<detail::quantity_like_type<Q>, quantity>
template<QuantityLike Q>
requires detail::QuantityConvertibleTo<detail::quantity_like_type<Q>, quantity>
constexpr explicit quantity(const Q& q) : quantity(detail::quantity_like_type<Q>(quantity_like_traits<Q>::number(q)))
{
}
@@ -151,7 +155,7 @@ public:
}
template<Unit U>
requires quantity_convertible_to_<quantity, quantity<::mp_units::reference<quantity_spec, U{}>{}, Rep>>
requires detail::QuantityConvertibleTo<quantity, quantity<::mp_units::reference<quantity_spec, U{}>{}, Rep>>
[[nodiscard]] constexpr quantity<::mp_units::reference<quantity_spec, U{}>{}, Rep> operator[](U) const
{
return quantity<::mp_units::reference<quantity_spec, U{}>{}, Rep>{*this};
@@ -252,7 +256,7 @@ public:
return *this;
}
template<detail::quantity_one Q>
template<detail::QuantityOne Q>
constexpr quantity& operator*=(const Q& rhs)
requires requires(rep a, const typename Q::rep b) {
{
@@ -277,7 +281,7 @@ public:
return *this;
}
template<detail::quantity_one Q>
template<detail::QuantityOne Q>
constexpr quantity& operator/=(const Q& rhs)
requires requires(rep a, const typename Q::rep b) {
{
@@ -303,7 +307,7 @@ public:
return *this;
}
template<detail::quantity_one Q>
template<detail::QuantityOne Q>
constexpr quantity& operator%=(const Q& rhs)
requires(!treat_as_floating_point<rep>) && (!treat_as_floating_point<typename Q::rep>) &&
requires(rep a, const typename Q::rep b) {
@@ -336,7 +340,7 @@ public:
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::mp_units::one;
requires invoke_result_of_<R.quantity_spec.character, std::plus<>, rep, Value>;
requires detail::InvokeResultOf<quantity_spec.character, std::plus<>, rep, Value>;
}
{
return ::mp_units::quantity(lhs.number() + rhs);
@@ -347,7 +351,7 @@ public:
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::mp_units::one;
requires invoke_result_of_<R.quantity_spec.character, std::plus<>, Value, rep>;
requires detail::InvokeResultOf<quantity_spec.character, std::plus<>, Value, rep>;
}
{
return ::mp_units::quantity(lhs + rhs.number());
@@ -358,7 +362,7 @@ public:
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::mp_units::one;
requires invoke_result_of_<R.quantity_spec.character, std::minus<>, rep, Value>;
requires detail::InvokeResultOf<quantity_spec.character, std::minus<>, rep, Value>;
}
{
return ::mp_units::quantity(lhs.number() - rhs);
@@ -369,14 +373,14 @@ public:
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::mp_units::one;
requires invoke_result_of_<R.quantity_spec.character, std::minus<>, Value, rep>;
requires detail::InvokeResultOf<quantity_spec.character, std::minus<>, Value, rep>;
}
{
return ::mp_units::quantity(lhs - rhs.number());
}
template<Representation Value>
requires invoke_result_of_<R.quantity_spec.character, std::multiplies<>, rep, const Value&>
requires detail::InvokeResultOf<quantity_spec.character, std::multiplies<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v)
{
using ret = quantity<R, std::invoke_result_t<std::multiplies<>, rep, Value>>;
@@ -384,7 +388,7 @@ public:
}
template<Representation Value>
requires invoke_result_of_<R.quantity_spec.character, std::multiplies<>, const Value&, rep>
requires detail::InvokeResultOf<quantity_spec.character, std::multiplies<>, const Value&, rep>
[[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q)
{
using ret = quantity<R, std::invoke_result_t<std::multiplies<>, Value, rep>>;
@@ -392,7 +396,7 @@ public:
}
template<typename Value>
requires(!Quantity<Value>) && invoke_result_of_<R.quantity_spec.character, std::divides<>, rep, const Value&>
requires(!Quantity<Value>) && detail::InvokeResultOf<quantity_spec.character, std::divides<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
@@ -401,7 +405,7 @@ public:
}
template<typename Value>
requires(!Quantity<Value>) && invoke_result_of_<R.quantity_spec.character, std::divides<>, const Value&, rep>
requires(!Quantity<Value>) && detail::InvokeResultOf<quantity_spec.character, std::divides<>, const Value&, rep>
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
return (dimensionless[::mp_units::one] / reference)(v / q.number());
@@ -409,7 +413,7 @@ public:
template<typename Value>
requires(!Quantity<Value>) && (!treat_as_floating_point<rep>) && (!treat_as_floating_point<Value>) &&
invoke_result_of_<R.quantity_spec.character, std::modulus<>, rep, const Value&>
detail::InvokeResultOf<quantity_spec.character, std::modulus<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
@@ -418,7 +422,7 @@ public:
}
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs)
requires(!treat_as_floating_point<rep>) && invoke_result_of_<R.quantity_spec.character, std::modulus<>, rep, rep>
requires(!treat_as_floating_point<rep>) && detail::InvokeResultOf<quantity_spec.character, std::modulus<>, rep, rep>
{
gsl_ExpectsAudit(rhs.number() != quantity_values<rep>::zero());
using ret = quantity<R, std::invoke_result_t<std::modulus<>, rep, rep>>;
@@ -450,13 +454,13 @@ template<auto R, RepresentationOf<R.quantity_spec.character> Rep>
explicit(false) quantity(Rep&&) -> quantity<R, Rep>;
#endif
template<quantity_like Q>
template<QuantityLike Q>
explicit quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>;
// non-member binary operators
template<Quantity Q1, Quantity Q2>
requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) &&
invoke_result_of_<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::plus<>,
detail::InvokeResultOf<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::plus<>,
typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
@@ -467,7 +471,7 @@ template<Quantity Q1, Quantity Q2>
template<Quantity Q1, Quantity Q2>
requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) &&
invoke_result_of_<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::minus<>,
detail::InvokeResultOf<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::minus<>,
typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
@@ -477,15 +481,15 @@ template<Quantity Q1, Quantity Q2>
}
template<Quantity Q1, Quantity Q2>
requires invoke_result_of_<(Q1::quantity_spec * Q2::quantity_spec).character, std::multiplies<>, typename Q1::rep,
typename Q2::rep>
requires detail::InvokeResultOf<(Q1::quantity_spec * Q2::quantity_spec).character, std::multiplies<>,
typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator*(const Q1& lhs, const Q2& rhs)
{
return (Q1::reference * Q2::reference)(lhs.number() * rhs.number());
}
template<Quantity Q1, Quantity Q2>
requires invoke_result_of_<(Q1::quantity_spec / Q2::quantity_spec).character, std::divides<>, typename Q1::rep,
requires detail::InvokeResultOf<(Q1::quantity_spec / Q2::quantity_spec).character, std::divides<>, typename Q1::rep,
typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator/(const Q1& lhs, const Q2& rhs)
{
@@ -495,8 +499,8 @@ template<Quantity Q1, Quantity Q2>
template<Quantity Q1, Quantity Q2>
requires(!treat_as_floating_point<typename Q1::rep>) && (!treat_as_floating_point<typename Q2::rep>) &&
(interconvertible(Q1::reference, Q2::reference) || quantity_of<Q2, dimensionless>) &&
invoke_result_of_<Q1::reference.character, std::modulus<>, typename Q1::rep, typename Q2::rep>
(interconvertible(Q1::reference, Q2::reference) || QuantityOf<Q2, dimensionless>) &&
detail::InvokeResultOf<Q1::reference.character, std::modulus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q2::rep>::zero());

View File

@@ -23,15 +23,21 @@
#pragma once
#include <mp_units/bits/quantity_point_concepts.h>
#include <mp_units/customization_points.h>
#include <mp_units/quantity.h>
#include <compare>
namespace mp_units {
template<QuantitySpec auto Q>
struct absolute_point_origin {
static constexpr QuantitySpec auto quantity_spec = Q;
};
namespace detail {
[[nodiscard]] consteval point_origin auto get_absolute_point_origin(point_origin auto po)
[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PointOrigin auto po)
{
if constexpr (requires { po.absolute_point_origin; })
return po.absolute_point_origin;
@@ -39,7 +45,7 @@ namespace detail {
return po;
}
template<quantity_point_like QP>
template<QuantityPointLike QP>
using quantity_point_like_type =
quantity_point<quantity_point_like_traits<QP>::reference, quantity_point_like_traits<QP>::point_origin,
typename quantity_point_like_traits<QP>::rep>;
@@ -55,17 +61,17 @@ using quantity_point_like_type =
* @tparam PO a type that represents the origin point from which the quantity point is measured from
* @tparam Rep a type to be used to represent values of a quantity point
*/
template<Reference auto R, point_origin_for<R.quantity_spec> auto PO = absolute_point_origin<R.quantity_spec>{},
RepresentationOf<R.quantity_spec.character> Rep = double>
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = absolute_point_origin<get_quantity_spec(R)>{},
RepresentationOf<get_quantity_spec(R).character> Rep = double>
class quantity_point {
public:
// member types and values
static constexpr Reference auto reference = R;
static constexpr QuantitySpec auto quantity_spec = reference.quantity_spec;
static constexpr Dimension auto dimension = reference.dimension;
static constexpr Unit auto unit = reference.unit;
static constexpr point_origin auto absolute_point_origin = detail::get_absolute_point_origin(PO);
static constexpr point_origin auto point_origin = PO;
static constexpr QuantitySpec auto quantity_spec = get_quantity_spec(reference);
static constexpr Dimension auto dimension = quantity_spec.dimension;
static constexpr Unit auto unit = get_unit(reference);
static constexpr PointOrigin auto absolute_point_origin = detail::get_absolute_point_origin(PO);
static constexpr PointOrigin auto point_origin = PO;
using rep = Rep;
using quantity_type = quantity<reference, Rep>;
@@ -95,13 +101,13 @@ public:
{
}
template<quantity_point_of<point_origin> QP2>
template<QuantityPointOf<point_origin> QP2>
requires std::convertible_to<typename QP2::quantity_type, quantity_type>
constexpr explicit(false) quantity_point(const QP2& qp) : q_(qp.relative())
{
}
template<quantity_point_like QP>
template<QuantityPointLike QP>
requires std::same_as<std::remove_const_t<decltype(quantity_point_like_traits<QP>::point_origin)>,
std::remove_const_t<decltype(point_origin)>> &&
std::convertible_to<typename detail::quantity_point_like_type<QP>::quantity_type, quantity_type>
@@ -195,21 +201,21 @@ public:
return quantity_point<q_type::reference, point_origin, typename q_type::rep>(q);
}
template<quantity_point_of<absolute_point_origin> QP>
template<QuantityPointOf<absolute_point_origin> QP>
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity_point& lhs, const QP& rhs)
requires requires(quantity_type q) { q - rhs.absolute(); }
{
return lhs.absolute() - rhs.absolute();
}
template<quantity_point_of<absolute_point_origin> QP>
template<QuantityPointOf<absolute_point_origin> QP>
requires std::three_way_comparable_with<quantity_type, typename QP::quantity_type>
[[nodiscard]] friend constexpr auto operator<=>(const quantity_point& lhs, const QP& rhs)
{
return lhs.relative() <=> rhs.relative();
}
template<quantity_point_of<absolute_point_origin> QP>
template<QuantityPointOf<absolute_point_origin> QP>
requires std::equality_comparable_with<quantity_type, typename QP::quantity_type>
[[nodiscard]] friend constexpr bool operator==(const quantity_point& lhs, const QP& rhs)
{
@@ -224,12 +230,12 @@ explicit quantity_point(Rep) -> quantity_point<dimensionless[one], absolute_poin
template<Quantity Q>
explicit quantity_point(Q) -> quantity_point<Q::reference, absolute_point_origin<Q::quantity_spec>{}, typename Q::rep>;
template<quantity_like Q>
template<QuantityLike Q>
explicit quantity_point(Q)
-> quantity_point<quantity_like_traits<Q>::reference, absolute_point_origin<quantity_like_traits<Q>::quantity_spec>{},
typename quantity_like_traits<Q>::rep>;
template<quantity_point_like QP>
template<QuantityPointLike QP>
explicit quantity_point(QP)
-> quantity_point<quantity_point_like_traits<QP>::reference, quantity_point_like_traits<QP>::point_origin,
typename quantity_point_like_traits<QP>::rep>;

View File

@@ -23,10 +23,65 @@
#pragma once
#include <mp_units/bits/quantity_concepts.h>
#include <mp_units/unit.h>
#include <mp_units/bits/reference_concepts.h>
#include <mp_units/bits/representation_concepts.h>
#include <mp_units/quantity_spec.h>
namespace mp_units {
namespace detail {
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity(U);
template<typename U, auto... Vs>
[[nodiscard]] consteval auto get_associated_quantity(power<U, Vs...>)
{
return get_associated_quantity(U{});
}
template<typename... Us>
[[nodiscard]] consteval auto get_associated_quantity(type_list<Us...>)
{
return (dimensionless * ... * get_associated_quantity(Us{}));
}
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity(U)
{
if constexpr (requires { U::reference_unit; })
return get_associated_quantity(U::reference_unit);
else if constexpr (requires { typename U::_num_; })
return get_associated_quantity(typename U::_num_{}) / get_associated_quantity(typename U::_den_{});
else if constexpr (requires { U::base_quantity; })
return U::base_quantity;
}
} // namespace detail
[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u)
{
return detail::get_associated_quantity(u);
}
template<auto Q, auto U>
[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(reference<Q, U>)
{
return Q;
}
[[nodiscard]] consteval Unit auto get_unit(AssociatedUnit auto u) { return u; }
template<auto Q, auto U>
[[nodiscard]] consteval Unit auto get_unit(reference<Q, U>)
{
return U;
}
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
class quantity;
/**
* @brief Quantity reference type
*
@@ -39,7 +94,7 @@ namespace mp_units {
*
* @code{.cpp}
* Reference auto kmph = isq::speed[km / h];
* quantity_of<isq::speed[km / h]> auto speed = 90 * kmph;
* QuantityOf<isq::speed[km / h]> auto speed = 90 * kmph;
* @endcode
*
* The following syntaxes are not allowed:
@@ -47,10 +102,6 @@ namespace mp_units {
*/
template<QuantitySpec auto Q, Unit auto U>
struct reference {
static constexpr QuantitySpec auto quantity_spec = Q;
static constexpr Dimension auto dimension = Q.dimension;
static constexpr Unit auto unit = U;
template<RepresentationOf<Q.character> Rep>
// TODO can we somehow return an explicit quantity type here?
[[nodiscard]] constexpr std::same_as<quantity<reference{}, Rep>> auto operator()(Rep&& value) const
@@ -59,54 +110,103 @@ struct reference {
}
};
// Reference
template<auto Q1, auto U1, auto Q2, auto U2>
[[nodiscard]] consteval bool operator==(reference<Q1, U1>, reference<Q2, U2>)
{
return Q1 == Q2 && U1 == U2;
}
template<Reference R1, Reference R2>
[[nodiscard]] consteval reference<R1::quantity_spec * R2::quantity_spec, R1::unit * R2::unit> operator*(R1, R2)
template<auto Q1, auto U1, AssociatedUnit U2>
[[nodiscard]] consteval bool operator==(reference<Q1, U1>, U2 u2)
{
return Q1 == get_quantity_spec(u2) && U1 == u2;
}
template<auto Q1, auto U1, auto Q2, auto U2>
[[nodiscard]] consteval reference<Q1 * Q2, U1 * U2> operator*(reference<Q1, U1>, reference<Q2, U2>)
{
return {};
}
template<Reference R1, Reference R2>
[[nodiscard]] consteval reference<R1::quantity_spec / R2::quantity_spec, R1::unit / R2::unit> operator/(R1, R2)
template<auto Q1, auto U1, AssociatedUnit U2>
[[nodiscard]] consteval reference<Q1 * get_quantity_spec(U2{}), U1* U2{}> operator*(reference<Q1, U1>, U2)
{
return {};
}
// TODO remove when all code is refactored to a new syntax
template<Representation Rep, Reference R>
template<AssociatedUnit U1, auto Q2, auto U2>
[[nodiscard]] consteval reference<get_quantity_spec(U1{}) * Q2, U1{} * U2> operator*(U1, reference<Q2, U2>)
{
return {};
}
template<auto Q1, auto U1, auto Q2, auto U2>
[[nodiscard]] consteval reference<Q1 / Q2, U1 / U2> operator/(reference<Q1, U1>, reference<Q2, U2>)
{
return {};
}
template<auto Q1, auto U1, AssociatedUnit U2>
[[nodiscard]] consteval reference<Q1 / get_quantity_spec(U2{}), U1 / U2{}> operator/(reference<Q1, U1>, U2)
{
return {};
}
template<AssociatedUnit U1, auto Q2, auto U2>
[[nodiscard]] consteval reference<get_quantity_spec(U1{}) / Q2, U1{} / U2> operator/(U1, reference<Q2, U2>)
{
return {};
}
template<Reference R, RepresentationOf<get_quantity_spec(R{}).character> Rep>
[[nodiscard]] constexpr quantity<R{}, Rep> operator*(const Rep& lhs, R)
{
return quantity<R{}, Rep>(lhs);
}
// TODO remove when all code is refactored to a new syntax
void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete;
template<Reference R1, Reference R2>
[[nodiscard]] consteval bool operator==(R1, R2)
template<auto Q1, auto U1, auto Q2, auto U2>
[[nodiscard]] consteval bool interconvertible(reference<Q1, U1>, reference<Q2, U2>)
{
return R1::quantity_spec == R2::quantity_spec && R1::unit == R2::unit;
return interconvertible(Q1, Q2) && interconvertible(U1, U2);
}
template<Reference R1, Reference R2>
[[nodiscard]] consteval bool interconvertible(R1, R2)
template<auto Q1, auto U1, AssociatedUnit U2>
[[nodiscard]] consteval bool interconvertible(reference<Q1, U1>, U2 u2)
{
return interconvertible(R1::quantity_spec, R2::quantity_spec) && interconvertible(R1::unit, R2::unit);
return interconvertible(Q1, get_quantity_spec(u2)) && interconvertible(U1, u2);
}
template<AssociatedUnit U1, auto Q2, auto U2>
[[nodiscard]] consteval bool interconvertible(U1 u1, reference<Q2, U2> r2)
{
return interconvertible(r2, u1);
}
[[nodiscard]] consteval auto common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2,
AssociatedUnit auto... rest)
requires requires {
{
common_unit(u1, u2, rest...)
} -> AssociatedUnit;
}
{
return common_unit(u1, u2, rest...);
}
[[nodiscard]] consteval auto common_reference(Reference auto r1, Reference auto r2, Reference auto... rest)
requires requires {
{
common_quantity_spec(r1.quantity_spec, r2.quantity_spec, rest.quantity_spec...)
common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...)
} -> QuantitySpec;
{
common_unit(r1.unit, r2.unit, rest.unit...)
common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...)
} -> Unit;
}
{
return reference<common_quantity_spec(r1.quantity_spec, r2.quantity_spec, rest.quantity_spec...),
common_unit(r1.unit, r2.unit, rest.unit...)>{};
return reference<common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...),
common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...)>{};
}
} // namespace mp_units

View File

@@ -56,7 +56,7 @@ namespace mp_units {
* @tparam CoU coherent unit for a quantity in this system
*/
template<QuantitySpec auto Q, Unit auto CoU>
requires(!detail::associated_unit<std::remove_const_t<decltype(CoU)>>) || (CoU == one)
requires(!AssociatedUnit<std::remove_const_t<decltype(CoU)>>) || (CoU == one)
struct system_reference {
static constexpr auto quantity_spec = Q;
static constexpr auto coherent_unit = CoU;

View File

@@ -28,84 +28,17 @@
#include <mp_units/bits/external/type_name.h>
#include <mp_units/bits/external/type_traits.h>
#include <mp_units/bits/magnitude.h>
#include <mp_units/bits/quantity_spec_concepts.h>
#include <mp_units/bits/ratio.h>
#include <mp_units/bits/symbol_text.h>
#include <mp_units/bits/text_tools.h>
#include <mp_units/bits/unit_concepts.h>
#include <mp_units/dimension.h>
#include <iterator>
#include <string>
namespace mp_units {
#ifdef __cpp_explicit_this_parameter
template<auto...>
#else
template<typename, auto...>
#endif
struct quantity_spec;
namespace detail {
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
#else
template<typename T, auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<T, Args...>*);
#endif
#ifdef __cpp_explicit_this_parameter
template<BaseDimension auto Dim, auto... Args>
template<auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Dim, Args...>*);
#else
template<typename Self, BaseDimension auto Dim, auto... Args>
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Self, Dim, Args...>*);
#endif
template<typename T>
inline constexpr bool is_specialization_of_quantity_spec = false;
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true;
#else
template<typename T, auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<T, Args...>> = true;
#endif
} // namespace detail
/**
* @brief Concept matching quantity specification types
*
* Satisfied by all types that derive from `quantity_spec`.
*/
template<typename T>
concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_quantity_spec(t); } &&
(!detail::is_specialization_of_quantity_spec<T>);
template<typename T>
concept BaseQuantitySpec =
NamedQuantitySpec<T> && requires(T* t) { detail::to_base_specialization_of_base_quantity_spec(t); };
namespace detail {
template<typename T>
inline constexpr bool is_unit = false;
} // namespace detail
/**
* @brief A concept matching all unit types in the library
*
* Satisfied by all unit types provided by the library.
*/
template<typename T>
concept Unit = detail::is_unit<T>;
/**
* @brief Unit being a scaled version of another unit
*
@@ -213,29 +146,6 @@ struct named_unit<Symbol, U> : std::remove_const_t<decltype(U)> {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
};
namespace detail {
template<basic_symbol_text Symbol, auto... Args>
void to_base_specialization_of_named_unit(const volatile named_unit<Symbol, Args...>*);
template<typename T>
inline constexpr bool is_specialization_of_named_unit = false;
template<basic_symbol_text Symbol, auto... Args>
inline constexpr bool is_specialization_of_named_unit<named_unit<Symbol, Args...>> = true;
} // namespace detail
/**
* @brief A concept matching all units with special names
*
* Satisfied by all unit types derived from the specialization of `named_unit`.
*/
template<typename T>
concept NamedUnit = Unit<T> && requires(T* t) { detail::to_base_specialization_of_named_unit(t); } &&
(!detail::is_specialization_of_named_unit<T>);
/**
* @brief A unit of a physical constant
*
@@ -282,24 +192,6 @@ inline constexpr bool is_derived_from_specialization_of_constant_unit =
} // namespace detail
/**
* @brief Prevents assignment of a prefix to specific units
*
* By default all named units allow assigning a prefix for them. There are some notable exceptions like
* `hour` or `degree_Celsius`. For those a partial specialization with the value `false` should be
* provided.
*/
template<NamedUnit auto V>
inline constexpr bool unit_can_be_prefixed = true;
/**
* @brief A concept to be used to define prefixes for a unit
*/
template<typename T>
concept PrefixableUnit = NamedUnit<T> && unit_can_be_prefixed<T{}>;
/**
* @brief A prefixed unit
*
@@ -328,23 +220,6 @@ struct prefixed_unit : std::remove_const_t<decltype(M * U)> {
static constexpr auto symbol = Symbol + U.symbol;
};
namespace detail {
template<typename T>
inline constexpr bool is_power_of_unit =
requires { requires is_specialization_of_power<T> && Unit<typename T::factor>; };
template<typename T>
inline constexpr bool is_per_of_units = false;
template<typename... Ts>
inline constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_power_of_unit<Ts>));
} // namespace detail
template<typename T>
concept DerivedUnitExpr = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;
/**
* @brief Measurement unit for a derived quantity
*
@@ -390,8 +265,8 @@ concept DerivedUnitExpr = Unit<T> || detail::is_power_of_unit<T> || detail::is_p
* @note User should not instantiate this type! It is not exported from the C++ module. The library will
* instantiate this type automatically based on the unit arithmetic equation provided by the user.
*/
template<DerivedUnitExpr... Us>
struct derived_unit : detail::expr_fractions<derived_unit<>, Us...> {};
template<DerivedUnitExpr... Expr>
struct derived_unit : detail::expr_fractions<derived_unit<>, Expr...> {};
/**
* @brief Unit one
@@ -404,19 +279,6 @@ inline constexpr struct one : derived_unit<> {} one;
namespace detail {
template<auto M, typename U>
void is_unit_impl(const volatile scaled_unit<M, U>*);
template<basic_symbol_text Symbol, auto... Args>
void is_unit_impl(const volatile named_unit<Symbol, Args...>*);
template<typename... Us>
void is_unit_impl(const volatile derived_unit<Us...>*);
template<typename T>
requires requires(T* t) { is_unit_impl(t); }
inline constexpr bool is_unit<T> = true;
/**
* @brief A canonical representation of a unit
*
@@ -452,8 +314,8 @@ template<Unit T, basic_symbol_text Symbol, Unit auto U>
template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&);
template<Unit T, typename... Us>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Us...>&);
template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Expr...>&);
template<Unit T, auto M, typename U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit<M, U>&)
@@ -511,16 +373,39 @@ template<typename... Us>
return canonical_unit{mag, u};
}
template<Unit T, typename... Us>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Us...>&)
template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Expr...>&)
{
auto num = get_canonical_unit_impl(typename derived_unit<Us...>::_num_{});
auto den = get_canonical_unit_impl(typename derived_unit<Us...>::_den_{});
auto num = get_canonical_unit_impl(typename derived_unit<Expr...>::_num_{});
auto den = get_canonical_unit_impl(typename derived_unit<Expr...>::_den_{});
return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit};
}
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return get_canonical_unit_impl(u, u); }
template<Unit U>
requires requires { U::base_quantity.dimension; }
using to_base_dimension = std::remove_const_t<decltype(U::base_quantity.dimension)>;
template<Unit U>
requires requires { U::base_quantity.dimension; }
[[nodiscard]] consteval Dimension auto get_dimension_for_impl(U)
{
return U::base_quantity.dimension;
}
template<typename... Expr>
requires expr_projectable<derived_unit<Expr...>, to_base_dimension>
[[nodiscard]] consteval Dimension auto get_dimension_for_impl(const derived_unit<Expr...>& u)
{
return expr_map<to_base_dimension, derived_dimension, struct dimension_one, type_list_of_base_dimension_less>(u);
}
[[nodiscard]] consteval Dimension auto get_dimension_for(AssociatedUnit auto u)
{
return get_dimension_for_impl(get_canonical_unit(u).reference_unit);
}
template<Unit Lhs, Unit Rhs>
[[nodiscard]] consteval bool less(Lhs, Rhs)
{
@@ -640,12 +525,13 @@ template<typename... Us1, typename... Us2>
return (... && same_canonical_reference_unit(Us1{}, Us2{}));
}
template<typename... Us1, typename... Us2>
[[nodiscard]] consteval bool same_canonical_reference_unit(const derived_unit<Us1...>&, const derived_unit<Us2...>&)
template<typename... Expr1, typename... Expr2>
[[nodiscard]] consteval bool same_canonical_reference_unit(const derived_unit<Expr1...>&, const derived_unit<Expr2...>&)
{
return same_canonical_reference_unit(typename derived_unit<Us1...>::_num_{},
typename derived_unit<Us2...>::_num_{}) &&
same_canonical_reference_unit(typename derived_unit<Us1...>::_den_{}, typename derived_unit<Us2...>::_den_{});
return same_canonical_reference_unit(typename derived_unit<Expr1...>::_num_{},
typename derived_unit<Expr2...>::_num_{}) &&
same_canonical_reference_unit(typename derived_unit<Expr1...>::_den_{},
typename derived_unit<Expr2...>::_den_{});
}
} // namespace detail
@@ -898,11 +784,11 @@ constexpr Out unit_symbol_impl(Out out, const type_list<Nums...>& nums, const ty
}
}
template<typename CharT, std::output_iterator<CharT> Out, typename... Us>
constexpr Out unit_symbol_impl(Out out, const derived_unit<Us...>&, unit_symbol_formatting fmt, bool negative_power)
template<typename CharT, std::output_iterator<CharT> Out, typename... Expr>
constexpr Out unit_symbol_impl(Out out, const derived_unit<Expr...>&, unit_symbol_formatting fmt, bool negative_power)
{
gsl_Expects(negative_power == false);
return unit_symbol_impl<CharT>(out, typename derived_unit<Us...>::_num_{}, typename derived_unit<Us...>::_den_{},
return unit_symbol_impl<CharT>(out, typename derived_unit<Expr...>::_num_{}, typename derived_unit<Expr...>::_den_{},
fmt);
}

View File

@@ -23,6 +23,7 @@
#pragma once
#include <mp_units/customization_points.h>
#include <mp_units/quantity_point.h>
#include <mp_units/systems/isq/space_and_time.h>
#include <mp_units/systems/si/prefixes.h>
#include <mp_units/systems/si/units.h>
@@ -80,7 +81,7 @@ struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::durati
}
};
template<quantity_of<isq::time> Q>
template<QuantityOf<isq::time> Q>
[[nodiscard]] constexpr auto to_chrono_duration(const Q& q)
{
constexpr auto canonical = detail::get_canonical_unit(Q::unit);
@@ -88,7 +89,7 @@ template<quantity_of<isq::time> Q>
return std::chrono::duration<typename Q::rep, std::ratio<r.num, r.den>>{q.number()};
}
template<quantity_point_of<isq::time> QP>
template<QuantityPointOf<isq::time> QP>
requires is_specialization_of<std::remove_const_t<decltype(QP::absolute_point_origin)>, chrono_point_origin>
[[nodiscard]] constexpr auto to_chrono_time_point(const QP& qp)
{

View File

@@ -109,7 +109,7 @@ template<Quantity Q>
* @param q Quantity being the base of the operation
* @return Quantity The value of the same quantity type
*/
template<quantity_of<dimension_one> Q>
template<QuantityOf<dimension_one> Q>
[[nodiscard]] inline Q exp(const Q& q)
requires requires { exp(q.number()); } || requires { std::exp(q.number()); }
{
@@ -153,12 +153,12 @@ template<Representation Rep, Reference R>
* @return Quantity The rounded quantity with unit type To
*/
template<Unit auto To, auto R, typename Rep>
[[nodiscard]] constexpr quantity<reference<R.quantity_spec, To>{}, Rep> floor(const quantity<R, Rep>& q) noexcept
[[nodiscard]] constexpr quantity<reference<get_quantity_spec(R), To>{}, Rep> floor(const quantity<R, Rep>& q) noexcept
requires((!treat_as_floating_point<Rep>) || requires { floor(q.number()); } ||
requires { std::floor(q.number()); }) &&
(To == R.unit || requires {
(To == get_unit(R) || requires {
::mp_units::quantity_cast<To>(q);
quantity<reference<R.quantity_spec, To>{}, Rep>::one();
quantity<reference<get_quantity_spec(R), To>{}, Rep>::one();
})
{
const auto handle_signed_results = [&]<typename T>(const T& res) {
@@ -169,14 +169,14 @@ template<Unit auto To, auto R, typename Rep>
};
if constexpr (treat_as_floating_point<Rep>) {
using std::floor;
if constexpr (To == R.unit) {
return quantity<reference<R.quantity_spec, To>{}, Rep>(floor(q.number()));
if constexpr (To == get_unit(R)) {
return quantity<reference<get_quantity_spec(R), To>{}, Rep>(floor(q.number()));
} else {
return handle_signed_results(
quantity<reference<R.quantity_spec, To>{}, Rep>(floor(quantity_cast<To>(q).number())));
quantity<reference<get_quantity_spec(R), To>{}, Rep>(floor(quantity_cast<To>(q).number())));
}
} else {
if constexpr (To == R.unit) {
if constexpr (To == get_unit(R)) {
return quantity_cast<To>(q);
} else {
return handle_signed_results(quantity_cast<To>(q));
@@ -191,11 +191,11 @@ template<Unit auto To, auto R, typename Rep>
* @return Quantity The rounded quantity with unit type To
*/
template<Unit auto To, auto R, typename Rep>
[[nodiscard]] constexpr quantity<reference<R.quantity_spec, To>{}, Rep> ceil(const quantity<R, Rep>& q) noexcept
[[nodiscard]] constexpr quantity<reference<get_quantity_spec(R), To>{}, Rep> ceil(const quantity<R, Rep>& q) noexcept
requires((!treat_as_floating_point<Rep>) || requires { ceil(q.number()); } || requires { std::ceil(q.number()); }) &&
(To == R.unit || requires {
(To == get_unit(R) || requires {
::mp_units::quantity_cast<To>(q);
quantity<reference<R.quantity_spec, To>{}, Rep>::one();
quantity<reference<get_quantity_spec(R), To>{}, Rep>::one();
})
{
const auto handle_signed_results = [&]<typename T>(const T& res) {
@@ -206,14 +206,14 @@ template<Unit auto To, auto R, typename Rep>
};
if constexpr (treat_as_floating_point<Rep>) {
using std::ceil;
if constexpr (To == R.unit) {
return quantity<reference<R.quantity_spec, To>{}, Rep>(ceil(q.number()));
if constexpr (To == get_unit(R)) {
return quantity<reference<get_quantity_spec(R), To>{}, Rep>(ceil(q.number()));
} else {
return handle_signed_results(
quantity<reference<R.quantity_spec, To>{}, Rep>(ceil(quantity_cast<To>(q).number())));
quantity<reference<get_quantity_spec(R), To>{}, Rep>(ceil(quantity_cast<To>(q).number())));
}
} else {
if constexpr (To == R.unit) {
if constexpr (To == get_unit(R)) {
return quantity_cast<To>(q);
} else {
return handle_signed_results(quantity_cast<To>(q));
@@ -230,18 +230,18 @@ template<Unit auto To, auto R, typename Rep>
* @return Quantity The rounded quantity with unit type To
*/
template<Unit auto To, auto R, typename Rep>
[[nodiscard]] constexpr quantity<reference<R.quantity_spec, To>{}, Rep> round(const quantity<R, Rep>& q) noexcept
[[nodiscard]] constexpr quantity<reference<get_quantity_spec(R), To>{}, Rep> round(const quantity<R, Rep>& q) noexcept
requires((!treat_as_floating_point<Rep>) || requires { round(q.number()); } ||
requires { std::round(q.number()); }) &&
(To == R.unit || requires {
(To == get_unit(R) || requires {
::mp_units::floor<To>(q);
quantity<reference<R.quantity_spec, To>{}, Rep>::one();
quantity<reference<get_quantity_spec(R), To>{}, Rep>::one();
})
{
if constexpr (To == R.unit) {
if constexpr (To == get_unit(R)) {
if constexpr (treat_as_floating_point<Rep>) {
using std::round;
return quantity<reference<R.quantity_spec, To>{}, Rep>(round(q.number()));
return quantity<reference<get_quantity_spec(R), To>{}, Rep>(round(q.number()));
} else {
return quantity_cast<To>(q);
}
@@ -268,7 +268,7 @@ template<Unit auto To, auto R, typename Rep>
* without undue overflow or underflow at intermediate stages of the computation
*/
template<Quantity Q1, Quantity Q2>
[[nodiscard]] inline quantity_of<common_reference(Q1::reference, Q2::reference)> auto hypot(const Q1& x,
[[nodiscard]] inline QuantityOf<common_reference(Q1::reference, Q2::reference)> auto hypot(const Q1& x,
const Q2& y) noexcept
requires requires { common_reference(Q1::reference, Q2::reference); } &&
(
@@ -284,7 +284,7 @@ template<Quantity Q1, Quantity Q2>
* without undue overflow or underflow at intermediate stages of the computation
*/
template<Quantity Q1, Quantity Q2, Quantity Q3>
[[nodiscard]] inline quantity_of<common_reference(Q1::reference, Q2::reference, Q3::reference)> auto hypot(
[[nodiscard]] inline QuantityOf<common_reference(Q1::reference, Q2::reference, Q3::reference)> auto hypot(
const Q1& x, const Q2& y, const Q3& z) noexcept
requires requires { common_reference(Q1::reference, Q2::reference, Q3::reference); } &&
(
@@ -299,54 +299,54 @@ template<Quantity Q1, Quantity Q2, Quantity Q3>
namespace isq {
template<weak_quantity_of<angular_measure> Q>
template<WeakQuantityOf<angular_measure> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<dimensionless[one]> auto sin(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless[one]> auto sin(const Q& q) noexcept
requires requires { sin(q.number()); } || requires { std::sin(q.number()); }
{
using std::sin;
return quantity{sin(q[si::radian].number())};
}
template<weak_quantity_of<angular_measure> Q>
template<WeakQuantityOf<angular_measure> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<dimensionless[one]> auto cos(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless[one]> auto cos(const Q& q) noexcept
requires requires { cos(q.number()); } || requires { std::cos(q.number()); }
{
using std::cos;
return quantity{cos(q[si::radian].number())};
}
template<weak_quantity_of<angular_measure> Q>
template<WeakQuantityOf<angular_measure> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<dimensionless[one]> auto tan(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless[one]> auto tan(const Q& q) noexcept
requires requires { tan(q.number()); } || requires { std::tan(q.number()); }
{
using std::tan;
return quantity{tan(q[si::radian].number())};
}
template<quantity_of<dimension_one> Q>
template<QuantityOf<dimension_one> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<angular_measure[si::radian]> auto asin(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<angular_measure[si::radian]> auto asin(const Q& q) noexcept
requires requires { asin(q.number()); } || requires { std::asin(q.number()); }
{
using std::asin;
return asin(quantity_cast<one>(q).number()) * angular_measure[si::radian];
}
template<quantity_of<dimension_one> Q>
template<QuantityOf<dimension_one> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<angular_measure[si::radian]> auto acos(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<angular_measure[si::radian]> auto acos(const Q& q) noexcept
requires requires { acos(q.number()); } || requires { std::acos(q.number()); }
{
using std::acos;
return acos(quantity_cast<one>(q).number()) * angular_measure[si::radian];
}
template<quantity_of<dimension_one> Q>
template<QuantityOf<dimension_one> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<angular_measure[si::radian]> auto atan(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<angular_measure[si::radian]> auto atan(const Q& q) noexcept
requires requires { atan(q.number()); } || requires { std::atan(q.number()); }
{
using std::atan;
@@ -357,55 +357,55 @@ template<quantity_of<dimension_one> Q>
namespace angular {
// TODO cannot use `weak_quantity_of<angle>` as it is not interconvertible with `isq_angle::angular_measure`
template<quantity_of<dim_angle> Q>
// TODO cannot use `WeakQuantityOf<angle>` as it is not interconvertible with `isq_angle::angular_measure`
template<QuantityOf<dim_angle> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<dimensionless[one]> auto sin(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless[one]> auto sin(const Q& q) noexcept
requires requires { sin(q.number()); } || requires { std::sin(q.number()); }
{
using std::sin;
return quantity{sin(q[radian].number())};
}
template<quantity_of<dim_angle> Q>
template<QuantityOf<dim_angle> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<dimensionless[one]> auto cos(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless[one]> auto cos(const Q& q) noexcept
requires requires { cos(q.number()); } || requires { std::cos(q.number()); }
{
using std::cos;
return quantity{cos(q[radian].number())};
}
template<quantity_of<dim_angle> Q>
template<QuantityOf<dim_angle> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<dimensionless[one]> auto tan(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless[one]> auto tan(const Q& q) noexcept
requires requires { tan(q.number()); } || requires { std::tan(q.number()); }
{
using std::tan;
return quantity{tan(q[radian].number())};
}
template<quantity_of<dimension_one> Q>
template<QuantityOf<dimension_one> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<angle[radian]> auto asin(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<angle[radian]> auto asin(const Q& q) noexcept
requires requires { asin(q.number()); } || requires { std::asin(q.number()); }
{
using std::asin;
return asin(quantity_cast<one>(q).number()) * angle[radian];
}
template<quantity_of<dimension_one> Q>
template<QuantityOf<dimension_one> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<angle[radian]> auto acos(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<angle[radian]> auto acos(const Q& q) noexcept
requires requires { acos(q.number()); } || requires { std::acos(q.number()); }
{
using std::acos;
return acos(quantity_cast<one>(q).number()) * angle[radian];
}
template<quantity_of<dimension_one> Q>
template<QuantityOf<dimension_one> Q>
requires treat_as_floating_point<typename Q::rep>
[[nodiscard]] inline quantity_of<angle[radian]> auto atan(const Q& q) noexcept
[[nodiscard]] inline QuantityOf<angle[radian]> auto atan(const Q& q) noexcept
requires requires { atan(q.number()); } || requires { std::atan(q.number()); }
{
using std::atan;

View File

@@ -60,13 +60,13 @@ inline constexpr auto energy = force * length;
// concepts verification
static_assert(BaseDimension<length_>);
static_assert(!BaseDimension<std::remove_const_t<decltype(frequency)>>);
static_assert(!DerivedDimension<length_>);
static_assert(DerivedDimension<std::remove_const_t<decltype(frequency)>>);
static_assert(!detail::DerivedDimension<length_>);
static_assert(detail::DerivedDimension<std::remove_const_t<decltype(frequency)>>);
static_assert(Dimension<length_>);
static_assert(Dimension<std::remove_const_t<decltype(frequency)>>);
static_assert(DerivedDimension<dimension_one_>);
static_assert(DerivedDimension<decltype(length / length)>); // dimension_one
static_assert(detail::DerivedDimension<dimension_one_>);
static_assert(detail::DerivedDimension<decltype(length / length)>); // dimension_one
static_assert(BaseDimension<decltype(speed * time)>); // length
// derived dimension expression template syntax verification

View File

@@ -397,7 +397,6 @@ template<auto& s>
concept invalid_operations = requires {
requires !requires { s < s; };
requires !requires { s / 2; };
requires !requires { 2 * s; };
requires !requires { s * 2; };
requires !requires { s + 2; };
requires !requires { 2 + s; };
@@ -411,16 +410,9 @@ concept invalid_operations = requires {
requires !requires { 2 < s; };
requires !requires { s + time[second]; };
requires !requires { s - time[second]; };
requires !requires { s* time[second]; };
requires !requires { s / time[second]; };
requires !requires { s == time[second]; };
requires !requires { s < time[second]; };
requires !requires { time[second] + s; };
requires !requires { time[second] - s; };
requires !requires { time[second] * s; };
requires !requires { time[second] / s; };
requires !requires { time[second] == s; };
requires !requires { time[second] < s; };
requires !requires { s + 1 * time[second]; };
requires !requires { s - 1 * time[second]; };
requires !requires { s * 1 * time[second]; };