Skip to content

Commit

Permalink
fix: add quantity_point::origin, like std::chrono::time_point::clock
Browse files Browse the repository at this point in the history
  • Loading branch information
JohelEGP committed Jun 28, 2021
1 parent 6af61af commit fb29d42
Show file tree
Hide file tree
Showing 26 changed files with 545 additions and 211 deletions.
4 changes: 2 additions & 2 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ CommentPragmas: '^ NOLINT'
# CompactNamespaces: false
# ConstructorInitializerAllOnOneLineOrOnePerLine: true
# ConstructorInitializerIndentWidth: 4
# ContinuationIndentWidth: 4
ContinuationIndentWidth: 2
# Cpp11BracedListStyle: true
DeriveLineEnding: false
DerivePointerAlignment: false
Expand Down Expand Up @@ -109,7 +109,7 @@ IndentRequires: true
# KeepEmptyLinesAtTheStartOfBlocks: false
# MacroBlockBegin: ''
# MacroBlockEnd: ''
# MaxEmptyLinesToKeep: 1
MaxEmptyLinesToKeep: 2
# NamespaceIndentation: None
# ObjCBinPackProtocolList: Never
# ObjCBlockIndentWidth: 2
Expand Down
Binary file modified docs/_static/img/concepts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 8 additions & 4 deletions docs/framework/basic_concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The most important concepts in the library are `Unit`, `Dimension`,
]

[<abstract>QuantityPoint|
[quantity_point<Dimension, Unit, Rep>]
[quantity_point<PointOrigin, Unit, Rep>]
]

