Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add quantity_point::origin, like std::chrono::time_point::clock #288

Merged
merged 1 commit into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
10 changes: 7 additions & 3 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,6 +39,10 @@ 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]
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;
Comment on lines +58 to +59
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why this was missing, as is is_quantity_like<duration>, or why it worked before. But now this one didn't, so I added this.


} // namespace detail

} // namespace units
2 changes: 1 addition & 1 deletion src/core/include/units/customization_points.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ struct quantity_like_traits;
/**
* @brief Provides support for external quantity point-like types
*
* The type trait should provide the following nested type aliases: @c dimension, @c unit, @c rep,
* The type trait should provide the following nested type aliases: @c origin, @c unit, @c rep,
* and a static member function @c relative(T) that will return the quantity-like value of the quantity point.
*
* Usage example can be found in @c units/chrono.h header file.
Expand Down
Loading