Skip to content

Commit

Permalink
Rewrote the type conversion system
Browse files Browse the repository at this point in the history
  • Loading branch information
jansende committed Sep 24, 2019
1 parent f32ad80 commit bbf4592
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 60 deletions.
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changes
**benri** uses [Semantic Versioning](https://semver.org/) to index its versions.

## v1.0.0 - New type conversion system
- Replaced the `is_compatible` with the new `convert` struct.
- The struct still provides a check if units should be implicitely convertible.
- It now additionally handles the actual conversion.
- This now allows us to interact with non *benri* types.

## v0.2.0 - Improved temperatures
- Added unit test for temperature conversion.
- Added unit test for affine units (via temperatures).
Expand Down
16 changes: 8 additions & 8 deletions include/benri/cmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ struct conversion_type<quantity<lhsUnit, ValueType>, quantity<rhsUnit, ValueType
AllowPoints>
{
static_assert(std::is_same<lhsUnit, rhsUnit>::value
|| type::detect_if<lhsUnit, is_compatible_with, rhsUnit>,
|| type::detect_if<lhsUnit, is_convertible_into, rhsUnit>,
"all arguments have to have the same or compatible units.");
using type =
std::conditional_t<std::is_same<lhsUnit, rhsUnit>::value
|| type::detect_if<lhsUnit, is_compatible_with, rhsUnit>,
|| type::detect_if<lhsUnit, is_convertible_into, rhsUnit>,
quantity<lhsUnit, ValueType>, void>;
};
template <class lhsUnit, class rhsUnit, class ValueType>
Expand Down Expand Up @@ -284,7 +284,7 @@ template <class xUnit, class yUnit, class zUnit, class ValueType>
const quantity<zUnit, ValueType> z) noexcept
-> std::enable_if_t<
std::is_same<multiply_units<xUnit, yUnit>, zUnit>::value
|| type::detect_if<multiply_units<xUnit, yUnit>, is_compatible_with, zUnit>,
|| type::detect_if<multiply_units<xUnit, yUnit>, is_convertible_into, zUnit>,
quantity<zUnit, ValueType>>
{
using ResultType = decltype(std::fma(x.value(), y.value(), z.value()));
Expand All @@ -296,7 +296,7 @@ template <class xUnit, class yUnit, class zUnit, class ValueType>
const quantity_point<zUnit, ValueType> z) noexcept
-> std::enable_if_t<
std::is_same<multiply_units<xUnit, yUnit>, zUnit>::value
|| type::detect_if<multiply_units<xUnit, yUnit>, is_compatible_with, zUnit>,
|| type::detect_if<multiply_units<xUnit, yUnit>, is_convertible_into, zUnit>,
quantity_point<zUnit, ValueType>>
{
using ResultType = decltype(std::fma(x.value(), y.value(), z.value()));
Expand All @@ -306,7 +306,7 @@ template <class yUnit, class zUnit, class ValueType>
[[nodiscard]] constexpr inline auto fma(ValueType x, const quantity<yUnit, ValueType> y,
const quantity<zUnit, ValueType> z) noexcept
-> std::enable_if_t<std::is_same<yUnit, zUnit>::value
|| type::detect_if<yUnit, is_compatible_with, zUnit>,
|| type::detect_if<yUnit, is_convertible_into, zUnit>,
quantity<zUnit, ValueType>>
{
using ResultType = decltype(std::fma(x, y.value(), z.value()));
Expand All @@ -316,7 +316,7 @@ template <class yUnit, class zUnit, class ValueType>
[[nodiscard]] constexpr inline auto fma(ValueType x, const quantity<yUnit, ValueType> y,
const quantity_point<zUnit, ValueType> z) noexcept
-> std::enable_if_t<std::is_same<yUnit, zUnit>::value
|| type::detect_if<yUnit, is_compatible_with, zUnit>,
|| type::detect_if<yUnit, is_convertible_into, zUnit>,
quantity_point<zUnit, ValueType>>
{
using ResultType = decltype(std::fma(x, y.value(), z.value()));
Expand All @@ -326,7 +326,7 @@ template <class xUnit, class zUnit, class ValueType>
[[nodiscard]] constexpr inline auto fma(const quantity<xUnit, ValueType> x, ValueType y,
const quantity<zUnit, ValueType> z) noexcept
-> std::enable_if_t<std::is_same<xUnit, zUnit>::value
|| type::detect_if<xUnit, is_compatible_with, zUnit>,
|| type::detect_if<xUnit, is_convertible_into, zUnit>,
quantity<zUnit, ValueType>>
{
using ResultType = decltype(std::fma(x.value(), y, z.value()));
Expand All @@ -336,7 +336,7 @@ template <class xUnit, class zUnit, class ValueType>
[[nodiscard]] constexpr inline auto fma(const quantity<xUnit, ValueType> x, ValueType y,
const quantity_point<zUnit, ValueType> z) noexcept
-> std::enable_if_t<std::is_same<xUnit, zUnit>::value
|| type::detect_if<xUnit, is_compatible_with, zUnit>,
|| type::detect_if<xUnit, is_convertible_into, zUnit>,
quantity_point<zUnit, ValueType>>
{
using ResultType = decltype(std::fma(x.value(), y, z.value()));
Expand Down
15 changes: 4 additions & 11 deletions include/benri/impl/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,13 @@ using pow_unit = unit<type::pow_list<typename Unit::dimension, Power>,
template <class lhsUnit, class rhsUnit>
using divide_units = multiply_units<lhsUnit, pow_unit<rhsUnit, std::ratio<-1>>>;
// Helper for handling units as equivalent even if the types are not.
template <class LDimension, class LPrefix, class RDimension, class RPrefix>
struct is_compatible : std::false_type
template <class From, class To>
struct convert
{
};
template <class LDimension, class LPrefix, class RDimension, class RPrefix>
constexpr bool is_compatible_v =
is_compatible<LDimension, LPrefix, RDimension, RPrefix>::value;
template <class From, class To>
using is_convertible_into = decltype(convert<From, To>{}(From{}));

template <class L, class R>
using is_compatible_with = typename std::enable_if_t<
is_compatible_v<
typename L::dimension, typename L::prefix, typename R::dimension,
typename R::
prefix> || is_compatible_v<typename R::dimension, typename R::prefix, typename L::dimension, typename L::prefix>>;
// Helper for removing a units prefix.
template <class Unit>
using drop_unit_prefix = unit<typename Unit::dimension, type::sorted_list<>>;
Expand Down
111 changes: 95 additions & 16 deletions include/benri/quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class quantity
friend class quantity;
template <class, class>
friend class quantity_point;
template <class, class>
friend struct convert;

using value_type = ValueType;
using unit_type = Unit;
Expand Down Expand Up @@ -116,29 +118,28 @@ class quantity
{
}
// implicit constructor
template <class Dummy = void, typename = std::enable_if_t<
type::detect_if<unit_type, type::is_one>, Dummy>>
constexpr inline quantity(const value_type& value) noexcept : _value(value)
{
}
template <class Dummy = void, typename = std::enable_if_t<
type::detect_if<unit_type, type::is_one>, Dummy>>
constexpr inline quantity(value_type&& value) noexcept : _value(std::move(value))
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
constexpr inline quantity(const Other& rhs) noexcept :
quantity(convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs))
{
}
template <class rhsUnit, class Dummy = void,
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<unit_type, is_compatible_with, rhsUnit>, Dummy>>
constexpr inline quantity(const quantity<rhsUnit, value_type>& rhs) noexcept :
_value(rhs._value)
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
constexpr inline quantity(Other&& rhs) noexcept :
quantity(std::move(convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(
std::move(rhs))))
{
}
template <class rhsUnit, class Dummy = void,

template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<unit_type, is_compatible_with, rhsUnit>, Dummy>>
constexpr inline quantity(quantity<rhsUnit, value_type>&& rhs) noexcept :
_value(std::move(rhs._value))
type::detect_if<quantity, is_convertible_into, Other>, Dummy>>
[[nodiscard]] constexpr inline explicit operator Other() const noexcept
{
return convert<std::remove_cv_t<quantity>, std::remove_cv_t<Other>>{}(*this);
}

// copy constructor
Expand Down Expand Up @@ -182,6 +183,15 @@ class quantity
{
return quantity{lhs._value + rhs._value};
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator+(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs + convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}

constexpr inline auto operator-=(const quantity& rhs) noexcept
{
this->_value -= rhs._value;
Expand All @@ -192,6 +202,14 @@ class quantity
{
return quantity{lhs._value - rhs._value};
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator-(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs - convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}
#pragma endregion
#pragma region multiplication
constexpr inline auto& operator*=(const quantity<one, value_type>& rhs) noexcept
Expand Down Expand Up @@ -242,31 +260,79 @@ class quantity
{
return lhs._value == rhs._value;
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator==(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs == convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}
[[nodiscard]] friend constexpr inline auto operator!=(const quantity& lhs,
const quantity& rhs) noexcept
{
return !(lhs == rhs);
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator!=(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs != convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}
[[nodiscard]] friend constexpr inline auto operator<(const quantity& lhs,
const quantity& rhs) noexcept
{
return lhs._value < rhs._value;
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator<(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs < convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}
[[nodiscard]] friend constexpr inline auto operator>(const quantity& lhs,
const quantity& rhs) noexcept
{
return rhs < lhs;
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator>(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs > convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}
[[nodiscard]] friend constexpr inline auto operator<=(const quantity& lhs,
const quantity& rhs) noexcept
{
return !(rhs < lhs);
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator<=(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs <= convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}
[[nodiscard]] friend constexpr inline auto operator>=(const quantity& lhs,
const quantity& rhs) noexcept
{
return !(lhs < rhs);
}
template <class Other, class Dummy = void,
typename = std::enable_if_t<
type::detect_if<Other, is_convertible_into, quantity>, Dummy>>
[[nodiscard]] friend constexpr inline auto operator>=(const quantity& lhs,
const Other& rhs) noexcept
{
return lhs >= convert<std::remove_cv_t<Other>, std::remove_cv_t<quantity>>{}(rhs);
}
#pragma endregion
#pragma endregion
};
Expand All @@ -288,6 +354,19 @@ template <class lhsUnit, class rhsUnit, class rhsValueType>
return quantity<divide_units<lhsUnit, rhsUnit>, rhsValueType>{lhs._value
/ rhs._value};
}
// Conversion operator for the unit without prefix and dimension
template <class ValueType>
struct convert<ValueType, quantity<one, ValueType>>
{
constexpr auto operator()(const ValueType& rhs) -> quantity<one, ValueType>
{
return quantity<one, ValueType>{rhs};
}
constexpr auto operator()(ValueType&& rhs) -> quantity<one, ValueType>
{
return quantity<one, ValueType>{std::move(rhs)};
}
};
#pragma region casting functions
// The value_type_cast function lets you cast the value_type of a quantity
// to another value_type.
Expand Down
Loading

0 comments on commit bbf4592

Please sign in to comment.