mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-30 02:17:16 +02:00
@ -52,8 +52,7 @@ constexpr QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto d,
|
||||
template<QuantityOf<isq::length> D, QuantityOf<isq::time> T, QuantityOf<isq::speed> V>
|
||||
void print_result(D distance, T duration, V speed)
|
||||
{
|
||||
constexpr auto kmph = si::kilo<si::metre> / si::hour;
|
||||
const auto result_in_kmph = quantity_cast<kmph>(speed);
|
||||
const auto result_in_kmph = value_cast<km / h>(speed);
|
||||
std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph
|
||||
<< ".\n";
|
||||
}
|
||||
@ -80,8 +79,7 @@ void example()
|
||||
std::cout << "\nSI units with 'double' as representation\n";
|
||||
|
||||
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
|
||||
print_result(distance, duration,
|
||||
fixed_int_si_avg_speed(quantity_cast<int>(distance), quantity_cast<int>(duration)));
|
||||
print_result(distance, duration, fixed_int_si_avg_speed(value_cast<int>(distance), value_cast<int>(duration)));
|
||||
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
}
|
||||
@ -97,7 +95,7 @@ void example()
|
||||
|
||||
// it is not possible to make a lossless conversion of miles to meters on an integral type
|
||||
// (explicit cast needed)
|
||||
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration));
|
||||
print_result(distance, duration, fixed_int_si_avg_speed(value_cast<si::metre>(distance), duration));
|
||||
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
}
|
||||
@ -114,9 +112,8 @@ void example()
|
||||
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
|
||||
// also it is not possible to make a lossless conversion of miles to meters on an integral type
|
||||
// (explicit cast needed)
|
||||
print_result(
|
||||
distance, duration,
|
||||
fixed_int_si_avg_speed(quantity_cast<quantity<isq::length[m], int>>(distance), quantity_cast<int>(duration)));
|
||||
print_result(distance, duration,
|
||||
fixed_int_si_avg_speed(value_cast<int>(value_cast<si::metre>(distance)), value_cast<int>(duration)));
|
||||
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
}
|
||||
@ -130,7 +127,7 @@ void example()
|
||||
|
||||
// it is not possible to make a lossless conversion of centimeters to meters on an integral type
|
||||
// (explicit cast needed)
|
||||
print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast<si::metre>(distance), duration));
|
||||
print_result(distance, duration, fixed_int_si_avg_speed(value_cast<si::metre>(distance), duration));
|
||||
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
}
|
||||
@ -145,9 +142,8 @@ void example()
|
||||
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
|
||||
// it is not possible to make a lossless conversion of centimeters to meters on an integral type
|
||||
// (explicit cast needed)
|
||||
print_result(
|
||||
distance, duration,
|
||||
fixed_int_si_avg_speed(quantity_cast<quantity<isq::length[m], int>>(distance), quantity_cast<int>(duration)));
|
||||
print_result(distance, duration,
|
||||
fixed_int_si_avg_speed(value_cast<int>(value_cast<m>(distance)), value_cast<int>(duration)));
|
||||
|
||||
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
|
@ -40,7 +40,7 @@ int main()
|
||||
constexpr auto R = isq::resistance(4.7, si::kilo<si::ohm>);
|
||||
|
||||
for (auto t = isq::time(0, ms); t <= isq::time(50, ms); ++t) {
|
||||
const weak_quantity_of<isq::voltage> auto Vt = V0 * mp_units::exp(-t / (R * C));
|
||||
const WeakQuantityOf<isq::voltage> auto Vt = V0 * mp_units::exp(-t / (R * C));
|
||||
|
||||
std::cout << "at " << t << " voltage is ";
|
||||
|
||||
|
@ -32,7 +32,7 @@ template<mp_units::Quantity Target, mp_units::Quantity Source>
|
||||
requires std::constructible_from<Target, Source>
|
||||
inline constexpr double conversion_factor(Target, Source)
|
||||
{
|
||||
return quantity_cast<Target::unit>(1. * Source::reference).number();
|
||||
return value_cast<Target::unit>(1. * Source::reference).number();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -52,7 +52,7 @@ struct Ship {
|
||||
template<Unit auto... Us, Quantity Q>
|
||||
auto fmt_line(const Q& q)
|
||||
{
|
||||
return STD_FMT::format("{:22}", q) + (STD_FMT::format(",{:20}", quantity_cast<Us>(q)) + ...);
|
||||
return STD_FMT::format("{:22}", q) + (STD_FMT::format(",{:20}", value_cast<Us>(q)) + ...);
|
||||
}
|
||||
|
||||
// Print the ship details in the units as defined in the Ship struct, in other si::imperial units, and in SI
|
||||
|
@ -77,7 +77,7 @@ void print(std::string_view phase_name, timestamp start_ts, const glide_computer
|
||||
std::cout << STD_FMT::format(
|
||||
"| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) "
|
||||
"|\n",
|
||||
phase_name, quantity_cast<si::minute>(new_point.ts - point.ts), quantity_cast<si::minute>(new_point.ts - start_ts),
|
||||
phase_name, value_cast<si::minute>(new_point.ts - point.ts), value_cast<si::minute>(new_point.ts - start_ts),
|
||||
new_point.dist - point.dist, new_point.dist, new_point.alt - point.alt, new_point.alt);
|
||||
}
|
||||
|
||||
|
@ -85,11 +85,11 @@ void print(const R& gliders)
|
||||
std::cout << "- Name: " << g.name << "\n";
|
||||
std::cout << "- Polar:\n";
|
||||
for (const auto& p : g.polar) {
|
||||
const auto ratio = quantity_cast<one>(glide_ratio(g.polar[0]));
|
||||
const auto ratio = value_cast<one>(glide_ratio(g.polar[0]));
|
||||
std::cout << STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v, ratio,
|
||||
// TODO is it possible to make ADL work below (we need another set of trig functions
|
||||
// for strong angle in a different namespace)
|
||||
quantity_cast<si::degree>(isq::asin(1 / ratio)));
|
||||
value_cast<si::degree>(isq::asin(1 / ratio)));
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ int main()
|
||||
constexpr auto v2 = isq::speed(70., mph);
|
||||
constexpr auto v3 = avg_speed(isq::distance(220, km), isq::duration(2, h));
|
||||
constexpr auto v4 = avg_speed(quantity<isq::distance[mi]>{140}, quantity<isq::duration[h]>{2});
|
||||
constexpr auto v5 = quantity_cast<quantity<isq::speed[m / s]>>(v3);
|
||||
constexpr auto v6 = quantity_cast<m / s>(v4);
|
||||
constexpr auto v7 = quantity_cast<int>(v6);
|
||||
constexpr auto v5 = value_cast<m / s>(v3);
|
||||
constexpr auto v6 = value_cast<m / s>(v4);
|
||||
constexpr auto v7 = value_cast<int>(v6);
|
||||
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
|
@ -95,7 +95,7 @@ void quantity_of_vector_cast()
|
||||
std::cout << "v = " << v << "\n";
|
||||
std::cout << "u = " << u << "\n";
|
||||
|
||||
std::cout << "v[km] = " << quantity_cast<km>(v) << "\n";
|
||||
std::cout << "v[km] = " << value_cast<km>(v) << "\n";
|
||||
std::cout << "u[m] = " << u[m] << "\n";
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ void si_example()
|
||||
<< "E = " << E3 << "\n";
|
||||
|
||||
std::cout << "\n[converted from SI units back to GeV]\n"
|
||||
<< "E = " << quantity_cast<GeV>(E3) << "\n";
|
||||
<< "E = " << value_cast<GeV>(E3) << "\n";
|
||||
}
|
||||
|
||||
void natural_example()
|
||||
|
@ -54,9 +54,11 @@ add_library(
|
||||
include/mp_units/bits/ratio.h
|
||||
include/mp_units/bits/reference_concepts.h
|
||||
include/mp_units/bits/representation_concepts.h
|
||||
include/mp_units/bits/sudo_cast.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/bits/value_cast.h
|
||||
|
||||
include/mp_units/concepts.h
|
||||
include/mp_units/customization_points.h
|
||||
|
@ -25,197 +25,42 @@
|
||||
#include <mp_units/bits/external/type_traits.h>
|
||||
#include <mp_units/bits/magnitude.h>
|
||||
#include <mp_units/bits/quantity_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/reference.h>
|
||||
#include <mp_units/unit.h>
|
||||
|
||||
UNITS_DIAGNOSTIC_PUSH
|
||||
// warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int
|
||||
UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA
|
||||
|
||||
namespace mp_units {
|
||||
|
||||
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
|
||||
class quantity;
|
||||
|
||||
// template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
|
||||
// class quantity_point;
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity
|
||||
* @brief Explicit cast of a quantity type
|
||||
*
|
||||
* 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 converts only a quantity type. It might be used to force some quantity type
|
||||
* conversions that are not implicitly allowed but are allowed explicitly.
|
||||
*
|
||||
* This cast gets the target quantity type to cast to. For example:
|
||||
* For example:
|
||||
*
|
||||
* auto q1 = 1234. * isq::length[mm];
|
||||
* auto q2 = quantity_cast<quantity<isq::height[m], int>>(q1);
|
||||
* @code{.cpp}
|
||||
* auto length = isq::length(42 * m);
|
||||
* auto distance = quantity_cast<isq::distance>(length);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam To a target quantity type to cast to
|
||||
*/
|
||||
template<Quantity To, auto R, typename Rep>
|
||||
requires(interconvertible(To::reference, R)) &&
|
||||
((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 (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
|
||||
// using static_cast to suppress all the compiler
|
||||
// warnings on conversions
|
||||
} else {
|
||||
// scale the number
|
||||
using rep_type = decltype([] {
|
||||
// determines the best representation type
|
||||
if constexpr (requires { typename std::common_type_t<Rep, typename To::rep>; })
|
||||
// returns a common type of two representation types if available
|
||||
// i.e. `double` and `int` will end up with `double` precision
|
||||
return std::common_type_t<Rep, typename To::rep>{};
|
||||
else
|
||||
return Rep{};
|
||||
}());
|
||||
using multiplier_type = decltype([] {
|
||||
// widen the type to prevent overflows
|
||||
using wider_type = decltype(rep_type{} * std::intmax_t{});
|
||||
// check if `wider_type` supports scaling operations
|
||||
if constexpr (requires(wider_type v) { v* v / v; })
|
||||
// if the `wider_type` can handle scaling operations then use it to improve accuracy
|
||||
return wider_type{};
|
||||
else
|
||||
// needed for example for linear algebra where `op/` on matrix types is not available
|
||||
return std::intmax_t{};
|
||||
}());
|
||||
|
||||
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);
|
||||
|
||||
constexpr auto val = [](Magnitude auto m) { return get_value<multiplier_type>(m); };
|
||||
return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.number()) * val(num) / val(den) * val(irr)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 only the target quantity specification to cast to. For example:
|
||||
*
|
||||
* auto v = quantity_cast<isq::velocity>(120 * isq::length[km] / (2 * isq::time[h]));
|
||||
* @note This cast does not affect the underlying value of a number stored in a quantity.
|
||||
*
|
||||
* @tparam ToQS a quantity specification to use for a target quantity
|
||||
*/
|
||||
template<QuantitySpec auto ToQS, auto R, typename Rep>
|
||||
requires(interconvertible(ToQS, get_quantity_spec(R)))
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
template<QuantitySpec auto ToQ, auto R, typename Rep>
|
||||
requires(interconvertible(ToQ, get_quantity_spec(R)))
|
||||
[[nodiscard]] constexpr Quantity auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
constexpr reference<ToQS, quantity<R, Rep>::unit> r;
|
||||
return quantity_cast<quantity<r, Rep>>(q);
|
||||
constexpr reference<ToQ, quantity<R, Rep>::unit> r;
|
||||
return q.count() * r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 only the target unit to cast to. For example:
|
||||
*
|
||||
* auto d = quantity_cast<si::second>(1234 * isq::time[ms]);
|
||||
*
|
||||
* @tparam ToU a unit to use for a target quantity
|
||||
*/
|
||||
template<Unit auto ToU, auto R, typename Rep>
|
||||
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;
|
||||
return quantity_cast<quantity<r, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 only representation to cast to. For example:
|
||||
*
|
||||
* auto q = quantity_cast<int>(1.23 * isq::time[ms]);
|
||||
*
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<Representation ToRep, auto R, typename 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);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity point
|
||||
// *
|
||||
// * Implicit conversions between quantity points of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets the target quantity point 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::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});
|
||||
// *
|
||||
// * @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast
|
||||
// */
|
||||
// template<typename CastSpec, typename O, typename U, typename Rep>
|
||||
// [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& qp)
|
||||
// requires requires {
|
||||
// requires is_specialization_of<CastSpec, quantity_point>;
|
||||
// requires requires { quantity_cast<typename CastSpec::quantity_type>(qp.relative()); };
|
||||
// requires equivalent<O, typename CastSpec::origin>;
|
||||
// } || // TODO: Simplify when Clang catches up.
|
||||
// requires { quantity_cast<CastSpec>(qp.relative()); }
|
||||
// {
|
||||
// if constexpr (is_specialization_of<CastSpec, quantity_point>)
|
||||
// return quantity_point(quantity_cast<typename CastSpec::quantity_type>(qp.relative()));
|
||||
// else
|
||||
// return quantity_point(quantity_cast<CastSpec>(qp.relative()));
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity point
|
||||
// *
|
||||
// * Implicit conversions between quantity points of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets both the target dimension and unit to cast to. For example:
|
||||
// *
|
||||
// * auto q1 = mp_units::quantity_point_cast<mp_units::isq::si::dim_speed, mp_units::isq::si::kilometre_per_hour>(v1);
|
||||
// *
|
||||
// * @note This cast is especially useful when working with quantity points of unknown dimensions
|
||||
// * (@c unknown_dimension).
|
||||
// *
|
||||
// * @tparam ToD a dimension type to use for a target quantity
|
||||
// * @tparam ToU a unit type to use for a target quantity
|
||||
// */
|
||||
// template<Dimension ToD, Unit ToU, typename O, typename U, typename Rep>
|
||||
// requires equivalent<ToD, typename O::dimension> && UnitOf<ToU, ToD> && RebindablePointOriginFor<O, ToD>
|
||||
// [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& q)
|
||||
// {
|
||||
// return quantity_point_cast<quantity_point<rebind_point_origin_dimension<O, ToD>, ToU, Rep>>(q);
|
||||
// }
|
||||
|
||||
} // namespace mp_units
|
||||
|
||||
UNITS_DIAGNOSTIC_POP
|
||||
|
@ -63,7 +63,7 @@ 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
|
||||
// TODO Fix it according to sudo_cast implementation
|
||||
template<typename T>
|
||||
concept scalable_ = // exposition only
|
||||
castable_number_<T> || (requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
|
||||
|
93
src/core/include/mp_units/bits/sudo_cast.h
Normal file
93
src/core/include/mp_units/bits/sudo_cast.h
Normal file
@ -0,0 +1,93 @@
|
||||
// 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/external/type_traits.h>
|
||||
#include <mp_units/bits/magnitude.h>
|
||||
#include <mp_units/bits/quantity_concepts.h>
|
||||
#include <mp_units/bits/reference_concepts.h>
|
||||
#include <mp_units/unit.h>
|
||||
|
||||
namespace mp_units {
|
||||
|
||||
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
|
||||
class quantity;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of entire quantity
|
||||
*
|
||||
* @note This is too powerful to be used by users.
|
||||
*
|
||||
* @tparam To a target quantity type to cast to
|
||||
*/
|
||||
template<Quantity To, auto R, typename Rep>
|
||||
requires(interconvertible(To::reference, R)) &&
|
||||
((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 Quantity auto sudo_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
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
|
||||
// using static_cast to suppress all the compiler
|
||||
// warnings on conversions
|
||||
} else {
|
||||
// scale the number
|
||||
using rep_type = decltype([] {
|
||||
// determines the best representation type
|
||||
if constexpr (requires { typename std::common_type_t<Rep, typename To::rep>; })
|
||||
// returns a common type of two representation types if available
|
||||
// i.e. `double` and `int` will end up with `double` precision
|
||||
return std::common_type_t<Rep, typename To::rep>{};
|
||||
else
|
||||
return Rep{};
|
||||
}());
|
||||
using multiplier_type = decltype([] {
|
||||
// widen the type to prevent overflows
|
||||
using wider_type = decltype(rep_type{} * std::intmax_t{});
|
||||
// check if `wider_type` supports scaling operations
|
||||
if constexpr (requires(wider_type v) { v* v / v; })
|
||||
// if the `wider_type` can handle scaling operations then use it to improve accuracy
|
||||
return wider_type{};
|
||||
else
|
||||
// needed for example for linear algebra where `op/` on matrix types is not available
|
||||
return std::intmax_t{};
|
||||
}());
|
||||
|
||||
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);
|
||||
|
||||
constexpr auto val = [](Magnitude auto m) { return get_value<multiplier_type>(m); };
|
||||
return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.number()) * val(num) / val(den) * val(irr)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mp_units
|
74
src/core/include/mp_units/bits/value_cast.h
Normal file
74
src/core/include/mp_units/bits/value_cast.h
Normal file
@ -0,0 +1,74 @@
|
||||
// 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_concepts.h>
|
||||
#include <mp_units/bits/reference_concepts.h>
|
||||
#include <mp_units/bits/representation_concepts.h>
|
||||
#include <mp_units/bits/unit_concepts.h>
|
||||
|
||||
namespace mp_units {
|
||||
|
||||
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
|
||||
class quantity;
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity's unit
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* auto d = value_cast<si::second>(1234 * ms);
|
||||
*
|
||||
* @tparam ToU a unit to use for a target quantity
|
||||
*/
|
||||
template<Unit auto ToU, auto R, typename Rep>
|
||||
requires(interconvertible(ToU, get_unit(R)))
|
||||
[[nodiscard]] constexpr Quantity auto value_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
if constexpr (detail::is_specialization_of_reference<R> || !AssociatedUnit<ToU>) {
|
||||
constexpr reference<quantity<R, Rep>::quantity_spec, ToU> r;
|
||||
return detail::sudo_cast<quantity<r, Rep>>(q);
|
||||
} else {
|
||||
return detail::sudo_cast<quantity<ToU, Rep>>(q);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity's representation type
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* auto q = value_cast<int>(1.23 * ms);
|
||||
*
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<Representation ToRep, auto R, typename Rep>
|
||||
requires RepresentationOf<ToRep, get_quantity_spec(R).character> && std::constructible_from<ToRep, Rep>
|
||||
[[nodiscard]] constexpr Quantity auto value_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
return detail::sudo_cast<quantity<R, ToRep>>(q);
|
||||
}
|
||||
|
||||
} // namespace mp_units
|
@ -24,11 +24,11 @@
|
||||
#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/sudo_cast.h>
|
||||
#include <mp_units/bits/unit_concepts.h>
|
||||
#include <mp_units/customization_points.h>
|
||||
#include <mp_units/dimension.h>
|
||||
@ -36,8 +36,6 @@
|
||||
#include <mp_units/reference.h>
|
||||
#include <mp_units/unit.h>
|
||||
#include <compare>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace mp_units {
|
||||
@ -62,7 +60,7 @@ concept Harmonic = // exposition only
|
||||
|
||||
template<typename QFrom, typename QTo>
|
||||
concept QuantityConvertibleTo = // exposition only
|
||||
Quantity<QFrom> && Quantity<QTo> && requires(QFrom q) { quantity_cast<QTo>(q); } &&
|
||||
Quantity<QFrom> && Quantity<QTo> && requires(QFrom q) { detail::sudo_cast<QTo>(q); } &&
|
||||
(treat_as_floating_point<typename QTo::rep> ||
|
||||
(!treat_as_floating_point<typename QFrom::rep> && Harmonic<QFrom, QTo>));
|
||||
|
||||
@ -129,7 +127,7 @@ public:
|
||||
}
|
||||
|
||||
template<detail::QuantityConvertibleTo<quantity> Q>
|
||||
constexpr explicit(false) quantity(const Q& q) : number_(quantity_cast<quantity>(q).number())
|
||||
constexpr explicit(false) quantity(const Q& q) : number_(detail::sudo_cast<quantity>(q).number())
|
||||
{
|
||||
}
|
||||
|
||||
@ -514,7 +512,7 @@ template<Quantity Q1, Quantity Q2>
|
||||
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
constexpr auto ref = common_reference(Q1::reference, Q2::reference);
|
||||
return quantity_cast<ref>(lhs).number() <=> quantity_cast<ref>(rhs).number();
|
||||
return quantity<ref, typename Q1::rep>(lhs).number() <=> quantity<ref, typename Q2::rep>(rhs).number();
|
||||
}
|
||||
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
@ -523,7 +521,7 @@ template<Quantity Q1, Quantity Q2>
|
||||
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
constexpr auto ref = common_reference(Q1::reference, Q2::reference);
|
||||
return quantity_cast<ref>(lhs).number() == quantity_cast<ref>(rhs).number();
|
||||
return quantity<ref, typename Q1::rep>(lhs).number() == quantity<ref, typename Q2::rep>(rhs).number();
|
||||
}
|
||||
|
||||
} // namespace mp_units
|
||||
|
@ -158,7 +158,7 @@ struct named_unit<Symbol, U> : std::remove_const_t<decltype(U)> {
|
||||
* operations. Also, if the user prefers integral types for a quantity representation, this will
|
||||
* not force the user to convert to a floating-point type right away. Only when a final quantity
|
||||
* number needs to actually account for the constant value, the floating-point operation (if any)
|
||||
* can be triggered lazily with the `quantity_cast<U>()`.
|
||||
* can be triggered lazily with the `value_cast<U>()`.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
|
@ -205,7 +205,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("percents")
|
||||
{
|
||||
const auto q = quantity_cast<percent>(15. * isq::length[m] / (100. * isq::length[m]));
|
||||
const auto q = value_cast<percent>(15. * isq::length[m] / (100. * isq::length[m]));
|
||||
os << q;
|
||||
|
||||
SECTION("iostream") { CHECK(os.str() == "15 %"); }
|
||||
@ -724,7 +724,7 @@ TEST_CASE("localization with the 'L' specifier", "[text][fmt][localization]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("quantity_cast", "[text][ostream]")
|
||||
TEST_CASE("value_cast", "[text][ostream]")
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
@ -740,13 +740,13 @@ TEST_CASE("quantity_cast", "[text][ostream]")
|
||||
|
||||
SECTION("int")
|
||||
{
|
||||
os << quantity_cast<int>(q);
|
||||
os << value_cast<int>(q);
|
||||
CHECK(os.str() == "60 km/h");
|
||||
}
|
||||
|
||||
SECTION("double")
|
||||
{
|
||||
os << quantity_cast<double>(q);
|
||||
os << value_cast<double>(q);
|
||||
CHECK(os.str() == "60 km/h");
|
||||
}
|
||||
}
|
||||
@ -763,13 +763,13 @@ TEST_CASE("quantity_cast", "[text][ostream]")
|
||||
|
||||
SECTION("int")
|
||||
{
|
||||
os << quantity_cast<int>(q);
|
||||
os << value_cast<int>(q);
|
||||
CHECK(os.str() == "60 km/h");
|
||||
}
|
||||
|
||||
SECTION("double")
|
||||
{
|
||||
os << quantity_cast<double>(q);
|
||||
os << value_cast<double>(q);
|
||||
CHECK(os.str() == "60.5 km/h");
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ TEST_CASE("vector quantity", "[la]")
|
||||
SECTION("truncating")
|
||||
{
|
||||
const quantity<isq::position_vector[m], vector<int>> v{vector<int>{1001, 1002, 1003}};
|
||||
CHECK(quantity_cast<km>(v).number() == vector<int>{1, 1, 1});
|
||||
CHECK(value_cast<km>(v).number() == vector<int>{1, 1, 1});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ static_assert(std::numbers::pi * 2 * rad == 1. * rev);
|
||||
|
||||
static_assert(360_q_deg == 1_q_rev);
|
||||
static_assert(400_q_grad == 1_q_rev);
|
||||
static_assert(std::numbers::pi * quantity_cast<double>(2._q_rad) == quantity_cast<double>(1._q_rev));
|
||||
static_assert(std::numbers::pi * value_cast<double>(2._q_rad) == value_cast<double>(1._q_rev));
|
||||
|
||||
static_assert(mp_units::aliases::deg<>(360.) == mp_units::aliases::rev<>(1.));
|
||||
static_assert(mp_units::aliases::deg<int>(360) == mp_units::aliases::rev<int>(1));
|
||||
|
Reference in New Issue
Block a user