Skip to content

Commit

Permalink
Merge pull request #495 from mpusz/interop
Browse files Browse the repository at this point in the history
feat: interoperability with other libraries redesigned
  • Loading branch information
mpusz authored Oct 1, 2023
2 parents 335ebc9 + b12b9f8 commit e9387c4
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 48 deletions.
46 changes: 37 additions & 9 deletions docs/users_guide/framework_basics/basic_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,12 @@ for which an instantiation of `quantity_like_traits` type trait yields a valid t
- Static data member `reference` that matches the [`Reference`](#Reference) concept,
- `rep` type that matches [`RepresentationOf`](#RepresentationOf) concept with the character provided
in `reference`,
- `value(T)` static member function returning a raw value of the quantity.
- `to_numerical_value(T)` static member function returning a raw value of the quantity packed in
either `convert_explicitly` or `convert_implicitly` wrapper that enables implicit conversion in
the latter case,
- `from_numerical_value(rep)` static member function returning `T` packed in either `convert_explicitly`
or `convert_implicitly` wrapper that enables implicit conversion in the latter case.


??? abstract "Examples"

Expand All @@ -438,10 +443,20 @@ for which an instantiation of `quantity_like_traits` type trait yields a valid t
struct mp_units::quantity_like_traits<std::chrono::seconds> {
static constexpr auto reference = si::second;
using rep = std::chrono::seconds::rep;
[[nodiscard]] static constexpr rep value(const std::chrono::seconds& q) { return q.count(); }

[[nodiscard]] static constexpr convert_implicitly<rep> to_numerical_value(const std::chrono::seconds& q)
{
return q.count();
}

[[nodiscard]] static constexpr convert_implicitly<std::chrono::seconds> from_numerical_value(const rep& v)
{
return std::chrono::seconds(v);
}
};

quantity q(42s);
quantity q = 42s;
std::chrono::seconds dur = 42 * s;
```


Expand All @@ -454,8 +469,13 @@ for which an instantiation of `quantity_point_like_traits` type trait yields a v
- Static data member `point_origin` that matches the [`PointOrigin`](#PointOrigin) concept
- `rep` type that matches [`RepresentationOf`](#RepresentationOf) concept with the character provided
in `reference`
- `quantity_from_origin(T)` static member function returning the `quantity` being the offset of the point
from the origin
- `to_quantity(T)` static member function returning the `quantity` being the offset of the point
from the origin packed in either `convert_explicitly` or `convert_implicitly` wrapper that enables
implicit conversion in the latter case,
- `from_quantity(quantity<reference, rep>)` static member function returning `T` packed in either
`convert_explicitly` or `convert_implicitly` wrapper that enables implicit conversion in the latter
case.


??? abstract "Examples"

Expand All @@ -464,14 +484,22 @@ for which an instantiation of `quantity_point_like_traits` type trait yields a v
```cpp
template<typename C>
struct mp_units::quantity_point_like_traits<std::chrono::time_point<C, std::chrono::seconds>> {
using T = std::chrono::time_point<C, std::chrono::seconds>;
static constexpr auto reference = si::second;
static constexpr auto point_origin = chrono_point_origin;
static constexpr struct point_origin : absolute_point_origin<isq::time> {} point_origin{};
using rep = std::chrono::seconds::rep;
[[nodiscard]] static constexpr auto quantity_from_origin(const std::chrono::time_point<C, std::chrono::seconds>& qp)

[[nodiscard]] static constexpr convert_implicitly<quantity<reference, rep>> to_quantity(const T& qp)
{
return quantity{qp.time_since_epoch()};
}

[[nodiscard]] static constexpr convert_implicitly<T> from_quantity(const quantity<reference, rep>& q)
{
return quantity{std::chrono::duration_cast<std::chrono::seconds>(qp.time_since_epoch())};
return T(q);
}
};

quantity_point qp(time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
quantity_point qp = time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
std::chrono::sys_seconds q = qp + 42 * s;
```
10 changes: 7 additions & 3 deletions src/core/include/mp-units/bits/quantity_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,19 @@ concept QuantityOf = Quantity<Q> && ReferenceOf<std::remove_const_t<decltype(Q::
* all quantity-specific information.
*/
template<typename T>
concept QuantityLike = requires(T q) {
concept QuantityLike = requires {
quantity_like_traits<T>::reference;
requires Reference<std::remove_const_t<decltype(quantity_like_traits<T>::reference)>>;
typename quantity_like_traits<T>::rep;
requires RepresentationOf<typename quantity_like_traits<T>::rep,
get_quantity_spec(quantity_like_traits<T>::reference).character>;
} && requires(T q, typename quantity_like_traits<T>::rep v) {
{
make_quantity<quantity_like_traits<T>::reference>(quantity_like_traits<T>::value(q))
} -> std::same_as<quantity<quantity_like_traits<T>::reference, typename quantity_like_traits<T>::rep>>;
quantity_like_traits<T>::to_numerical_value(q)
} -> detail::ConversionSpecOf<typename quantity_like_traits<T>::rep>;
{
quantity_like_traits<T>::from_numerical_value(v)
} -> detail::ConversionSpecOf<T>;
};

} // namespace mp_units
16 changes: 9 additions & 7 deletions src/core/include/mp-units/bits/quantity_point_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,21 +173,23 @@ concept QuantityPointOf =
* all quantity_point-specific information.
*/
template<typename T>
concept QuantityPointLike = requires(T qp) {
concept QuantityPointLike = requires {
quantity_point_like_traits<T>::reference;
requires Reference<std::remove_const_t<decltype(quantity_point_like_traits<T>::reference)>>;
quantity_point_like_traits<T>::point_origin;
requires PointOrigin<std::remove_const_t<decltype(quantity_point_like_traits<T>::point_origin)>>;
typename quantity_point_like_traits<T>::rep;
requires RepresentationOf<typename quantity_point_like_traits<T>::rep,
get_quantity_spec(quantity_point_like_traits<T>::reference).character>;
requires Quantity<std::remove_cvref_t<decltype(quantity_point_like_traits<T>::quantity_from_origin(qp))>>;
} && requires(T qp, quantity<quantity_point_like_traits<T>::reference, typename quantity_point_like_traits<T>::rep> q) {
{
make_quantity_point<quantity_point_like_traits<T>::point_origin>(
quantity_point_like_traits<T>::quantity_from_origin(qp))
}
-> std::same_as<quantity_point<quantity_point_like_traits<T>::reference, quantity_point_like_traits<T>::point_origin,
typename quantity_point_like_traits<T>::rep>>;
quantity_point_like_traits<T>::to_quantity(qp)
} -> detail::ConversionSpecOf<
quantity<quantity_point_like_traits<T>::reference, typename quantity_point_like_traits<T>::rep>>;

{
quantity_point_like_traits<T>::from_quantity(q)
} -> detail::ConversionSpecOf<T>;
};

} // namespace mp_units
40 changes: 37 additions & 3 deletions src/core/include/mp-units/customization_points.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,42 @@ struct quantity_values {
}
};

template<typename T>
struct convert_explicitly {
using value_type = T;
T value;
constexpr explicit(false) convert_explicitly(T v) noexcept(std::is_nothrow_constructible_v<T>) : value(std::move(v))
{
}
};

template<typename T>
struct convert_implicitly {
using value_type = T;
T value;
constexpr explicit(false) convert_implicitly(T v) noexcept(std::is_nothrow_constructible_v<T>) : value(std::move(v))
{
}
};

namespace detail {

template<typename T>
concept ConversionSpec = is_specialization_of<T, convert_explicitly> || is_specialization_of<T, convert_implicitly>;

template<typename T, typename U>
concept ConversionSpecOf = ConversionSpec<T> && std::same_as<typename T::value_type, U>;

} // namespace detail

/**
* @brief Provides support for external quantity-like types
*
* The type trait should provide the @c reference object, a type alias @c rep,
* and a static member function @c value(T) that returns the raw value of the quantity.
* and static member functions @c to_numerical_value(T) that returns the raw value
* of the quantity and @c from_numerical_value(rep) that returns @c T from @c rep.
* Both return types should be encapsulated in either @c convert_explicitly or
* @c convert_implicitly to specify if the conversion is allowed to happen implicitly.
*
* Usage example can be found in @c units/chrono.h header file.
*
Expand All @@ -149,8 +180,11 @@ struct quantity_like_traits;
* @brief Provides support for external quantity point-like types
*
* The type trait should provide nested @c reference and @c origin objects,
* a type alias @c rep, and a static member function @c quantity_from_origin(T) that will
* return the quantity being the offset of the point from the origin.
* a type alias @c rep, and static member functions @c to_quantity(T) that returns
* the quantity being the offset of the point from the origin and
* @c from_quantity(quantity<reference, rep>) that returns @c T form this quantity.
* Both return types should be encapsulated in either @c convert_explicitly or
* @c convert_implicitly to specify if the conversion is allowed to happen implicitly.
*
* Usage example can be found in @c units/chrono.h header file.
*
Expand Down
33 changes: 29 additions & 4 deletions src/core/include/mp-units/quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,17 @@ class quantity {
template<QuantityLike Q>
requires detail::QuantityConvertibleTo<
quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>, quantity>
constexpr explicit quantity(const Q& q) :
quantity(make_quantity<quantity_like_traits<Q>::reference>(quantity_like_traits<Q>::value(q)))
constexpr explicit(
is_specialization_of<decltype(quantity_like_traits<Q>::to_numerical_value(std::declval<Q>())), convert_explicitly>)
quantity(const Q& q) :
quantity(make_quantity<quantity_like_traits<Q>::reference>(quantity_like_traits<Q>::to_numerical_value(q).value))
{
}

quantity& operator=(const quantity&) = default;
quantity& operator=(quantity&&) = default;

// conversions
// unit conversions
template<UnitCompatibleWith<unit, quantity_spec> U>
requires detail::QuantityConvertibleTo<quantity, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
[[nodiscard]] constexpr Quantity auto in(U) const
Expand Down Expand Up @@ -189,6 +191,27 @@ class quantity {
return (*this).force_in(U{}).numerical_value_;
}

// conversion operators
template<typename Q>
requires QuantityLike<std::remove_cvref_t<Q>>
[[nodiscard]] explicit(is_specialization_of<decltype(quantity_like_traits<Q>::from_numerical_value(numerical_value_)),
convert_explicitly>) constexpr
operator Q() const& noexcept(noexcept(quantity_like_traits<Q>::from_numerical_value(numerical_value_)) &&
std::is_nothrow_copy_constructible_v<rep>)
{
return quantity_like_traits<Q>::from_numerical_value(numerical_value_).value;
}

template<typename Q>
requires QuantityLike<std::remove_cvref_t<Q>>
[[nodiscard]] explicit(is_specialization_of<decltype(quantity_like_traits<Q>::from_numerical_value(numerical_value_)),
convert_explicitly>) constexpr
operator Q() && noexcept(noexcept(quantity_like_traits<Q>::from_numerical_value(numerical_value_)) &&
std::is_nothrow_move_constructible_v<rep>)
{
return quantity_like_traits<Q>::from_numerical_value(std::move(numerical_value_)).value;
}

// member unary operators
[[nodiscard]] constexpr Quantity auto operator+() const
requires requires(rep v) {
Expand Down Expand Up @@ -365,7 +388,9 @@ class quantity {

// CTAD
template<QuantityLike Q>
explicit quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>;
explicit(
is_specialization_of<decltype(quantity_like_traits<Q>::to_numerical_value(std::declval<Q>())), convert_explicitly>)
quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>;

// binary operators on quantities
template<auto R1, typename Rep1, auto R2, typename Rep2>
Expand Down
38 changes: 33 additions & 5 deletions src/core/include/mp-units/quantity_point.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,10 @@ class quantity_point {
std::convertible_to<
quantity<quantity_point_like_traits<QP>::reference, typename quantity_point_like_traits<QP>::rep>,
quantity_type>
constexpr explicit quantity_point(const QP& qp) :
quantity_from_origin_(quantity_point_like_traits<QP>::quantity_from_origin(qp))
constexpr explicit(
is_specialization_of<decltype(quantity_point_like_traits<QP>::to_quantity(std::declval<QP>())), convert_explicitly>)
quantity_point(const QP& qp) :
quantity_from_origin_(quantity_point_like_traits<QP>::to_quantity(qp).value)
{
}

Expand Down Expand Up @@ -168,6 +170,7 @@ class quantity_point {
return *this - PO2{};
}

// unit conversions
template<UnitCompatibleWith<unit, quantity_spec> U>
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
[[nodiscard]] constexpr QuantityPoint auto in(U) const
Expand All @@ -182,6 +185,29 @@ class quantity_point {
return make_quantity_point<PO>(quantity_ref_from(PO).force_in(U{}));
}

// conversion operators
template<typename QP>
requires QuantityPointLike<std::remove_cvref_t<QP>>
[[nodiscard]] explicit(
is_specialization_of<decltype(quantity_point_like_traits<QP>::from_quantity(quantity_from_origin_)),
convert_explicitly>) constexpr
operator QP() const& noexcept(noexcept(quantity_point_like_traits<QP>::from_quantity(quantity_from_origin_)) &&
std::is_nothrow_copy_constructible_v<rep>)
{
return quantity_point_like_traits<QP>::from_quantity(quantity_from_origin_).value;
}

template<typename QP>
requires QuantityPointLike<std::remove_cvref_t<QP>>
[[nodiscard]] explicit(
is_specialization_of<decltype(quantity_point_like_traits<QP>::from_quantity(quantity_from_origin_)),
convert_explicitly>) constexpr
operator QP() && noexcept(noexcept(quantity_point_like_traits<QP>::from_quantity(quantity_from_origin_)) &&
std::is_nothrow_move_constructible_v<rep>)
{
return quantity_point_like_traits<QP>::from_quantity(std::move(quantity_from_origin_)).value;
}

// member unary operators
template<typename QP>
friend constexpr decltype(auto) operator++(QP&& qp)
Expand Down Expand Up @@ -249,9 +275,11 @@ class quantity_point {

// CTAD
template<QuantityPointLike QP>
explicit quantity_point(QP)
-> quantity_point<quantity_point_like_traits<QP>::reference, quantity_point_like_traits<QP>::point_origin,
typename quantity_point_like_traits<QP>::rep>;
explicit(
is_specialization_of<decltype(quantity_point_like_traits<QP>::to_quantity(std::declval<QP>())), convert_explicitly>)
quantity_point(QP)
-> quantity_point<quantity_point_like_traits<QP>::reference, quantity_point_like_traits<QP>::point_origin,
typename quantity_point_like_traits<QP>::rep>;

template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
// TODO simplify when gcc catches up
Expand Down
27 changes: 23 additions & 4 deletions src/utility/include/mp-units/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,18 @@ template<typename Rep, typename Period>
struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
static constexpr auto reference = detail::time_unit_from_chrono_period<Period>();
using rep = Rep;
[[nodiscard]] static constexpr rep value(const std::chrono::duration<Rep, Period>& q) { return q.count(); }

[[nodiscard]] static constexpr convert_implicitly<rep> to_numerical_value(
const std::chrono::duration<Rep, Period>& q) noexcept(std::is_nothrow_copy_constructible_v<rep>)
{
return q.count();
}

[[nodiscard]] static constexpr convert_implicitly<std::chrono::duration<Rep, Period>> from_numerical_value(
const rep& v) noexcept(std::is_nothrow_copy_constructible_v<rep>)
{
return std::chrono::duration<Rep, Period>(v);
}
};

template<typename C>
Expand All @@ -77,22 +88,30 @@ inline constexpr chrono_point_origin_<C> chrono_point_origin;

template<typename C, typename Rep, typename Period>
struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> {
using T = std::chrono::time_point<C, std::chrono::duration<Rep, Period>>;
static constexpr auto reference = detail::time_unit_from_chrono_period<Period>();
static constexpr auto point_origin = chrono_point_origin<C>;
using rep = Rep;
[[nodiscard]] static constexpr quantity<reference, rep> quantity_from_origin(
const std::chrono::time_point<C, std::chrono::duration<Rep, Period>>& qp)

[[nodiscard]] static constexpr convert_implicitly<quantity<reference, rep>> to_quantity(const T& qp) noexcept(
std::is_nothrow_copy_constructible_v<rep>)
{
return quantity{qp.time_since_epoch()};
}

[[nodiscard]] static constexpr convert_implicitly<T> from_quantity(const quantity<reference, rep>& q) noexcept(
std::is_nothrow_copy_constructible_v<rep>)
{
return T(q);
}
};

template<QuantityOf<isq::time> Q>
[[nodiscard]] constexpr auto to_chrono_duration(const Q& q)
{
constexpr auto canonical = detail::get_canonical_unit(Q::unit);
constexpr ratio r = as_ratio(canonical.mag);
return std::chrono::duration<typename Q::rep, std::ratio<r.num, r.den>>{q.numerical_value_ref_in(Q::unit)};
return std::chrono::duration<typename Q::rep, std::ratio<r.num, r.den>>{q};
}

template<QuantityPointOf<isq::time> QP>
Expand Down
Loading

0 comments on commit e9387c4

Please sign in to comment.