Skip to content

Commit

Permalink
feat: 💥 initial implementation of implicit point origins
Browse files Browse the repository at this point in the history
  • Loading branch information
mpusz committed Dec 2, 2023
1 parent 611e380 commit b7045e7
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 108 deletions.
15 changes: 7 additions & 8 deletions example/currency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;

QUANTITY_SPEC(currency, dim_currency);

constexpr struct zero : absolute_point_origin<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;
Expand Down Expand Up @@ -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
}
7 changes: 4 additions & 3 deletions example/include/geographic.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

namespace geographic {

inline constexpr struct mean_sea_level : mp_units::absolute_point_origin<mp_units::isq::altitude> {
inline constexpr struct mean_sea_level : mp_units::absolute_point_origin<mean_sea_level, mp_units::isq::altitude> {
} mean_sea_level;

using msl_altitude = mp_units::quantity_point<mp_units::isq::altitude[mp_units::si::metre], mean_sea_level>;
Expand All @@ -64,9 +64,10 @@ struct MP_UNITS_STD_FMT::formatter<geographic::msl_altitude> : formatter<geograp

namespace geographic {

inline constexpr struct equator : mp_units::absolute_point_origin<mp_units::isq::angular_measure> {
inline constexpr struct equator : mp_units::absolute_point_origin<equator, mp_units::isq::angular_measure> {
} equator;
inline constexpr struct prime_meridian : mp_units::absolute_point_origin<mp_units::isq::angular_measure> {
inline constexpr struct prime_meridian :
mp_units::absolute_point_origin<prime_meridian, mp_units::isq::angular_measure> {
} prime_meridian;


Expand Down
4 changes: 2 additions & 2 deletions example/unmanned_aerial_vehicle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ using namespace geographic;
enum class earth_gravity_model { egm84_15, egm95_5, egm2008_1 };

template<earth_gravity_model M>
struct height_above_ellipsoid_t : absolute_point_origin<isq::altitude> {
struct height_above_ellipsoid_t : absolute_point_origin<height_above_ellipsoid_t<M>, isq::altitude> {
static constexpr earth_gravity_model egm = M;
};
template<earth_gravity_model M>
Expand Down Expand Up @@ -107,7 +107,7 @@ hae_altitude<M> to_hae(msl_altitude msl, position<long double> pos)
// **** HAL ****

// clang-format off
inline constexpr struct height_above_launch : absolute_point_origin<isq::altitude> {} height_above_launch;
inline constexpr struct height_above_launch : absolute_point_origin<height_above_launch, isq::altitude> {} height_above_launch;
// clang-format on

using hal_altitude = quantity_point<isq::altitude[si::metre], height_above_launch>;
Expand Down
10 changes: 5 additions & 5 deletions src/core/include/mp-units/bits/quantity_point_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

namespace mp_units {

template<QuantitySpec auto Q>
template<typename Derived, QuantitySpec auto QS>
struct absolute_point_origin;

namespace detail {
Expand All @@ -41,11 +41,11 @@ inline constexpr bool is_quantity_point = false;
template<typename T>
inline constexpr bool is_specialization_of_absolute_point_origin = false;

template<auto Q>
inline constexpr bool is_specialization_of_absolute_point_origin<absolute_point_origin<Q>> = true;
template<typename D, auto Q>
inline constexpr bool is_specialization_of_absolute_point_origin<absolute_point_origin<D, Q>> = true;

template<auto Q>
void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin<Q>*);
template<typename D, auto Q>
void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin<D, Q>*);

template<typename T>
inline constexpr bool is_derived_from_specialization_of_absolute_point_origin =
Expand Down
85 changes: 78 additions & 7 deletions src/core/include/mp-units/quantity_point.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,25 @@

namespace mp_units {

template<QuantitySpec auto QS>
namespace detail {

template<typename T>
requires requires {
{
T::zero()
} -> std::equality_comparable_with<T>;
}
[[nodiscard]] constexpr bool is_eq_zero(T v)
{
return v == T::zero();
}

} // namespace detail

template<typename Derived, QuantitySpec auto QS>
struct absolute_point_origin {
static constexpr QuantitySpec auto quantity_spec = QS;
using _type_ = absolute_point_origin;
};

template<QuantityPoint auto QP>
Expand All @@ -47,14 +63,45 @@ struct relative_point_origin {
static constexpr PointOrigin auto absolute_point_origin = QP.absolute_point_origin;
};

namespace detail {
template<PointOrigin PO1, PointOrigin PO2>
[[nodiscard]] consteval bool operator==(PO1 po1, PO2 po2)
{
if constexpr (detail::AbsolutePointOrigin<PO1> && detail::AbsolutePointOrigin<PO2>)
return is_same_v<typename PO1::_type_, typename PO2::_type_>;
else if constexpr (detail::RelativePointOrigin<PO1> && detail::RelativePointOrigin<PO2>)
return PO1::quantity_point == PO2::quantity_point;
else if constexpr (detail::RelativePointOrigin<PO1>)
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 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;

[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PointOrigin auto po)
template<Reference R>
[[nodiscard]] consteval PointOriginFor<get_quantity_spec(R{})> auto zeroth_point_origin(R)
{
if constexpr (requires { po.quantity_point.absolute_point_origin; })
return po.quantity_point.absolute_point_origin;
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>
[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PO po)
{
if constexpr (AbsolutePointOrigin<PO>)
return po;
else
return po.quantity_point.absolute_point_origin;
}

} // namespace detail
Expand All @@ -68,7 +115,7 @@ namespace detail {
* @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:
Expand Down Expand Up @@ -103,7 +150,14 @@ class quantity_point {
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))
{
Expand Down Expand Up @@ -186,6 +240,20 @@ class quantity_point {
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>>
Expand Down Expand Up @@ -291,6 +359,9 @@ class quantity_point {
};

// 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>;

Expand Down
55 changes: 43 additions & 12 deletions src/core/include/mp-units/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
*
Expand All @@ -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
*
Expand All @@ -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
*
Expand All @@ -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
*
Expand Down Expand Up @@ -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...>&);
Expand All @@ -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);
}
Expand Down
35 changes: 0 additions & 35 deletions src/systems/si/include/mp-units/systems/si/point_origins.h

This file was deleted.

1 change: 0 additions & 1 deletion src/systems/si/include/mp-units/systems/si/si.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Loading

0 comments on commit b7045e7

Please sign in to comment.