mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-30 10:27:16 +02:00
fix: common_unit
handling fixed for some corner cases
This commit is contained in:
@ -26,7 +26,12 @@
|
|||||||
#include <mp-units/framework/quantity_spec.h>
|
#include <mp-units/framework/quantity_spec.h>
|
||||||
#include <mp-units/framework/unit_concepts.h>
|
#include <mp-units/framework/unit_concepts.h>
|
||||||
|
|
||||||
namespace mp_units::detail {
|
namespace mp_units {
|
||||||
|
|
||||||
|
template<Unit U1, Unit U2, Unit... Rest>
|
||||||
|
struct common_unit;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
template<AssociatedUnit U>
|
template<AssociatedUnit U>
|
||||||
[[nodiscard]] consteval auto all_are_kinds(U);
|
[[nodiscard]] consteval auto all_are_kinds(U);
|
||||||
@ -61,6 +66,12 @@ template<AssociatedUnit U>
|
|||||||
template<AssociatedUnit U>
|
template<AssociatedUnit U>
|
||||||
using to_quantity_spec = decltype(get_associated_quantity_impl(U{}));
|
using to_quantity_spec = decltype(get_associated_quantity_impl(U{}));
|
||||||
|
|
||||||
|
template<typename... Us>
|
||||||
|
[[nodiscard]] consteval auto get_associated_quantity_impl(common_unit<Us...>)
|
||||||
|
{
|
||||||
|
return get_common_quantity_spec(get_associated_quantity_impl(Us{})...);
|
||||||
|
}
|
||||||
|
|
||||||
template<AssociatedUnit U>
|
template<AssociatedUnit U>
|
||||||
[[nodiscard]] consteval auto get_associated_quantity_impl(U u)
|
[[nodiscard]] consteval auto get_associated_quantity_impl(U u)
|
||||||
{
|
{
|
||||||
@ -83,4 +94,6 @@ template<AssociatedUnit U>
|
|||||||
return get_associated_quantity_impl(u);
|
return get_associated_quantity_impl(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mp_units::detail
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace mp_units
|
||||||
|
@ -93,16 +93,17 @@ concept HaveCommonReferenceImpl = requires { get_common_reference(R1, R2); };
|
|||||||
template<auto R1, auto R2>
|
template<auto R1, auto R2>
|
||||||
concept HaveCommonReference = HaveCommonReferenceImpl<R1, R2>;
|
concept HaveCommonReference = HaveCommonReferenceImpl<R1, R2>;
|
||||||
|
|
||||||
|
template<typename Func, Quantity Q1, Quantity Q2>
|
||||||
|
using common_quantity_for = quantity<get_common_reference(Q1::reference, Q2::reference),
|
||||||
|
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
|
||||||
|
|
||||||
template<typename Func, typename Q1, typename Q2>
|
template<typename Func, typename Q1, typename Q2>
|
||||||
concept CommonlyInvocableQuantities =
|
concept CommonlyInvocableQuantities =
|
||||||
Quantity<Q1> && Quantity<Q2> && HaveCommonReference<Q1::reference, Q2::reference> &&
|
Quantity<Q1> && Quantity<Q2> && HaveCommonReference<Q1::reference, Q2::reference> &&
|
||||||
|
std::convertible_to<Q1, common_quantity_for<Func, Q1, Q2>> &&
|
||||||
|
std::convertible_to<Q2, common_quantity_for<Func, Q1, Q2>> &&
|
||||||
InvocableQuantities<Func, Q1, Q2, get_common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character>;
|
InvocableQuantities<Func, Q1, Q2, get_common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character>;
|
||||||
|
|
||||||
template<typename Func, Quantity Q1, Quantity Q2>
|
|
||||||
requires CommonlyInvocableQuantities<Func, Q1, Q2>
|
|
||||||
using common_quantity_for = quantity<get_common_reference(Q1::reference, Q2::reference),
|
|
||||||
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
|
|
||||||
|
|
||||||
template<auto R1, auto R2, typename Rep1, typename Rep2>
|
template<auto R1, auto R2, typename Rep1, typename Rep2>
|
||||||
concept SameValueAs = SameReference<R1, R2> && std::same_as<Rep1, Rep2>;
|
concept SameValueAs = SameReference<R1, R2> && std::same_as<Rep1, Rep2>;
|
||||||
|
|
||||||
|
@ -60,9 +60,6 @@ import std;
|
|||||||
|
|
||||||
namespace mp_units {
|
namespace mp_units {
|
||||||
|
|
||||||
template<Unit U1, Unit U2, Unit... Rest>
|
|
||||||
struct common_unit;
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<Magnitude auto M, Unit U>
|
template<Magnitude auto M, Unit U>
|
||||||
@ -784,9 +781,18 @@ constexpr Out unit_symbol_impl(Out out, const scaled_unit_impl<M, U>& u, const u
|
|||||||
template<typename... Us, Unit U>
|
template<typename... Us, Unit U>
|
||||||
[[nodiscard]] consteval Unit auto get_common_unit_in(common_unit<Us...>, U u)
|
[[nodiscard]] consteval Unit auto get_common_unit_in(common_unit<Us...>, U u)
|
||||||
{
|
{
|
||||||
|
auto get_magnitude = [&]() {
|
||||||
|
if constexpr (requires { common_unit<Us...>::mag; })
|
||||||
|
return common_unit<Us...>::mag;
|
||||||
|
else
|
||||||
|
return mag<1>;
|
||||||
|
};
|
||||||
constexpr auto canonical_u = get_canonical_unit(u);
|
constexpr auto canonical_u = get_canonical_unit(u);
|
||||||
constexpr Magnitude auto mag = common_unit<Us...>::mag / canonical_u.mag;
|
constexpr Magnitude auto cmag = get_magnitude() / canonical_u.mag;
|
||||||
return scaled_unit<mag, U>{};
|
if constexpr (cmag == mag<1>)
|
||||||
|
return u;
|
||||||
|
else
|
||||||
|
return scaled_unit<cmag, U>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT, std::output_iterator<CharT> Out, typename U, typename... Rest>
|
template<typename CharT, std::output_iterator<CharT> Out, typename U, typename... Rest>
|
||||||
|
@ -214,6 +214,7 @@ static_assert(UnitOf<struct si::hertz, inverse(isq::time)>);
|
|||||||
static_assert(UnitOf<struct one, dimensionless>);
|
static_assert(UnitOf<struct one, dimensionless>);
|
||||||
static_assert(UnitOf<struct percent, dimensionless>);
|
static_assert(UnitOf<struct percent, dimensionless>);
|
||||||
static_assert(UnitOf<struct si::radian, isq::angular_measure>);
|
static_assert(UnitOf<struct si::radian, isq::angular_measure>);
|
||||||
|
static_assert(UnitOf<struct si::degree, isq::angular_measure>);
|
||||||
static_assert(UnitOf<struct one, isq::angular_measure>);
|
static_assert(UnitOf<struct one, isq::angular_measure>);
|
||||||
static_assert(UnitOf<struct percent, isq::angular_measure>);
|
static_assert(UnitOf<struct percent, isq::angular_measure>);
|
||||||
static_assert(!UnitOf<struct si::radian, dimensionless>);
|
static_assert(!UnitOf<struct si::radian, dimensionless>);
|
||||||
@ -247,9 +248,15 @@ static_assert(ReferenceOf<decltype(dimensionless[one]), dimensionless>);
|
|||||||
static_assert(ReferenceOf<decltype(isq::rotation[one]), isq::rotation>);
|
static_assert(ReferenceOf<decltype(isq::rotation[one]), isq::rotation>);
|
||||||
static_assert(ReferenceOf<decltype(isq::rotation[one]), dimensionless>);
|
static_assert(ReferenceOf<decltype(isq::rotation[one]), dimensionless>);
|
||||||
static_assert(ReferenceOf<struct si::radian, isq::angular_measure>);
|
static_assert(ReferenceOf<struct si::radian, isq::angular_measure>);
|
||||||
|
static_assert(ReferenceOf<struct si::degree, isq::angular_measure>);
|
||||||
|
static_assert(ReferenceOf<decltype(get_common_unit(si::degree, si::radian)), isq::angular_measure>);
|
||||||
static_assert(!ReferenceOf<struct si::radian, dimensionless>);
|
static_assert(!ReferenceOf<struct si::radian, dimensionless>);
|
||||||
|
static_assert(!ReferenceOf<struct si::degree, dimensionless>);
|
||||||
|
static_assert(!ReferenceOf<decltype(get_common_unit(si::degree, si::radian)), dimensionless>);
|
||||||
static_assert(ReferenceOf<decltype(isq::angular_measure[si::radian]), isq::angular_measure>);
|
static_assert(ReferenceOf<decltype(isq::angular_measure[si::radian]), isq::angular_measure>);
|
||||||
|
static_assert(ReferenceOf<decltype(isq::angular_measure[si::degree]), isq::angular_measure>);
|
||||||
static_assert(!ReferenceOf<decltype(isq::angular_measure[si::radian]), dimensionless>);
|
static_assert(!ReferenceOf<decltype(isq::angular_measure[si::radian]), dimensionless>);
|
||||||
|
static_assert(!ReferenceOf<decltype(isq::angular_measure[si::degree]), dimensionless>);
|
||||||
static_assert(ReferenceOf<struct one, isq::rotation>);
|
static_assert(ReferenceOf<struct one, isq::rotation>);
|
||||||
static_assert(ReferenceOf<struct one, isq::angular_measure>);
|
static_assert(ReferenceOf<struct one, isq::angular_measure>);
|
||||||
static_assert(!ReferenceOf<decltype(dimensionless[one]), isq::rotation>);
|
static_assert(!ReferenceOf<decltype(dimensionless[one]), isq::rotation>);
|
||||||
|
@ -273,6 +273,10 @@ static_assert(quantity<isq::length[m], int>(2000 * m).force_in(km).numerical_val
|
|||||||
static_assert((15. * m).in(nm).numerical_value_in(m) == 15.);
|
static_assert((15. * m).in(nm).numerical_value_in(m) == 15.);
|
||||||
static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.);
|
static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.);
|
||||||
|
|
||||||
|
// check if unit conversion works - don't bother about the actual result
|
||||||
|
static_assert((1. * rad + 1. * deg).in(rad) != 0 * rad);
|
||||||
|
static_assert((1. * rad + 1. * deg).in(deg) != 0 * deg);
|
||||||
|
|
||||||
#if MP_UNITS_HOSTED
|
#if MP_UNITS_HOSTED
|
||||||
using namespace std::complex_literals;
|
using namespace std::complex_literals;
|
||||||
static_assert(((2. + 1i) * V).in(mV).numerical_value_in(mV) == 2000. + 1000i);
|
static_assert(((2. + 1i) * V).in(mV).numerical_value_in(mV) == 2000. + 1000i);
|
||||||
@ -788,6 +792,12 @@ consteval bool invalid_arithmetic(Ts... ts)
|
|||||||
static_assert(invalid_arithmetic(5 * isq::activity[Bq], 5 * isq::frequency[Hz]));
|
static_assert(invalid_arithmetic(5 * isq::activity[Bq], 5 * isq::frequency[Hz]));
|
||||||
static_assert(invalid_arithmetic(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz]));
|
static_assert(invalid_arithmetic(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz]));
|
||||||
|
|
||||||
|
// irrational conversion factors require floating point representation
|
||||||
|
static_assert(invalid_arithmetic(1 * rad, 1 * deg));
|
||||||
|
static_assert(is_of_type<1. * rad + 1 * deg, quantity<common_unit<struct si::degree, struct si::radian>{}, double>>);
|
||||||
|
static_assert(is_of_type<1 * rad + 1. * deg, quantity<common_unit<struct si::degree, struct si::radian>{}, double>>);
|
||||||
|
static_assert(is_of_type<1. * rad + 1. * deg, quantity<common_unit<struct si::degree, struct si::radian>{}, double>>);
|
||||||
|
|
||||||
// Physical constants
|
// Physical constants
|
||||||
static_assert(1 * si::si2019::speed_of_light_in_vacuum + 10 * isq::speed[m / s] == 299'792'468 * isq::speed[m / s]);
|
static_assert(1 * si::si2019::speed_of_light_in_vacuum + 10 * isq::speed[m / s] == 299'792'468 * isq::speed[m / s]);
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include <mp-units/systems/angular.h>
|
||||||
#include <mp-units/systems/iau.h>
|
#include <mp-units/systems/iau.h>
|
||||||
#include <mp-units/systems/iec.h>
|
#include <mp-units/systems/iec.h>
|
||||||
#include <mp-units/systems/international.h>
|
#include <mp-units/systems/international.h>
|
||||||
@ -209,6 +210,10 @@ static_assert(unit_symbol(get_common_unit(kilo<metre> / hour, metre / second) /
|
|||||||
"EQUIV{[1/5 km/h], [1/18 m/s]}/s");
|
"EQUIV{[1/5 km/h], [1/18 m/s]}/s");
|
||||||
static_assert(unit_symbol(get_common_unit(kilo<metre> / hour, metre / second) * second) ==
|
static_assert(unit_symbol(get_common_unit(kilo<metre> / hour, metre / second) * second) ==
|
||||||
"EQUIV{[1/5 km/h], [1/18 m/s]} s");
|
"EQUIV{[1/5 km/h], [1/18 m/s]} s");
|
||||||
|
static_assert(unit_symbol(get_common_unit(radian, degree)) == "EQUIV{[1/𝜋°], [1/180 rad]}");
|
||||||
|
static_assert(unit_symbol(get_common_unit(angular::radian, angular::revolution)) == "EQUIV{rad, [2⁻¹ 𝜋⁻¹ rev]}");
|
||||||
|
static_assert(unit_symbol<usf{.solidus = always}>(get_common_unit(angular::radian, angular::revolution)) ==
|
||||||
|
"EQUIV{rad, [1/(2 𝜋) rev]}");
|
||||||
|
|
||||||
// derived units
|
// derived units
|
||||||
static_assert(unit_symbol(one) == ""); // NOLINT(readability-container-size-empty)
|
static_assert(unit_symbol(one) == ""); // NOLINT(readability-container-size-empty)
|
||||||
|
@ -69,6 +69,7 @@ inline constexpr struct nu_second_ final : named_unit<"s"> {} nu_second;
|
|||||||
|
|
||||||
// derived named units
|
// derived named units
|
||||||
inline constexpr struct radian_ final : named_unit<"rad", metre / metre> {} radian;
|
inline constexpr struct radian_ final : named_unit<"rad", metre / metre> {} radian;
|
||||||
|
inline constexpr struct revolution_ final : named_unit<"rev", mag<2> * mag<pi> * radian> {} revolution;
|
||||||
inline constexpr struct steradian_ final : named_unit<"sr", square(metre) / square(metre)> {} steradian;
|
inline constexpr struct steradian_ final : named_unit<"sr", square(metre) / square(metre)> {} steradian;
|
||||||
inline constexpr struct hertz_ final : named_unit<"Hz", inverse(second)> {} hertz;
|
inline constexpr struct hertz_ final : named_unit<"Hz", inverse(second)> {} hertz;
|
||||||
inline constexpr struct becquerel_ final : named_unit<"Bq", inverse(second)> {} becquerel;
|
inline constexpr struct becquerel_ final : named_unit<"Bq", inverse(second)> {} becquerel;
|
||||||
@ -539,6 +540,9 @@ static_assert(is_of_type<get_common_unit(mile, yard), yard_>);
|
|||||||
static_assert(
|
static_assert(
|
||||||
is_of_type<get_common_unit(speed_of_light_in_vacuum, metre / second), derived_unit<metre_, per<second_>>>);
|
is_of_type<get_common_unit(speed_of_light_in_vacuum, metre / second), derived_unit<metre_, per<second_>>>);
|
||||||
|
|
||||||
|
static_assert(is_of_type<get_common_unit(radian, revolution), common_unit<radian_, revolution_>>);
|
||||||
|
static_assert(is_of_type<get_common_unit(radian, degree), common_unit<degree_, radian_>>);
|
||||||
|
|
||||||
// those should return instantiations of the `common_unit` class template
|
// those should return instantiations of the `common_unit` class template
|
||||||
static_assert(is_of_type<get_common_unit(kilometre, mile), common_unit<kilo_<metre_>, mile_>>);
|
static_assert(is_of_type<get_common_unit(kilometre, mile), common_unit<kilo_<metre_>, mile_>>);
|
||||||
static_assert(is_of_type<get_common_unit(mile, kilometre), common_unit<kilo_<metre_>, mile_>>);
|
static_assert(is_of_type<get_common_unit(mile, kilometre), common_unit<kilo_<metre_>, mile_>>);
|
||||||
|
Reference in New Issue
Block a user