-
Notifications
You must be signed in to change notification settings - Fork 94
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 #580 by using a fixed-point implementation for unit conversions using integer representations #615
base: master
Are you sure you want to change the base?
Fix #580 by using a fixed-point implementation for unit conversions using integer representations #615
Changes from 1 commit
cf9c05f
3635260
74f30da
2f54c76
e003a58
60c94da
d00b330
99d4315
653d3d2
ed2574f
e688ffc
55d8fd6
38dcf64
ad76149
95cc9f3
5f8eb5c
1b57404
f673619
f642d37
b6a6752
647ce6b
464ecd4
e933be7
6873c8b
65a0ee4
0c1971e
4ef0210
35ed472
329b9f5
7fa15d2
e464677
cc9ea9d
a51462c
f4c8e90
01f44c6
5713243
ff11878
b35e241
ef0e7b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -70,15 +70,37 @@ class measurement { | |||||
[[nodiscard]] friend constexpr measurement operator+(const measurement& lhs, const measurement& rhs) | ||||||
{ | ||||||
using namespace std; | ||||||
return measurement(lhs.value() + rhs.value(), hypot(lhs.uncertainty(), rhs.uncertainty())); | ||||||
return measurement{std::in_place, lhs.value() + rhs.value(), hypot(lhs.uncertainty(), rhs.uncertainty())}; | ||||||
} | ||||||
|
||||||
[[nodiscard]] friend constexpr measurement operator-(const measurement& lhs, const measurement& rhs) | ||||||
{ | ||||||
using namespace std; | ||||||
return measurement(lhs.value() - rhs.value(), hypot(lhs.uncertainty(), rhs.uncertainty())); | ||||||
return measurement{std::in_place, lhs.value() - rhs.value(), hypot(lhs.uncertainty(), rhs.uncertainty())}; | ||||||
} | ||||||
|
||||||
template<typename To, mp_units::Magnitude M> | ||||||
[[nodiscard]] constexpr measurement<To> scale(std::type_identity<measurement<To>>, M scaling_factor) const | ||||||
{ | ||||||
constexpr std::type_identity<To> to_value_type; | ||||||
return measurement<To>{ | ||||||
std::in_place, | ||||||
mp_units::scale(to_value_type, scaling_factor, value()), | ||||||
mp_units::scale(to_value_type, scaling_factor, value()), | ||||||
}; | ||||||
} | ||||||
|
||||||
template<mp_units::Magnitude M> | ||||||
[[nodiscard]] constexpr auto scale(M scaling_factor) const | ||||||
{ | ||||||
return measurement{ | ||||||
std::in_place, | ||||||
mp_units::scale(scaling_factor, value()), | ||||||
mp_units::scale(scaling_factor, value()), | ||||||
}; | ||||||
} | ||||||
|
||||||
|
||||||
[[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const measurement& rhs) | ||||||
{ | ||||||
const auto val = lhs.value() * rhs.value(); | ||||||
|
@@ -127,15 +149,23 @@ class measurement { | |||||
private: | ||||||
value_type value_{}; | ||||||
value_type uncertainty_{}; | ||||||
|
||||||
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) | ||||||
constexpr measurement(std::in_place_t, value_type val, value_type err) : | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not the right way of using This is not the case here. We initialize two members with one argument each. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you would like to have in-place like construction for type then probably something like piecewise_construct is needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I just wanted a tag to select the private constructor, indicating "I know what I'm doing - I guarantee that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can either remove it or use a new dedicated tag. Maybe something similar to: mp-units/example/include/validated_type.h Lines 43 to 44 in 727a898
|
||||||
value_(std::move(val)), uncertainty_(std::move(err)) | ||||||
{ | ||||||
} | ||||||
}; | ||||||
|
||||||
} // namespace | ||||||
|
||||||
|
||||||
template<typename T> | ||||||
constexpr bool mp_units::is_scalar<measurement<T>> = true; | ||||||
template<typename T> | ||||||
constexpr bool mp_units::is_vector<measurement<T>> = true; | ||||||
|
||||||
static_assert(mp_units::RepresentationOf<measurement<int>, mp_units::quantity_character::scalar>); | ||||||
static_assert(mp_units::RepresentationOf<measurement<double>, mp_units::quantity_character::scalar>); | ||||||
|
||||||
namespace { | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,238 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// The MIT License (MIT) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Copyright (c) 2018 Mateusz Pusz | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// of this software and associated documentation files (the "Software"), to deal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// in the Software without restriction, including without limitation the rights | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// copies of the Software, and to permit persons to whom the Software is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// furnished to do so, subject to the following conditions: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// The above copyright notice and this permission notice shall be included in all | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// copies or substantial portions of the Software. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// SOFTWARE. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
#pragma once | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
// IWYU pragma: private, include <mp-units/framework.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
#include <mp-units/bits/fixed_point.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
#include <mp-units/framework/customization_points.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
#ifndef MP_UNITS_IN_MODULE_INTERFACE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
#ifdef MP_UNITS_IMPORT_STD | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
import std; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
#else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
#include <concepts> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
#endif | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
#endif | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
namespace mp_units { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
namespace detail { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The above is shorter and makes it harder to export public APIs from |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<std::floating_point A, std::floating_point B> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
using minimal_floating_point_type = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
std::conditional_t<(std::numeric_limits<A>::digits >= std::numeric_limits<B>::digits), A, B>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename To, typename T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
constexpr auto cast_integral(const T& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if constexpr (std::is_integral_v<T>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<To>(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is meant to be a floating point value, then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really. The specification of
So the first case is much broader. For standard types, this in particular includes the case where There are other ways to handle this. We could also just silence the warning locally, or branch-out the mixed int/float paths further above. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct floating_point_scaling_factor_type { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// fallback implementation for types with a `value_type` nested type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
using type = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
std::enable_if_t<!std::is_same_v<value_type_t<T>, T>, floating_point_scaling_factor_type<value_type_t<T>>>::type; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably, it is better to leave the primary template undefined and handle |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<std::floating_point T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct floating_point_scaling_factor_type<T> : std::type_identity<T> {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<std::integral T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct floating_point_scaling_factor_type<T> : std::common_type<T, float> {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
requires requires { typename scaling_traits<T>::floating_point_scaling_factor_type; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct floating_point_scaling_factor_type<T> : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
std::type_identity<typename scaling_traits<T>::floating_point_scaling_factor_type> {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<Magnitude auto M> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct floating_point_scaling_impl { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr Magnitude auto num = _numerator(M); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr Magnitude auto den = _denominator(M); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr Magnitude auto irr = M * (den / num); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr T ratio = [] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
using U = long double; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<T>(_get_value<U>(M)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename To, typename From> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In such a case, |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr To scale(const From& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
using U = minimal_floating_point_type<typename floating_point_scaling_factor_type<To>::type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
typename floating_point_scaling_factor_type<From>::type>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if constexpr (_is_integral(M)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<To>(cast_integral<U>(value) * _get_value<U>(num)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if constexpr (_is_integral(_pow<-1>(M))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<To>(cast_integral<U>(value) / _get_value<U>(den)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<To>(cast_integral<U>(value) * ratio<U>); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @brief Default implementation of `scaling_traits` for "floating-point like" types | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* This class implements scaling by either multiplying or dividing the value with | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* a floating-point representation of the scaling factor; the floating-point representation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* is chosen such that it is of comparable precision as the representation type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @note This is a low-level facility. Neither the `From`, nor the `To` types of the scaling | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* operation are actually constrained to be floating-point or even "floating-point like" types. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* All it represents is the scaling operation as implemented by multiplication with a floating-point | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* representation of the scaling factor. This is also used whenever simultaneously scaling and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* converting between integer and floating-point types. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @tparam Rep Representation type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename Rep> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct floating_point_scaling_traits { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<Magnitude M, typename From> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr Rep scale_from(M, const From& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return floating_point_scaling_impl<M{}>::template scale<Rep>(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<Magnitude M> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr auto scale(M, const Rep& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// for standard floating-point types, the result respresentation is always the same | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return floating_point_scaling_impl<M{}>::template scale<Rep>(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<Magnitude auto M> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct fixed_point_scaling_impl { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr Magnitude auto num = _numerator(M); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr Magnitude auto den = _denominator(M); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<std::integral T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr auto ratio = [] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
using U = long double; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return detail::fixed_point<T>(_get_value<U>(M)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename To, typename From> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
requires std::integral<value_type_t<To>> && std::integral<value_type_t<From>> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr To scale(const From& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
using U = std::common_type_t<value_type_t<From>, value_type_t<To>>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if constexpr (_is_integral(M)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<To>(static_cast<value_type_t<From>>(value) * _get_value<U>(num)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if constexpr (_is_integral(_pow<-1>(M))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<To>(static_cast<value_type_t<From>>(value) / _get_value<U>(den)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return static_cast<To>(ratio<U>.scale(static_cast<value_type_t<From>>(value))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename Rep> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct fixed_point_scaling_traits { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<Magnitude M, typename From> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr Rep scale_from(M, const From& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fixed_point_scaling_impl<M{}>::template scale<Rep>(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<Magnitude M> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static constexpr auto scale(M, const Rep& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// for standard integer types, the result respresentation is always the same | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fixed_point_scaling_impl<M{}>::template scale<Rep>(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename T, typename Other = T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
inline constexpr auto select_scaling_traits = [] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if constexpr (requires { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// we only check if the traits class is complete, not it's members; we do not want to fall-back | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// depending on the argument types | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ sizeof(mp_units::scaling_traits<T>) } -> std::integral; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not the following?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not a standard lawyer, but my tests on godbolt suggest that the latter just tests if the type is declared, not if it is defined/complete. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. Still, I am not sure if checking for Anyway, checking the return type of |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// traits class is defined; use that | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return mp_units::scaling_traits<T>{}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't scaling traits also take There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes and no. There are basically two cases:
So, there are really two customisation points, one without specifying the output representation type, and one with. I do realise though that the one with explicit output type actually needs multi-dispatch, so the current approach is indeed not suitable. Perhaps we just need to select or create an appropriate placeholder for the |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// undefined traits class; fall-back to default handling | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it better to have dedicated specializations of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was my initial plan, actually. I pivoted as lead by the example of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I am not against having some implemention details here. But I think that in the PR we have too many layers for this:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
if constexpr (mp_units::treat_as_floating_point<T> || mp_units::treat_as_floating_point<Other>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return floating_point_scaling_traits<T>{}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if constexpr (std::is_integral_v<T> || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
(std::is_integral_v<value_type_t<T>> && std::convertible_to<value_type_t<T>, T> && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
std::convertible_to<T, value_type_t<T>>)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fixed_point_scaling_traits<value_type_t<T>>{}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// placeholder to report failure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return std::false_type{}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be equivalent to the lack of definition for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I understand correctly: you suggest There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think that having In case of problems with constraints subsumption, we could expose proper concepts to make it work. If there is no way to do it as above, then we could implement this in a similar way to CPOs (Niebloids). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The latest stint here now indeed works with a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename T, typename Other = T> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
concept HasScalingTraits = !std::convertible_to<decltype(select_scaling_traits<T, Other>), std::false_type>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
static_assert(HasScalingTraits<int>); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static_assert(HasScalingTraits<double>); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
} // namespace detail | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<typename To, Magnitude M, typename From> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
requires detail::HasScalingTraits<To, From> || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
requires(const From& value) { value.scale(std::type_identity<To>{}, M{}); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
constexpr To scale(std::type_identity<To> to_type, M scaling_factor, const From& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if constexpr (requires { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ value.scale(to_type, scaling_factor) } -> std::convertible_to<To>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return value.scale(to_type, scaling_factor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return detail::select_scaling_traits<To, From>.scale_from(scaling_factor, value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the above suggestions, this will simplify to:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
template<Magnitude M, typename From> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
requires detail::HasScalingTraits<From> || requires(const From& value) { value.scale(M{}); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
constexpr auto scale(M scaling_factor, const From& value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if constexpr (requires { value.scale(scaling_factor); }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return value.scale(scaling_factor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not think that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fine with me. Explicit specialisations are sometimes a bit painful to do because of the namespaces. That said, there are plenty of precedents in the standard library. ADL is a bit more convenient, and modern approaches would go through a CPO. On the other hand, I would actually prefer not to dispatch on the magnitude type at all, because if a different implementation is chosen for different magnitudes by accident, that would be very confusing. Instead, all paths for different magnitudes should remain close and under the control of the same "customiser". Thus, I think I'll give it another shot just explicit specialisation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ADL is not a good choice here as someone may want to provide a customization for a third-party existing type, and this would require injecting some stuff into the third-party namespace, which may not be allowed, like in the case of |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return detail::select_scaling_traits<From>.scale(scaling_factor, value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
static_assert(_is_integral(mag<299'792'458>)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static_assert(_get_value<int>(mag<299'792'458>) == 299'792'458); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static_assert(scale(mag<299'792'458>, 1) == 299'792'458); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
static_assert(scale(std::type_identity<int>{}, mag<299'792'458>, 1) == 299'792'458); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
} // namespace mp_units |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
type_identity
usage here is quite novel. I understand the rationale, but maybe we can find a more traditional way of doing it? @JohelEGP, do you have any ideas here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea comes from
boost::hana::type
. Would be great if the standard had its own dedicated "type tag".