feat: 💥 initial implementation of implicit point origins

This commit is contained in:
Mateusz Pusz
2023-12-21 12:25:09 +01:00
parent a114b9b9a3
commit 10a2e6dc0d
8 changed files with 108 additions and 69 deletions

View File

@@ -33,8 +33,6 @@ inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
QUANTITY_SPEC(currency, dim_currency);
constexpr struct zero : absolute_point_origin<zero, currency> {} zero;
inline constexpr struct euro : named_unit<"EUR", kind_of<currency>> {} euro;
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
inline constexpr struct great_british_pound : named_unit<"GBP", kind_of<currency>> {} great_british_pound;
@@ -90,18 +88,19 @@ quantity<To, Rep> exchange_to(quantity<From, Rep> q)
template<ReferenceOf<currency> auto To, ReferenceOf<currency> auto From, auto PO, typename Rep>
quantity_point<To, PO, Rep> exchange_to(quantity_point<From, PO, Rep> q)
{
return quantity_point{zero + static_cast<Rep>(exchange_rate<q.unit, get_unit(To)>() *
(q - q.absolute_point_origin).numerical_value_in(q.unit)) *
To};
return quantity_point{
static_cast<Rep>(exchange_rate<q.unit, get_unit(To)>() * (q - q.absolute_point_origin).numerical_value_in(q.unit)) *
To};
}
int main()
{
using namespace unit_symbols;
quantity_point price_usd = zero + 100 * USD;
quantity_point price_usd{100 * USD};
quantity_point price_euro = exchange_to<euro>(price_usd);
std::cout << price_usd.quantity_from(zero) << " -> " << price_euro.quantity_from(zero) << "\n";
// std::cout << price_usd.quantity_from(zero) + price_euro.quantity_from(zero) << "\n"; // does not compile
std::cout << price_usd.quantity_from_zero() << " -> " << price_euro.quantity_from_zero() << "\n";
// std::cout << price_usd.quantity_from_zero() + price_euro.quantity_from_zero() << "\n"; // does
// not compile
}

View File