[<abstract>QuantityKind|
Expand All @@ -39,13 +39,17 @@ The most important concepts in the library are `Unit`, `Dimension`,
[Unit]<-[Quantity]
[Quantity]<-[QuantityPoint]

[<abstract>PointOrigin]<-[QuantityPoint]
[Dimension]<-[PointOrigin]
[PointOrigin]<-[PointKind]

[<abstract>Kind]<-[QuantityKind]
[Dimension]<-[Kind]
[Quantity]<-[QuantityKind]

[<abstract>PointKind]<-[QuantityPointKind]
[Kind]<-[PointKind]
[QuantityKind]<-[QuantityPointKind]
[QuantityKind]-<[QuantityPointKind]

`Unit` is a basic building block of the library. Every dimension works with
a concrete hierarchy of units. Such hierarchy defines a reference unit and
Expand All @@ -60,14 +64,14 @@ derived dimensions. Examples: ``si::dim_time``, ``si::dim_length``, ``si::dim_sp
specific representation. Examples: ``quantity<si::dim_time, si::second, int>``,
``si::length<si::metre, int>``, ``si::speed<si::kilometre_per_hour>``.

`QuantityPoint` is an absolute `Quantity` with respect to some origin.
`QuantityPoint` is an absolute `Quantity` with respect to an origin.
Examples: timestamp (as opposed to duration), absolute temperature
(as opposed to temperature difference).

`QuantityKind` is a `Quantity` with more specific usage. Examples:
distance (``horizonal_kind``) and height (``vertical_kind``) are different kinds
of a length quantity.

`QuantityPointKind` is an absolute `QuantityKind` with respect to some origin.
`QuantityPointKind` is an absolute `QuantityKind` with respect to an origin.
Examples: altitude is a quantity point of ``vertical_kind`` (as opposed to
height).
14 changes: 9 additions & 5 deletions docs/framework/dimensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ Quantity Points
+++++++++++++++

Quantity points have a more restricted set of operations.
Quantity can be added to or subtracted from a quantity point.
The result will always be a quantity point of the same dimension:
Quantity can be added to or subtracted
from a quantity point of the same origin.
The result will always be a quantity point of the same origin:

.. code-block::
:emphasize-lines: 3-5
Expand All @@ -132,9 +133,10 @@ The result is a relative quantity of the same dimension:

It is not allowed to:

- add quantity points
- subtract a quantity point from a quantity:
- multiply nor divide quantity points with anything else.
- add quantity points,
- subtract a quantity point from a quantity,
- multiply nor divide quantity points with anything else, and
- mix quantity points with different origins:

.. code-block::
:emphasize-lines: 3-5
Expand All @@ -144,6 +146,8 @@ The result is a relative quantity of the same dimension:
auto res1 = quantity_point{dist1} + quantity_point{dist2}; // ERROR
auto res2 = dist1 - quantity_point{dist2}; // ERROR
auto res3 = quantity_point{dist1} / (2 * s); // ERROR
auto res4 = quantity_point{std::chrono::utc_second{1s}} +
quantity_point{std::chrono::sys_second{1s}}; // ERROR
Quantity Point Kinds
++++++++++++++++++++
Expand Down
21 changes: 16 additions & 5 deletions docs/framework/quantity_points.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@
Quantity Points
===============

A quantity point is an absolute quantity with respect to zero
(which represents some origin) and is represented in the library with a
`quantity_point` class template.
A quantity point is an absolute quantity with respect to an origin
and is represented in the library with a `quantity_point` class template.

Point Origins
-------------

We need a `point_origin` to represent the origin of a quantity point::

struct mean_sea_level : point_origin<si::dim_length> {};

Quantities points with this origin represent a point from the mean sea level.

The library offers a `dynamic_origin<Dimension>`
for quantity points whose origin is not specified in the type system.


Construction
Expand All @@ -14,7 +25,7 @@ Construction
To create the quantity point object from a `quantity` we just have to pass
the value to the `quantity_point` class template explicit constructor::

quantity_point<si::dim_length, si::kilometre, double> d(123 * km);
quantity_point<dynamic_origin<si::dim_length>, si::kilometre, double> d(123 * km);

.. note::

Expand All @@ -25,7 +36,7 @@ the value to the `quantity_point` class template explicit constructor::
`copy initialization <https://en.cppreference.com/w/cpp/language/copy_initialization>`_
**does not compile**::

quantity_point<si::dim_length, si::kilometre, double> d = 123 * km; // ERROR
quantity_point<dynamic_origin<si::dim_length>, si::kilometre, double> d = 123 * km; // ERROR


Differences To Quantity
Expand Down
5 changes: 3 additions & 2 deletions docs/use_cases/interoperability.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ such an explicit conversion::

For external quantity point-like types, `quantity_point_like_traits` is also provided.
It works just like `quantity_like_traits`, except that
``number(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value.
``number(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value
and ``dimension`` is replaced with ``origin``.

Similar to `quantity` and `quantity_kind`, `quantity_point` and `quantity_kind_point`
provide a deduction guide from `QuantityPointLike`::

using namespace std::chrono_literals;

static_assert(quantity_point{std::chrono::sys_seconds{1s}} + 1 * s == quantity_point{2s});
static_assert((quantity_point{std::chrono::sys_seconds{1s}} + 1 * s).relative() == 2s);
3 changes: 2 additions & 1 deletion example/glide_computer/include/glide_computer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <units/quantity_point_kind.h>
// IWYU pragma: end_exports

#include <units/chrono.h>
#include <units/format.h>
#include <units/math.h> // IWYU pragma: keep
#include <algorithm>
Expand Down Expand Up @@ -88,7 +89,7 @@ using altitude = units::quantity_point_kind<vertical_point_kind, units::isq::si:

// time
using duration = units::isq::si::time<units::isq::si::second>;
using timestamp = units::quantity_point<units::isq::si::dim_time, units::isq::si::second>;
using timestamp = units::quantity_point<units::clock_origin<std::chrono::system_clock>, units::isq::si::second>;

// speed
using velocity = units::quantity_kind<velocity_kind, units::isq::si::kilometre_per_hour>;
Expand Down
57 changes: 54 additions & 3 deletions src/core/include/units/bits/basic_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,52 @@ concept UnitOf =
Dimension<D> &&
std::same_as<typename U::reference, typename dimension_unit<D>::reference>;

// PointOrigin

template<Dimension D>
struct point_origin;

/**
* @brief A concept matching a point origin
*
* Satisfied by types derived from an specialization of @c point_origin.
*/
template<typename T>
concept PointOrigin = is_derived_from_specialization_of<T, point_origin> &&
requires {
typename T::dimension;
requires Dimension<typename T::dimension>;
typename T::point_origin;
requires std::same_as<typename T::point_origin, point_origin<typename T::dimension>>;
requires !std::same_as<T, point_origin<typename T::dimension>>;
};

// RebindablePointOriginFor

namespace detail {

template<typename O, typename D>
struct rebind_point_origin_dimension_impl {
using type = typename O::template rebind<D>;
};

} // namespace detail

template<PointOrigin O, Dimension D>
using rebind_point_origin_dimension = typename conditional<is_same_v<typename O::dimension, D>, std::type_identity<O>,
detail::rebind_point_origin_dimension_impl<O, D>>::type;

/**
* @brief A concept predicating the possibility of changing an origin's dimension
*
* Satisfied by point origins whose dimension can be made to be `D`.
*/
template<typename T, typename D>
concept RebindablePointOriginFor =
requires { typename rebind_point_origin_dimension<T, D>; } &&
PointOrigin<rebind_point_origin_dimension<T, D>> &&
std::same_as<D, typename rebind_point_origin_dimension<T, D>::dimension>;

// Kind
namespace detail {

Expand All @@ -216,7 +262,7 @@ struct _kind_base;
template<typename T, template<typename...> typename Base>
concept kind_impl_ =
is_derived_from_specialization_of<T, Base> &&
requires(T* t) {
requires {
typename T::base_kind;
typename T::dimension;
requires Dimension<typename T::dimension>;
Expand All @@ -236,7 +282,7 @@ concept Kind =
// PointKind
namespace detail {

template<Kind>
template<Kind, PointOrigin>
struct _point_kind_base;

} // namespace detail
Expand All @@ -247,7 +293,12 @@ struct _point_kind_base;
* Satisfied by all point kind types derived from an specialization of @c point_kind.
*/
template<typename T>
concept PointKind = kind_impl_<T, detail::_point_kind_base>;
concept PointKind =
kind_impl_<T, detail::_point_kind_base> &&
requires { typename T::origin; } &&
PointOrigin<typename T::origin> &&
std::same_as<typename T::dimension, typename T::base_kind::dimension> &&
std::same_as<typename T::dimension, typename T::origin::dimension>;

// Reference
namespace detail {
Expand Down
5 changes: 3 additions & 2 deletions src/core/include/units/bits/common_quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace units {
template<Dimension D, UnitOf<D> U, Representation Rep>
class quantity;

template<Dimension D, UnitOf<D> U, Representation Rep>
template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
class quantity_point;

template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
Expand Down Expand Up @@ -99,7 +99,8 @@ template<units::QuantityPoint QP1, units::QuantityPointEquivalentTo<QP1> QP2>
requires requires { typename common_type_t<typename QP1::rep, typename QP2::rep>; }
struct common_type<QP1, QP2> {
using type = units::quantity_point<
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::dimension,
units::rebind_point_origin_dimension<typename QP1::origin,
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::dimension>,
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::unit,
typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::rep>;
};
Expand Down
31 changes: 25 additions & 6 deletions src/core/include/units/bits/equivalent.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,29 +81,48 @@ template<Unit U1, Dimension D1, Unit U2, Dimension D2>
struct equivalent_unit : std::disjunction<equivalent_impl<U1, U2>,
std::bool_constant<U1::ratio / dimension_unit<D1>::ratio == U2::ratio / dimension_unit<D2>::ratio>> {};

// point origins

template<PointOrigin T, PointOrigin U>
struct equivalent_impl<T, U> : std::bool_constant<requires { // TODO: Simplify when Clang catches up.
requires RebindablePointOriginFor<T, typename U::dimension> && RebindablePointOriginFor<U, typename T::dimension> &&
std::same_as<T, rebind_point_origin_dimension<U, typename T::dimension>> &&
std::same_as<U, rebind_point_origin_dimension<T, typename U::dimension>>;
} && equivalent_impl<typename T::dimension, typename U::dimension>::value> {};


// (point) kinds

template<typename T, typename U>
requires (Kind<T> && Kind<U>) || (PointKind<T> && PointKind<U>)
template<Kind T, Kind U>
struct equivalent_impl<T, U> :
std::conjunction<std::is_same<typename T::base_kind, typename U::base_kind>,
equivalent_impl<typename T::dimension, typename U::dimension>> {};

template<PointKind T, PointKind U>
struct equivalent_impl<T, U> :
std::conjunction<equivalent_impl<typename T::base_kind, typename U::base_kind>,
equivalent_impl<typename T::origin, typename U::origin>> {};


// quantities, quantity points, quantity (point) kinds

template<typename Q1, typename Q2>
requires (Quantity<Q1> && Quantity<Q2>) || (QuantityPoint<Q1> && QuantityPoint<Q2>)
template<Quantity Q1, Quantity Q2>
struct equivalent_impl<Q1, Q2> : std::conjunction<equivalent_impl<typename Q1::dimension, typename Q2::dimension>,
equivalent_unit<typename Q1::unit, typename Q1::dimension,
typename Q2::unit, typename Q2::dimension>> {};

template<typename QK1, typename QK2>
requires (QuantityKind<QK1> && QuantityKind<QK2>) || (QuantityPointKind<QK1> && QuantityPointKind<QK2>)
template<QuantityPoint QP1, QuantityPoint QP2>
struct equivalent_impl<QP1, QP2> : std::conjunction<equivalent_impl<typename QP1::quantity_type, typename QP2::quantity_type>,
equivalent_impl<typename QP1::origin, typename QP2::origin>> {};

template<QuantityKind QK1, QuantityKind QK2>
struct equivalent_impl<QK1, QK2> : std::conjunction<equivalent_impl<typename QK1::kind_type, typename QK2::kind_type>,
equivalent_impl<typename QK1::quantity_type, typename QK2::quantity_type>> {};

template<QuantityPointKind QPK1, QuantityPointKind QPK2>
struct equivalent_impl<QPK1, QPK2> : std::conjunction<equivalent_impl<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>,
equivalent_impl<typename QPK1::origin, typename QPK2::origin>> {};

} // namespace detail

template<typename T, typename U>
Expand Down
13 changes: 6 additions & 7 deletions src/core/include/units/bits/quantity_of.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,20 @@ template<typename Q1, typename Q2>
concept QuantityEquivalentTo = Quantity<Q1> && QuantityOf<Q2, typename Q1::dimension>;

/**
* @brief A concept matching all quantity points with provided dimension
* @brief A concept matching all quantity points of the provided origin
*
* Satisfied by all quantity points with a dimension being the instantiation derived from
* the provided dimension type.
* Satisfied by all quantity points with an origin equivalent to the provided one.
*/
template<typename QP, typename Dim>
concept QuantityPointOf = QuantityPoint<QP> && Dimension<Dim> && equivalent<typename QP::dimension, Dim>;
template<typename QP, typename Orig>
concept QuantityPointOf = QuantityPoint<QP> && PointOrigin<Orig> && equivalent<typename QP::origin, Orig>;

/**
* @brief A concept matching two equivalent quantity points
*
* Satisfied by quantity points having equivalent dimensions.
* Satisfied by quantity points having equivalent origins.
*/
template<typename QP1, typename QP2>
concept QuantityPointEquivalentTo = QuantityPoint<QP1> && QuantityPointOf<QP2, typename QP1::dimension>;
concept QuantityPointEquivalentTo = QuantityPoint<QP1> && QuantityPointOf<QP2, typename QP1::origin>;

/**
* @brief A concept matching only quantity kinds of a specific kind.
Expand Down
15 changes: 13 additions & 2 deletions src/core/include/units/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <units/customization_points.h>
// IWYU pragma: begin_exports
#include <units/isq/si/time.h>
#include <units/point_origin.h>
#include <chrono>
// IWYU pragma: end_exports

Expand All @@ -38,15 +39,25 @@ struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
[[nodiscard]] static constexpr rep number(const std::chrono::duration<Rep, Period>& q) { return q.count(); }
};

template<typename C>
struct clock_origin : point_origin<isq::si::dim_time> { };

template<typename C, typename Rep, typename Period>
struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> {
using dimension = isq::si::dim_time;
using unit = downcast_unit<dimension, ratio(Period::num, Period::den)>;
using origin = clock_origin<C>;
using unit = downcast_unit<typename origin::dimension, ratio(Period::num, Period::den)>;
using rep = Rep;
[[nodiscard]] static constexpr auto relative(
const std::chrono::time_point<C, std::chrono::duration<Rep, Period>>& qp) {
return qp.time_since_epoch();
}
};

namespace detail {

template<typename C, typename Rep, typename Period>
inline constexpr bool is_quantity_point_like<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> = true;

} // namespace detail

} // namespace units
Loading

0 comments on commit fb29d42

Please sign in to comment.