@@ -71,13 +71,28 @@ template<PointOrigin PO1, PointOrigin PO2>
else if constexpr (detail::RelativePointOrigin<PO1> && detail::RelativePointOrigin<PO2>)
return PO1::quantity_point == PO2::quantity_point;
else if constexpr (detail::RelativePointOrigin<PO1>)
return detail::same_absolute_point_origins(po1, po2) &&
return same_absolute_point_origins(po1, po2) &&
detail::is_eq_zero(PO1::quantity_point.quantity_from(PO1::quantity_point.absolute_point_origin));
else if constexpr (detail::RelativePointOrigin<PO2>)
return detail::same_absolute_point_origins(po1, po2) &&
return same_absolute_point_origins(po1, po2) &&
detail::is_eq_zero(PO2::quantity_point.quantity_from(PO2::quantity_point.absolute_point_origin));
}
template<QuantitySpec auto QS>
struct implicit_zeroth_point_origin_ : absolute_point_origin<implicit_zeroth_point_origin_<QS>, QS> {};
template<QuantitySpec auto QS>
inline constexpr implicit_zeroth_point_origin_<QS> implicit_zeroth_point_origin;
template<Reference R>
[[nodiscard]] consteval PointOriginFor<get_quantity_spec(R{})> auto zeroth_point_origin(R)
{
if constexpr (requires { get_unit(R{}).point_origin; })
return get_unit(R{}).point_origin;
else
return implicit_zeroth_point_origin<get_quantity_spec(R{})>;
}
namespace detail {
template<PointOrigin PO>
@@ -100,7 +115,7 @@ template<PointOrigin PO>
* @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, PointOriginFor<get_quantity_spec(R)> auto PO,
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = zeroth_point_origin(R),
RepresentationOf<get_quantity_spec(R).character> Rep = double>
class quantity_point {
public:
@@ -135,7 +150,14 @@ public:
quantity_point(quantity_point&&) = default;
template<typename Q>
requires std::same_as<std::remove_cvref_t<Q>, quantity_type>
requires QuantityOf<std::remove_cvref_t<Q>, get_quantity_spec(R)> && std::constructible_from<quantity_type, Q> &&
(point_origin == zeroth_point_origin(R)) && (point_origin == zeroth_point_origin(Q::reference))
constexpr explicit quantity_point(Q&& q) : quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
{
}
template<typename Q>
requires QuantityOf<std::remove_cvref_t<Q>, get_quantity_spec(R)> && std::constructible_from<quantity_type, Q>
constexpr quantity_point(Q&& q, std::remove_const_t<decltype(PO)>) :
quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
{
@@ -219,6 +241,20 @@ public:
return *this - PO2{};
}
// returns always a value relative to the unit's zero
// available only if point is defined in terms of a unit's zero point origin
[[nodiscard]] constexpr Quantity auto quantity_from_zero() const
requires(detail::same_absolute_point_origins(absolute_point_origin, zeroth_point_origin(R)))
{
// original quantity point unit can be lost in the below operation
const auto q = quantity_from(zeroth_point_origin(R));
if constexpr (requires { q.in(unit); })
// restore it if possible (non-truncating)
return q.in(unit);
else
return q;
}
// unit conversions
template<UnitCompatibleWith<unit, quantity_spec> U>
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
@@ -322,6 +358,9 @@ public:
};
// CTAD
template<Quantity Q>
quantity_point(Q q) -> quantity_point<Q::reference, zeroth_point_origin(Q::reference), typename Q::rep>;
template<Quantity Q, PointOriginFor<Q::quantity_spec> PO>
quantity_point(Q q, PO) -> quantity_point<Q::reference, PO{}, typename Q::rep>;

View File

@@ -29,6 +29,7 @@
#include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/get_associated_quantity.h>
#include <mp-units/bits/magnitude.h>
#include <mp-units/bits/quantity_point_concepts.h>
#include <mp-units/bits/quantity_spec_concepts.h>
#include <mp-units/bits/ratio.h>
#include <mp-units/bits/symbol_text.h>
@@ -116,6 +117,14 @@ struct named_unit<Symbol, QS> {
static constexpr auto quantity_spec = QS;
};
template<basic_symbol_text Symbol, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
requires(!Symbol.empty()) && detail::BaseDimension<std::remove_const_t<decltype(QS.dimension)>>
struct named_unit<Symbol, QS, PO> {
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
static constexpr auto quantity_spec = QS;
static constexpr auto point_origin = PO;
};
/**
* @brief Specialization for a unit that can be reused by several base quantities
*
@@ -132,6 +141,13 @@ struct named_unit<Symbol> {
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
};
template<basic_symbol_text Symbol, PointOrigin auto PO>
requires(!Symbol.empty())
struct named_unit<Symbol, PO> {
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
static constexpr auto point_origin = PO;
};
/**
* @brief Specialization for a unit with special name
*
@@ -146,6 +162,13 @@ struct named_unit<Symbol, U> : std::remove_const_t<decltype(U)> {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
};
template<basic_symbol_text Symbol, Unit auto U, PointOrigin auto PO>
requires(!Symbol.empty())
struct named_unit<Symbol, U, PO> : std::remove_const_t<decltype(U)> {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
static constexpr auto point_origin = PO;
};
/**
* @brief Specialization for a unit with special name valid only for a specific quantity
*
@@ -162,6 +185,14 @@ struct named_unit<Symbol, U, QS> : std::remove_const_t<decltype(U)> {
static constexpr auto quantity_spec = QS;
};
template<basic_symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
struct named_unit<Symbol, U, QS, PO> : std::remove_const_t<decltype(U)> {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
static constexpr auto quantity_spec = QS;
static constexpr auto point_origin = PO;
};
/**
* @brief A prefixed unit
*
@@ -286,14 +317,14 @@ canonical_unit(M, U) -> canonical_unit<M, U>;
#endif
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&);
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q, Args...>&);
template<Unit T, basic_symbol_text Symbol>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&);
template<Unit T, basic_symbol_text Symbol, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&);
template<Unit T, basic_symbol_text Symbol, Unit auto U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&);
template<Unit T, basic_symbol_text Symbol, Unit auto U, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&);
template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&);
@@ -308,20 +339,20 @@ template<Unit T, auto M, typename U>
return canonical_unit{M * base.mag, base.reference_unit};
}
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&)
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q, Args...>&)
{
return canonical_unit{mag<1>, t};
}
template<Unit T, basic_symbol_text Symbol>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&)
template<Unit T, basic_symbol_text Symbol, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&)
{
return canonical_unit{mag<1>, t};
}
template<Unit T, basic_symbol_text Symbol, Unit auto U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&)
template<Unit T, basic_symbol_text Symbol, Unit auto U, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&)
{
return get_canonical_unit_impl(U, U);
}

View File

@@ -1,38 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/quantity_point.h>
#include <mp-units/systems/si/units.h>
namespace mp_units::si {
// clang-format off
inline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;
inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;
inline constexpr struct ice_point : relative_point_origin<absolute_zero + 273.15 * kelvin> {} ice_point;
inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;
// clang-format on
} // namespace mp_units::si

View File

@@ -23,7 +23,6 @@
#pragma once
#include <mp-units/systems/si/constants.h>
#include <mp-units/systems/si/point_origins.h>
#include <mp-units/systems/si/prefixes.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <mp-units/systems/si/units.h>

View File

@@ -22,6 +22,7 @@
#pragma once
#include <mp-units/quantity_point.h>
#include <mp-units/systems/isq/atomic_and_nuclear_physics.h>
#include <mp-units/systems/isq/base_quantities.h>
#include <mp-units/systems/isq/space_and_time.h>
@@ -39,7 +40,11 @@ inline constexpr struct metre : named_unit<"m", kind_of<isq::length>> {} metre;
inline constexpr struct gram : named_unit<"g", kind_of<isq::mass>> {} gram;
inline constexpr struct kilogram : decltype(kilo<gram>) {} kilogram;
inline constexpr struct ampere : named_unit<"A", kind_of<isq::electric_current>> {} ampere;
inline constexpr struct kelvin : named_unit<"K", kind_of<isq::thermodynamic_temperature>> {} kelvin;
inline constexpr struct absolute_zero : absolute_point_origin<absolute_zero, isq::thermodynamic_temperature> {} absolute_zero;
inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin;
inline constexpr struct kelvin : named_unit<"K", kind_of<isq::thermodynamic_temperature>, zeroth_kelvin> {} kelvin;
inline constexpr struct mole : named_unit<"mol", kind_of<isq::amount_of_substance>> {} mole;
inline constexpr struct candela : named_unit<"cd", kind_of<isq::luminous_intensity>> {} candela;
@@ -68,7 +73,11 @@ inline constexpr struct siemens : named_unit<"S", one / ohm> {} siemens;
inline constexpr struct weber : named_unit<"Wb", volt * second> {} weber;
inline constexpr struct tesla : named_unit<"T", weber / square(metre)> {} tesla;
inline constexpr struct henry : named_unit<"H", weber / ampere> {} henry;
inline constexpr struct degree_Celsius : named_unit<basic_symbol_text{"°C", "`C"}, kelvin> {} degree_Celsius;
inline constexpr struct ice_point : relative_point_origin<quantity_point{273.15 * kelvin}> {} ice_point;
inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius;
inline constexpr struct degree_Celsius : named_unit<basic_symbol_text{"°C", "`C"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius;
inline constexpr struct lumen : named_unit<"lm", candela * steradian> {} lumen;
inline constexpr struct lux : named_unit<"lx", lumen / square(metre)> {} lux;
inline constexpr struct becquerel : named_unit<"Bq", one / second, kind_of<isq::activity>> {} becquerel;

View File

@@ -110,9 +110,9 @@ inline constexpr struct troy_pound : named_unit<"lb t", mag<12> * troy_once> {}
inline constexpr struct inch_of_mercury : named_unit<"inHg", mag<ratio(3'386'389, 1'000)> * si::pascal> {} inch_of_mercury;
// https://en.wikipedia.org/wiki/United_States_customary_units#Temperature
inline constexpr struct degree_Fahrenheit : named_unit<basic_symbol_text{"°F", "`F"}, mag<ratio{5, 9}> * si::degree_Celsius> {} degree_Fahrenheit;
inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin<si::zeroth_degree_Celsius - 32 * (mag<ratio{5, 9}> * si::degree_Celsius)> {} zeroth_degree_Fahrenheit;
inline constexpr struct degree_Fahrenheit : named_unit<basic_symbol_text{"°F", "`F"}, mag<ratio{5, 9}> * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_Fahrenheit;
inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin<si::zeroth_degree_Celsius - 32 * degree_Fahrenheit> {} zeroth_degree_Fahrenheit;
// clang-format on
namespace unit_symbols {

View File

@@ -253,7 +253,7 @@ static_assert(quantity_point<si::degree_Celsius, si::ice_point>::dimension == is
static_assert(quantity_point<si::degree_Celsius, si::ice_point>::unit == si::degree_Celsius);
static_assert(is_of_type<quantity_point<si::degree_Celsius, si::ice_point>::point_origin, struct si::ice_point>);
static_assert(
is_of_type<quantity_point<si::degree_Celsius, si::ice_point>::absolute_point_origin, struct si::absolute_zero>);
is_of_type<quantity_point<si::degree_Celsius, si::ice_point>::absolute_point_origin, struct si::zeroth_kelvin>);
static_assert(quantity_point<isq::Celsius_temperature[si::degree_Celsius], si::ice_point>::reference ==
isq::Celsius_temperature[si::degree_Celsius]);
@@ -266,7 +266,7 @@ static_assert(is_of_type<quantity_point<isq::Celsius_temperature[si::degree_Cels
struct si::ice_point>);
static_assert(
is_of_type<quantity_point<isq::Celsius_temperature[si::degree_Celsius], si::ice_point>::absolute_point_origin,
struct si::absolute_zero>);
struct si::zeroth_kelvin>);
//////////////////