refactor: V2 design update

This commit is contained in:
Mateusz Pusz committed 2022-10-06 23:43:01 +01:00
1 parent 4411b8ea6d
commit 4a49bdda05
146 files changed
+3076 -13622

No files matched your search

+57 -60
View File
@@ -22,58 +22,56 @@
#pragma once
#include <units/bits/dimension_op.h>
#include <units/bits/equivalent.h>
#include <units/quantity_cast.h>
#include <units/concepts.h>
#include <units/dimension.h>
#include <units/unit.h>
// #include <units/bits/equivalent.h>
// #include <units/quantity_cast.h>
namespace units {
template<Dimension D, UnitOf<D> U>
struct reference;
// template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
// class quantity_point;
template<Dimension D, UnitOf<D> U, Representation Rep>
class quantity;
// template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
// class quantity_kind;
template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
class quantity_point;
// template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
// class quantity_point_kind;
template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
class quantity_kind;
// TODO common_unit should use common_magnitude(U1::mag, U2::mag)
template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
class quantity_point_kind;
template<Dimension D1, Dimension D2>
requires(equivalent(D1{}, D2{}))
using common_dimension = conditional<std::is_same_v<D1, D2>, D1, detail::dim_type<D1>>;
template<Unit U1, Unit U2>
// requires(equivalent<U1, U2>)
using common_unit = U1;
namespace detail {
template<typename R1, typename R2>
struct common_quantity_reference_impl;
template<typename D, typename U>
struct common_quantity_reference_impl<reference<D, U>, reference<D, U>> {
using type = reference<D, U>;
template<typename R>
struct common_quantity_reference_impl<R, R> {
using type = R;
};
template<typename D, typename U1, typename U2>
struct common_quantity_reference_impl<reference<D, U1>, reference<D, U2>> {
using type = reference<D, downcast_unit<D, common_magnitude(U1::mag, U2::mag)>>;
using type = reference<D, common_unit<U1, U2>>;
};
template<typename D1, typename U1, typename D2, typename U2>
requires(same_unit_reference<dimension_unit<D1>, dimension_unit<D2>>::value)
struct common_quantity_reference_impl<reference<D1, U1>, reference<D2, U2>> {
using type = reference<D1, downcast_unit<D1, common_magnitude(U1::mag, U2::mag)>>;
template<typename R1, typename R2>
requires(equivalent(R1::dimension, R2::dimension))
struct common_quantity_reference_impl<R1, R2> {
using type = reference<common_dimension<decltype(R1::dimension), decltype(R2::dimension)>,
common_unit<decltype(R1::unit), decltype(R2::unit)>>;
};
template<typename D1, typename U1, typename D2, typename U2>
struct common_quantity_reference_impl<reference<D1, U1>, reference<D2, U2>> {
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = common_magnitude(reference<D1, U1>::mag,
reference<D2, U2>::mag);
using dimension = conditional<is_specialization_of<D1, unknown_dimension>, D2, D1>;
using unit = downcast_unit<dimension, mag / dimension::mag>;
using type = reference<dimension, unit>;
};
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
using common_quantity_reference =
TYPENAME detail::common_quantity_reference_impl<std::remove_const_t<decltype(Q1::reference)>,
std::remove_const_t<decltype(Q2::reference)>>::type;
@@ -83,43 +81,42 @@ using common_quantity_reference =
namespace std {
template<units::Quantity Q1, units::QuantityEquivalentTo<Q1> Q2>
template<units::Quantity Q1, units::quantity_equivalent_to<Q1> Q2>
requires requires { typename common_type_t<typename Q1::rep, typename Q2::rep>; }
struct common_type<Q1, Q2> {
private:
using ref = units::detail::common_quantity_reference<Q1, Q2>;
public:
using type =
units::quantity<typename ref::dimension, typename ref::unit, common_type_t<typename Q1::rep, typename Q2::rep>>;
using type = units::quantity<ref{}, common_type_t<typename Q1::rep, typename Q2::rep>>;
};
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<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>;
};
// 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<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>;
// };
template<units::QuantityKind QK1, units::QuantityKindEquivalentTo<QK1> QK2>
requires requires { typename common_type_t<typename QK1::rep, typename QK2::rep>; }
struct common_type<QK1, QK2> {
using type =
units::quantity_kind<typename QK1::kind_type,
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::unit,
typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::rep>;
};
// template<units::QuantityKind QK1, units::QuantityKindEquivalentTo<QK1> QK2>
// requires requires { typename common_type_t<typename QK1::rep, typename QK2::rep>; }
// struct common_type<QK1, QK2> {
// using type =
// units::quantity_kind<typename QK1::kind_type,
// typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::unit,
// typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::rep>;
// };
template<units::QuantityPointKind QPK1, units::QuantityPointKindEquivalentTo<QPK1> QPK2>
requires requires { typename common_type_t<typename QPK1::rep, typename QPK2::rep>; }
struct common_type<QPK1, QPK2> {
using type = units::quantity_point_kind<
typename QPK1::point_kind_type,
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::unit,
typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::rep>;
};
// template<units::QuantityPointKind QPK1, units::QuantityPointKindEquivalentTo<QPK1> QPK2>
// requires requires { typename common_type_t<typename QPK1::rep, typename QPK2::rep>; }
// struct common_type<QPK1, QPK2> {
// using type = units::quantity_point_kind<
// typename QPK1::point_kind_type,
// typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::unit,
// typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::rep>;
// };
} // namespace std
@@ -34,7 +34,39 @@ struct type_list {};
template<typename T, typename... Ts>
struct per {};
namespace detail {
template<int Num, int... Den>
inline constexpr bool valid_ratio = true;
template<int... Den>
inline constexpr bool valid_ratio<0, Den...> = false;
template<int Num>
inline constexpr bool valid_ratio<Num, 0> = false;
template<>
inline constexpr bool valid_ratio<0, 0> = false;
template<int Num, int... Den>
inline constexpr bool positive_ratio = (Num > 0);
template<int Num, int Den>
inline constexpr bool positive_ratio<Num, Den> = (Num * Den > 0);
template<int Num, int... Den>
inline constexpr bool ratio_one = false;
template<>
inline constexpr bool ratio_one<1> = true;
template<int N>
inline constexpr bool ratio_one<N, N> = true;
} // namespace detail
template<typename F, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && detail::positive_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power {
using factor = F;
static constexpr ratio exponent{Num, Den...};
@@ -146,9 +178,9 @@ using expr_consolidate = typename expr_consolidate_impl<List>::type;
// expr_simplify
template<typename T, typename powerNum, typename powerDen>
template<typename T, ratio Num, ratio Den>
struct expr_simplify_power {
static constexpr ratio r = powerNum::exponent - powerDen::exponent;
static constexpr ratio r = Num - Den;
using type = power_or_T<T, ratio{abs(r.num), r.den}>;
using num = conditional<(r > 0), type_list<type>, type_list<>>;
using den = conditional<(r < 0), type_list<type>, type_list<>>;
@@ -179,7 +211,7 @@ struct expr_simplify<type_list<T, NRest...>, type_list<T, DRest...>, Pred> :
template<typename T, typename... NRest, int... Ints, typename... DRest, template<typename, typename> typename Pred>
struct expr_simplify<type_list<power<T, Ints...>, NRest...>, type_list<T, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, power<T, Ints...>, power<T, 1>>;
using type = expr_simplify_power<T, power<T, Ints...>::exponent, ratio{1}>;
using num = type_list_join<typename type::num, typename impl::num>;
using den = type_list_join<typename type::den, typename impl::den>;
};
@@ -187,7 +219,7 @@ struct expr_simplify<type_list<power<T, Ints...>, NRest...>, type_list<T, DRest.
template<typename T, typename... NRest, typename... DRest, int... Ints, template<typename, typename> typename Pred>
struct expr_simplify<type_list<T, NRest...>, type_list<power<T, Ints...>, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, power<T, 1>, power<T, Ints...>>;
using type = expr_simplify_power<T, ratio{1}, power<T, Ints...>::exponent>;
using num = type_list_join<typename impl::num, typename type::num>;
using den = type_list_join<typename impl::den, typename type::den>;
};
@@ -197,7 +229,7 @@ template<typename T, typename... NRest, int... Ints1, typename... DRest, int...
requires(!std::same_as<power<T, Ints1...>, power<T, Ints2...>>)
struct expr_simplify<type_list<power<T, Ints1...>, NRest...>, type_list<power<T, Ints2...>, DRest...>, Pred> {
using impl = expr_simplify<type_list<NRest...>, type_list<DRest...>, Pred>;
using type = expr_simplify_power<T, power<T, Ints1...>, power<T, Ints2...>>;
using type = expr_simplify_power<T, power<T, Ints1...>::exponent, power<T, Ints2...>::exponent>;
using num = type_list_join<typename impl::num, typename type::num>;
using den = type_list_join<typename impl::den, typename type::den>;
};
+1 -1
View File
@@ -5,7 +5,7 @@
#include <string_view>
template<typename T>
constexpr auto type_name()
[[nodiscard]] consteval std::string_view type_name()
{
std::string_view name, prefix, suffix;
#ifdef __clang__
+3
View File
@@ -77,4 +77,7 @@ template<typename T, template<typename...> typename Type>
// inline constexpr bool // TODO: Replace with concept when it works with MSVC
concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
template<typename T, typename... Ts>
concept one_of = (... || std::same_as<T, Ts>);
} // namespace units
+1 -1
View File
@@ -37,7 +37,7 @@ constexpr auto magnitude_text()
{
constexpr auto exp10 = extract_power_of_10(M);
constexpr Magnitude auto base = M / pow<exp10>(mag<10>());
constexpr Magnitude auto base = M / mag_power<10, exp10>;
constexpr Magnitude auto num = numerator(base);
constexpr Magnitude auto den = denominator(base);
static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported");
+90 -2
View File
@@ -23,6 +23,94 @@
#pragma once
// IWYU pragma: begin_exports
#include <units/bits/basic_concepts.h>
#include <units/bits/quantity_of.h>
// #include <units/bits/basic_concepts.h>
// #include <units/bits/quantity_of.h>
// IWYU pragma: end_exports
#include <units/bits/external/type_traits.h>
#include <units/dimension.h>
#include <units/unit.h>
namespace units {
template<Dimension D, Unit U>
struct reference;
/**
* @brief A concept matching all references in the library.
*
* Satisfied by all specializations of @c reference.
*/
template<typename T>
concept Reference = is_specialization_of<T, reference>;
namespace detail {
template<typename T>
inline constexpr bool is_quantity = false;
}
template<typename T>
concept Quantity = detail::is_quantity<T>;
template<typename T, typename U>
concept common_type_with_ = // exposition only
(std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>>) &&
(std::constructible_from<std::common_type_t<T, U>, T>) && (std::constructible_from<std::common_type_t<T, U>, U>);
template<typename T, typename U = T>
concept scalable_number_ = // exposition only
(std::regular_invocable<std::multiplies<>, T, U>) && (std::regular_invocable<std::divides<>, T, U>);
template<typename T>
concept castable_number_ = // exposition only
common_type_with_<T, std::intmax_t> && scalable_number_<std::common_type_t<T, std::intmax_t>>;
template<typename T>
concept scalable_ = // exposition only
castable_number_<T> || (requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
template<typename T, typename U>
concept scalable_with_ = // exposition only
common_type_with_<T, U> && scalable_<std::common_type_t<T, U>>;
template<typename T>
concept Representation = (!Quantity<T>) &&
// (!QuantityLike<T>) && (!wrapped_quantity_<T>) &&
std::regular<T> && scalable_<T>;
template<Reference auto R, Representation Rep>
class quantity;
namespace detail {
template<auto R, typename Rep>
inline constexpr bool is_quantity<quantity<R, Rep>> = true;
// template<typename T>
// requires units::is_derived_from_specialization_of<T, units::quantity>
// inline constexpr bool is_quantity<T> = true;
} // namespace detail
/**
* @brief A concept matching all quantities with provided dimension
*
* Satisfied by all quantities with a dimension being the instantiation derived from
* the provided dimension type.
*/
template<typename Q, auto V>
concept quantity_of = Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
(Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension));
/**
* @brief A concept matching two equivalent quantities
*
* Satisfied by quantities having equivalent dimensions and units.
*/
template<typename Q1, typename Q2>
concept quantity_equivalent_to = Quantity<Q1> && equivalent(Q1::dimension, Q2::dimension) &&
equivalent(Q1::unit, Q2::unit);
} // namespace units
+35 -6
View File
@@ -52,6 +52,12 @@ namespace detail {
template<basic_fixed_string Symbol>
void to_base_base_dimension(const volatile base_dimension<Symbol>*);
template<typename T>
inline constexpr bool is_specialization_of_base_dimension = false;
template<basic_fixed_string Symbol>
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true;
} // namespace detail
/**
@@ -60,13 +66,18 @@ void to_base_base_dimension(const volatile base_dimension<Symbol>*);
* Satisfied by all dimension types derived from an specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); };
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); } &&
(!detail::is_specialization_of_base_dimension<T>);
template<BaseDimension D1, BaseDimension D2>
struct base_dimension_less : std::bool_constant<(D1::symbol < D2::symbol)> {};
// TODO Can we provide a smarter implementation?
std::false_type is_derived_dimension(...);
namespace detail {
template<typename T>
inline constexpr bool is_derived_dimension = false;
}
/**
* @brief A concept matching all derived dimensions in the library.
@@ -74,7 +85,7 @@ std::false_type is_derived_dimension(...);
* Satisfied by all dimension types derived from an specialization of `derived_dimension`.
*/
template<typename T>
concept DerivedDimension = decltype(is_derived_dimension(std::declval<T*>()))::value;
concept DerivedDimension = detail::is_derived_dimension<T>;
/**
* @brief A concept matching all dimensions in the library.
@@ -107,8 +118,20 @@ struct derived_dimension : detail::expr_fractions<derived_dimension<>, Ds...> {
using type = derived_dimension<Ds...>;
};
template<typename... Args>
std::true_type is_derived_dimension(const volatile derived_dimension<Args...>*);
namespace detail {
template<typename... Ds>
void to_base_specialization_of_derived_dimension(const volatile derived_dimension<Ds...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_derived_dimension =
requires(T * t) { to_base_specialization_of_derived_dimension(t); };
template<typename T>
requires is_derived_from_specialization_of_derived_dimension<T>
inline constexpr bool is_derived_dimension<T> = true;
} // namespace detail
/**
* @brief Dimension one
@@ -162,6 +185,12 @@ template<Dimension D>
template<Dimension D1, Dimension D2>
[[nodiscard]] consteval bool operator==(D1, D2)
{
return is_same_v<D1, D2>;
}
template<Dimension D1, Dimension D2>
[[nodiscard]] consteval bool equivalent(D1, D2)
{
return is_same_v<detail::dim_type<D1>, detail::dim_type<D2>>;
}
+209 -160
View File
@@ -22,7 +22,10 @@
#pragma once
#include <units/bits/expression_template.h>
#include <units/bits/external/hacks.h>
#include <units/bits/external/type_name.h>
#include <units/bits/external/type_traits.h>
#include <units/bits/prime.h>
#include <units/ratio.h>
#include <concepts>
@@ -40,31 +43,47 @@ using factorizer = wheel_factorizer<4>;
} // namespace detail
/**
* @brief A type to represent a standalone constant value.
*/
template<auto V>
struct constant {
static constexpr auto value = V;
};
// is_derived_from_specialization_of_constant
namespace detail {
template<auto V>
void to_base_specialization_of_constant(const volatile constant<V>*);
template<typename T>
inline constexpr bool is_magnitude = false;
template<typename T>
inline constexpr bool is_derived_from_specialization_of_constant =
requires(T * t) { to_base_specialization_of_constant(t); };
inline constexpr bool is_specialization_of_magnitude = false;
} // namespace detail
/**
* @brief Concept to detect whether T is a valid Magnitude.
*/
template<typename T>
concept Constant = detail::is_derived_from_specialization_of_constant<T>;
concept Magnitude = detail::is_magnitude<T>;
struct pi_v : constant<std::numbers::pi_v<long double>> {};
/**
* @brief A type to represent a standalone constant value.
*/
// template<auto V>
// struct constant {
// static constexpr auto value = V;
// };
// // is_derived_from_specialization_of_constant
// namespace detail {
// template<auto V>
// void to_base_specialization_of_constant(const volatile constant<V>*);
// template<typename T>
// inline constexpr bool is_derived_from_specialization_of_constant =
// requires(T * t) { to_base_specialization_of_constant(t); };
// } // namespace detail
// template<typename T>
// concept Constant = detail::is_derived_from_specialization_of_constant<T>;
// struct pi_v : constant<std::numbers::pi_v<long double>> {};
/**
* @brief Any type which can be used as a basis vector in a PowerV.
@@ -106,20 +125,26 @@ struct pi_v : constant<std::numbers::pi_v<long double>> {};
* _existing_ bases, including both prime numbers and any other irrational bases. For example, even though `sqrt(2)` is
* irrational, we must not ever use it as a base; instead, we would use `base_power{2, ratio{1, 2}}`.
*/
template<typename T>
concept PowerVBase = Constant<T> || std::integral<T>;
// TODO Unify with `power` if UTPs (P1985) are accepted by the Committee
template<PowerVBase auto V, int Num, int... Den>
requires(Num != 0)
struct power_v {
static constexpr auto base = V;
static constexpr ratio exponent{Num, Den...};
static_assert(exponent != 1);
};
namespace detail {
template<typename T>
inline constexpr bool is_named_magnitude = Magnitude<T> && !detail::is_specialization_of_magnitude<T>;
}
template<typename T>
concept PowerVBase = one_of<T, int, std::intmax_t, long double> || detail::is_named_magnitude<T>;
// TODO Unify with `power` if UTPs (P1985) are accepted by the Committee
template<PowerVBase auto V, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power_v {
static constexpr auto base = V;
static constexpr ratio exponent{Num, Den...};
};
namespace detail {
/**
* @brief Deduction guides for base_power: only permit deducing integral bases.
@@ -149,6 +174,8 @@ concept PowerV = detail::is_specialization_of_power_v<T>;
namespace detail {
// We do not want magnitude type to have the `l` literal after a value for a small integral number.
// For example this modifies `magnitude<3l>` to be `magnitude<3>`
template<auto V>
[[nodiscard]] consteval auto shorten_T()
{
@@ -156,8 +183,10 @@ template<auto V>
if constexpr (V <= std::numeric_limits<int>::max()) {
return static_cast<int>(V);
} else {
return V;
return static_cast<std::intmax_t>(V);
}
} else if constexpr (std::floating_point<decltype(V)>) {
return static_cast<long double>(V);
} else {
return V;
}
@@ -178,7 +207,7 @@ template<PowerVBase auto V, ratio R>
}
};
consteval auto inverse(PowerV auto bp) { return power_v_or_T<bp.base, bp.exponent * (-1)>(); }
// consteval auto inverse(PowerV auto bp) { return power_v_or_T<bp.base, bp.exponent * (-1)>(); }
// `widen_t` gives the widest arithmetic type in the same category, for intermediate computations.
// template<typename T>
@@ -253,35 +282,35 @@ consteval auto inverse(PowerV auto bp) { return power_v_or_T<bp.base, bp.exponen
//
// The input is the desired result, but in a (wider) intermediate type. The point of this function
// is to cast to the desired type, but avoid overflow in doing so.
// template<typename To, typename From>
// // TODO(chogg): Migrate this to use `treat_as_floating_point`.
// requires(!std::is_integral_v<To> || std::is_integral_v<From>)
// constexpr To checked_static_cast(From x)
// {
// // This function should only ever be called at compile time. The purpose of these exceptions is
// // to produce compiler errors, because we cannot `static_assert` on function arguments.
// if constexpr (std::is_integral_v<To>) {
// if (!std::in_range<To>(x)) {
// throw std::invalid_argument{"Cannot represent magnitude in this type"};
// }
// }
template<typename To, typename From>
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
requires(!std::is_integral_v<To> || std::is_integral_v<From>)
constexpr To checked_static_cast(From x)
{
// This function should only ever be called at compile time. The purpose of these exceptions is
// to produce compiler errors, because we cannot `static_assert` on function arguments.
if constexpr (std::is_integral_v<To>) {
if (!std::in_range<To>(x)) {
throw std::invalid_argument{"Cannot represent magnitude in this type"};
}
}
return static_cast<To>(x);
}
// return static_cast<To>(x);
// }
} // namespace detail
/**
* @brief Equality detection for two base powers.
*/
template<PowerV T, PowerV U>
[[nodiscard]] consteval bool operator==(T, U)
{
return std::is_same_v<T, U>;
}
// template<PowerV T, PowerV U>
// [[nodiscard]] consteval bool operator==(T, U)
// {
// return std::is_same_v<T, U>;
// }
template<typename T>
concept MagnitudeSpec = std::integral<T> || Constant<T> || PowerV<T>;
concept MagnitudeSpec = PowerVBase<T> || PowerV<T>;
// A variety of implementation detail helpers.
namespace detail {
@@ -295,17 +324,6 @@ template<MagnitudeSpec Element>
return element;
}
template<MagnitudeSpec Element>
[[nodiscard]] consteval auto get_base_value(Element element)
{
const auto base = get_base(element);
using base_type = decltype(base);
if constexpr (std::integral<base_type>)
return base;
else
return base_type::value;
}
template<MagnitudeSpec Element>
[[nodiscard]] consteval ratio get_exponent(Element)
{
@@ -338,88 +356,92 @@ template<MagnitudeSpec Element>
}
// A way to check whether a number is prime at compile time.
[[nodiscard]] consteval bool is_prime(std::intmax_t n)
{
return (n >= 0) && factorizer::is_prime(static_cast<std::size_t>(n));
}
// [[nodiscard]] consteval bool is_prime(std::intmax_t n)
// {
// return (n >= 0) && factorizer::is_prime(static_cast<std::size_t>(n));
// }
template<MagnitudeSpec Element>
[[nodiscard]] consteval bool is_valid_element(Element element)
{
if (get_exponent(element) == 0) {
return false;
}
if constexpr (std::integral<decltype(get_base(element))>) {
// Some prime numbers are so big, that we can't check their primality without exhausting limits on constexpr steps
// and/or iterations. We can still _perform_ the factorization for these by using the `known_first_factor`
// workaround. However, we can't _check_ that they are prime, because this workaround depends on the input being
// usable in a constexpr expression. This is true for `prime_factorization` (below), where the input `N` is a
// template parameter, but is not true for our case, where the input `bp.get_base()` is a function parameter. (See
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html for some background reading on this
// distinction.)
//
// In our case: we simply give up on excluding every possible ill-formed base power, and settle for catching the
// most likely and common mistakes.
if (const bool too_big_to_check = (get_base_value(element) > 1'000'000'000)) {
return true;
}
// template<MagnitudeSpec Element>
// [[nodiscard]] consteval bool is_valid_element(Element element)
// {
// if (get_exponent(element) == 0) {
// return false;
// }
// if constexpr (std::integral<decltype(get_base(element))>) {
// // Some prime numbers are so big, that we can't check their primality without exhausting limits on constexpr
// steps
// // and/or iterations. We can still _perform_ the factorization for these by using the `known_first_factor`
// // workaround. However, we can't _check_ that they are prime, because this workaround depends on the input being
// // usable in a constexpr expression. This is true for `prime_factorization` (below), where the input `N` is a
// // template parameter, but is not true for our case, where the input `bp.get_base()` is a function parameter.
// (See
// // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html for some background reading on this
// // distinction.)
// //
// // In our case: we simply give up on excluding every possible ill-formed base power, and settle for catching the
// // most likely and common mistakes.
// if (const bool too_big_to_check = (get_base_value(element) > 1'000'000'000)) {
// return true;
// }
return is_prime(get_base_value(element));
} else {
return get_base_value(element) > 0;
}
}
// return is_prime(get_base_value(element));
// } else {
// return get_base_value(element) > 0;
// }
// }
// A function object to apply a predicate to all consecutive pairs of values in a sequence.
template<typename Predicate>
struct pairwise_all {
Predicate predicate;
// template<typename Predicate>
// struct pairwise_all {
// Predicate predicate;
template<typename... Ts>
[[nodiscard]] consteval bool operator()(Ts&&... ts) const
{
// Carefully handle different sizes, avoiding unsigned integer underflow.
constexpr auto num_comparisons = [](auto num_elements) {
return (num_elements > 1) ? (num_elements - 1) : 0;
}(sizeof...(Ts));
// template<typename... Ts>
// [[nodiscard]] consteval bool operator()(Ts&&... ts) const
// {
// // Carefully handle different sizes, avoiding unsigned integer underflow.
// constexpr auto num_comparisons = [](auto num_elements) {
// return (num_elements > 1) ? (num_elements - 1) : 0;
// }(sizeof...(Ts));
// Compare zero or more pairs of neighbours as needed.
return [this]<std::size_t... Is>(std::tuple<Ts...> && t, std::index_sequence<Is...>)
{
return (predicate(std::get<Is>(t), std::get<Is + 1>(t)) && ...);
}
(std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>());
}
};
// // Compare zero or more pairs of neighbours as needed.
// return [this]<std::size_t... Is>(std::tuple<Ts...> && t, std::index_sequence<Is...>)
// {
// return (predicate(std::get<Is>(t), std::get<Is + 1>(t)) && ...);
// }
// (std::make_tuple(std::forward<Ts>(ts)...), std::make_index_sequence<num_comparisons>());
// }
// };
// Deduction guide: permit constructions such as `pairwise_all{std::less{}}`.
// template<typename T>
// pairwise_all(T) -> pairwise_all<T>;
// Check whether a sequence of (possibly heterogeneously typed) values are strictly increasing.
template<typename... Ts>
requires(std::is_signed_v<Ts> && ...)
[[nodiscard]] consteval bool strictly_increasing(Ts&&... ts)
{
return pairwise_all{std::less{}}(std::forward<Ts>(ts)...);
}
// template<typename... Ts>
// requires(std::is_signed_v<Ts> && ...)
// [[nodiscard]] consteval bool strictly_increasing(Ts&&... ts)
// {
// return pairwise_all{std::less{}}(std::forward<Ts>(ts)...);
// }
template<MagnitudeSpec auto... Elements>
inline constexpr bool all_elements_valid = (is_valid_element(Elements) && ...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_valid = (is_valid_element(Elements) && ...);
template<MagnitudeSpec auto... Elements>
inline constexpr bool all_elements_in_order = strictly_increasing(get_base_value(Elements)...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_in_order = strictly_increasing(get_base_value(Elements)...);
template<MagnitudeSpec auto... Elements>
inline constexpr bool is_element_pack_valid = all_elements_valid<Elements...> && all_elements_in_order<Elements...>;
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool is_element_pack_valid = all_elements_valid<Elements...> && all_elements_in_order<Elements...>;
[[nodiscard]] consteval bool is_rational(MagnitudeSpec auto element)
{
return std::is_integral_v<decltype(get_base_value(element))> && (get_exponent(element).den == 1);
static_assert(!Magnitude<decltype(element)>); // magnitudes are handles by another overload
return std::is_integral_v<decltype(get_base(element))> && (get_exponent(element).den == 1);
}
[[nodiscard]] consteval bool is_integral(MagnitudeSpec auto element)
{
static_assert(!Magnitude<decltype(element)>); // magnitudes are handles by another overload
return is_rational(element) && get_exponent(element).num > 0;
}
@@ -433,29 +455,40 @@ inline constexpr bool is_element_pack_valid = all_elements_valid<Elements...> &&
* rational powers, and compare for equality.
*/
template<MagnitudeSpec auto... Ms>
requires detail::is_element_pack_valid<Ms...>
// requires detail::is_element_pack_valid<Ms...>
struct magnitude {
// Whether this magnitude represents an integer.
[[nodiscard]] friend consteval bool is_integral(const magnitude&) { return (detail::is_integral(Ms) && ...); }
[[nodiscard]] friend consteval bool is_integral(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_integral(Ms) && ...);
}
// Whether this magnitude represents a rational number.
[[nodiscard]] friend consteval bool is_rational(const magnitude&) { return (detail::is_rational(Ms) && ...); }
[[nodiscard]] friend consteval bool is_rational(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_rational(Ms) && ...);
}
};
namespace detail {
// Implementation for Magnitude concept (below).
template<auto... Ms>
void to_base_specialization_of_magnitude(const volatile magnitude<Ms...>*);
template<typename T>
inline constexpr bool is_magnitude = false;
template<auto... BPs>
inline constexpr bool is_magnitude<magnitude<BPs...>> = true;
inline constexpr bool is_derived_from_specialization_of_magnitude =
requires(T * t) { to_base_specialization_of_magnitude(t); };
template<typename T>
requires is_derived_from_specialization_of_magnitude<T>
inline constexpr bool is_magnitude<T> = true;
template<auto... Ms>
inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail
/**
* @brief Concept to detect whether T is a valid Magnitude.
*/
template<typename T>
concept Magnitude = detail::is_magnitude<T>;
/**
* @brief The value of a Magnitude in a desired type T.
@@ -471,23 +504,25 @@ concept Magnitude = detail::is_magnitude<T>;
// return result;
// }
/**
* @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number.
*/
inline constexpr Magnitude auto mag_pi = magnitude<pi_v{}>{};
inline constexpr struct mag_pi : magnitude<std::numbers::pi_v<long double>> {
} mag_pi;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude equality implementation.
template<auto... LeftBPs, auto... RightBPs>
constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>)
{
if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) {
return ((LeftBPs == RightBPs) && ...);
} else {
return false;
}
}
// template<auto... LeftBPs, auto... RightBPs>
// constexpr bool operator==(magnitude<LeftBPs...>, magnitude<RightBPs...>)
// {
// if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) {
// return ((LeftBPs == RightBPs) && ...);
// } else {
// return false;
// }
// }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude rational powers implementation.
@@ -517,6 +552,21 @@ constexpr auto cbrt(magnitude<Ms...> m)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude product implementation.
namespace detail {
consteval bool less(MagnitudeSpec auto lhs, MagnitudeSpec auto rhs)
{
if constexpr (is_named_magnitude<decltype(lhs)> && is_named_magnitude<decltype(rhs)>)
return type_name(lhs) < type_name(rhs);
else if constexpr (!is_named_magnitude<decltype(lhs)> && !is_named_magnitude<decltype(rhs)>)
return get_base(lhs) < get_base(rhs);
else
return is_named_magnitude<decltype(lhs)>;
}
} // namespace detail
// Base cases, for when either (or both) inputs are the identity.
constexpr Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; }
constexpr Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; }
@@ -528,32 +578,31 @@ constexpr Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
{
using namespace detail;
// Case for when H1 has the smaller base.
if constexpr (get_base_value(H1) < get_base_value(H2)) {
if constexpr (less(H1, H2)) {
if constexpr (sizeof...(T1) == 0) {
// Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases.
return magnitude<H1, H2, T2...>{};
} else {
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
}
}
// Case for when H2 has the smaller base.
if constexpr (get_base_value(H1) > get_base_value(H2)) {
} else if constexpr (less(H2, H1)) {
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
}
// "Same leading base" case.
if constexpr (get_base(H1) == get_base(H2)) {
} else {
constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
// Make a new power_v with the common base of H1 and H2, whose power is their powers' sum.
constexpr auto new_head = power_v_or_T<get_base(H1), get_exponent(H1) + get_exponent(H2)>();
if constexpr (is_same_v<decltype(get_base(H1)), decltype(get_base(H2))>) {
// Make a new power_v with the common base of H1 and H2, whose power is their powers' sum.
constexpr auto new_head = power_v_or_T<get_base(H1), get_exponent(H1) + get_exponent(H2)>();
if constexpr (get_exponent(new_head) == 0) {
return partial_product;
if constexpr (get_exponent(new_head) == 0) {
return partial_product;
} else {
return magnitude<new_head>{} * partial_product;
}
} else if constexpr (is_named_magnitude<decltype(get_base(H1))>) {
return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
} else {
return magnitude<new_head>{} * partial_product;
return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
}
}
}
+64 -57
View File
@@ -23,33 +23,37 @@
#pragma once
#include <units/bits/common_type.h>
#include <units/generic/dimensionless.h>
// #include <units/bits/common_type.h>
// #include <units/generic/dimensionless.h>
// IWYU pragma: begin_exports
#include <units/quantity_cast.h>
#include <units/ratio.h>
// #include <units/quantity_cast.h>
// #include <units/ratio.h>
#include <units/bits/common_type.h>
#include <units/concepts.h>
#include <units/customization_points.h>
#include <units/dimension.h>
#include <units/reference.h>
#include <units/unit.h>
#include <compare>
// IWYU pragma: end_exports
#include <units/reference.h>
#include <utility>
// #include <units/reference.h>
// #include <utility>
namespace units {
namespace detail {
template<Reference auto R> // TODO: Replace with `v * R` pending https://github.com/BobSteagall/wg21/issues/58.
inline constexpr auto make_quantity = [](auto&& v) {
// TODO: Replace with `v * R` pending https://github.com/BobSteagall/wg21/issues/58.
template<Reference auto R>
inline constexpr auto make_quantity = [](Representation auto&& v) {
using Rep = std::remove_cvref_t<decltype(v)>;
return quantity<typename decltype(R)::dimension, typename decltype(R)::unit, Rep>(std::forward<decltype(v)>(v));
return quantity<R, Rep>(std::forward<decltype(v)>(v));
};
template<typename T>
concept quantity_one =
Quantity<T> &&
(std::same_as<typename T::dimension, dim_one> || std::same_as<typename T::dimension, unknown_dimension<>>) &&
detail::equivalent_unit<typename T::unit, typename T::dimension, ::units::one, typename T::dimension>::value;
concept quantity_one = Quantity<T> && (T::dimension == one_dim) && (T::unit == one);
} // namespace detail
@@ -65,12 +69,12 @@ concept safe_convertible_to_ = // exposition only
// QFrom ratio is an exact multiple of QTo
template<typename QFrom, typename QTo>
concept harmonic_ = // exposition only
Quantity<QFrom> && Quantity<QTo> && is_integral(detail::quantity_magnitude<QFrom> / detail::quantity_magnitude<QTo>);
true; // TODO fix it when magnitudes are ready
// Quantity<QFrom> && Quantity<QTo> && is_integral(detail::quantity_magnitude<QFrom> / detail::quantity_magnitude<QTo>);
template<typename QFrom, typename QTo>
concept safe_castable_to_ = // exposition only
Quantity<QFrom> && QuantityOf<QTo, typename QFrom::dimension> &&
scalable_with_<typename QFrom::rep, typename QTo::rep> &&
Quantity<QFrom> && quantity_of<QTo, QFrom::dimension> && scalable_with_<typename QFrom::rep, typename QTo::rep> &&
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
template<typename Func, typename T, typename U>
@@ -83,9 +87,23 @@ concept invoke_result_convertible_to_ =
template<typename Func, typename Q, typename V>
concept have_quantity_for_ = Quantity<Q> && (!Quantity<V>) && quantity_value_for_<Func, typename Q::rep, V>;
template<typename T>
concept QuantityLike = requires(T q) {
typename quantity_like_traits<T>::dimension;
typename quantity_like_traits<T>::unit;
typename quantity_like_traits<T>::rep;
requires Dimension<typename quantity_like_traits<T>::dimension>;
requires Unit<typename quantity_like_traits<T>::unit>;
requires Representation<typename quantity_like_traits<T>::rep>;
{
quantity_like_traits<T>::number(q)
} -> std::convertible_to<typename quantity_like_traits<T>::rep>;
};
template<QuantityLike Q>
using quantity_like_type = quantity<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit,
typename quantity_like_traits<Q>::rep>;
using quantity_like_type =
quantity<reference<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit>{},
typename quantity_like_traits<Q>::rep>;
/**
* @brief A quantity
@@ -97,15 +115,15 @@ using quantity_like_type = quantity<typename quantity_like_traits<Q>::dimension,
* @tparam U a measurement unit of the quantity
* @tparam Rep a type to be used to represent values of a quantity
*/
template<Dimension D, UnitOf<D> U, Representation Rep = double>
template<Reference auto R, Representation Rep = double>
class quantity {
Rep number_;
public:
// member types and values
using dimension = D;
using unit = U;
using rep = Rep;
static constexpr units::reference<dimension, unit> reference{};
static constexpr auto reference = R;
static constexpr auto dimension = R.dimension;
static constexpr auto unit = R.unit;
// static member functions
[[nodiscard]] static constexpr quantity zero() noexcept
@@ -171,14 +189,14 @@ public:
} -> std::common_with<rep>;
}
{
using ret = quantity<D, U, decltype(+number())>;
using ret = quantity<R, decltype(+number())>;
return ret(+number());
}
[[nodiscard]] constexpr Quantity auto operator-() const
requires(std::regular_invocable<std::negate<>, rep>)
{
using ret = quantity<D, U, decltype(-number())>;
using ret = quantity<R, decltype(-number())>;
return ret(-number());
}
@@ -380,7 +398,7 @@ public:
requires(invoke_result_convertible_to_<rep, std::multiplies<>, rep, const Value&>)
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v)
{
using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, rep, Value>>;
using ret = quantity<R, std::invoke_result_t<std::multiplies<>, rep, Value>>;
return ret(q.number() * v);
}
@@ -388,7 +406,7 @@ public:
requires(invoke_result_convertible_to_<rep, std::multiplies<>, const Value&, rep>)
[[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q)
{
using ret = quantity<D, U, std::invoke_result_t<std::multiplies<>, Value, rep>>;
using ret = quantity<R, std::invoke_result_t<std::multiplies<>, Value, rep>>;
return ret(v * q.number());
}
@@ -397,7 +415,7 @@ public:
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::divides<>, rep, Value>>;
using ret = quantity<R, std::invoke_result_t<std::divides<>, rep, Value>>;
return ret(q.number() / v);
}
@@ -405,7 +423,7 @@ public:
requires(!Quantity<Value>) && (invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep>)
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
return detail::make_quantity<::units::reference<dim_one, ::units::one>{} / reference>(v / q.number());
return detail::make_quantity<dimensionless[one] / reference>(v / q.number());
}
template<typename Value>
@@ -414,7 +432,7 @@ public:
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::modulus<>, rep, Value>>;
using ret = quantity<R, std::invoke_result_t<std::modulus<>, rep, Value>>;
return ret(q.number() % v);
}
@@ -422,7 +440,7 @@ public:
requires(!floating_point_<rep>) && (invoke_result_convertible_to_<rep, std::modulus<>, rep, rep>)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<rep>::zero());
using ret = quantity<D, U, std::invoke_result_t<std::modulus<>, rep, rep>>;
using ret = quantity<R, std::invoke_result_t<std::modulus<>, rep, rep>>;
return ret(lhs.number() % rhs.number());
}
@@ -441,36 +459,37 @@ public:
// CTAD
#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16
template<typename D, typename U, typename Rep>
explicit(false) quantity(Rep&&)->quantity<D, U, Rep>;
template<auto R, typename Rep>
explicit(false) quantity(Rep&&) -> quantity<R, Rep>;
#endif
template<typename D, typename U, typename Rep>
explicit(false) quantity(quantity<D, U, Rep>)->quantity<D, U, Rep>;
template<auto R, typename Rep>
explicit(false) quantity(quantity<R, Rep>) -> quantity<R, Rep>;
template<Representation Rep>
explicit(false) quantity(Rep)->quantity<dim_one, one, Rep>;
explicit(false) quantity(Rep)->quantity<dimensionless[one], Rep>;
template<QuantityLike Q>
explicit quantity(Q) -> quantity<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit,
typename quantity_like_traits<Q>::rep>;
explicit quantity(Q)
-> quantity<reference<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit>{},
typename quantity_like_traits<Q>::rep>;
// non-member binary operators
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
requires(quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
using ref = detail::common_quantity_reference<Q1, Q2>;
using ret = quantity<typename ref::dimension, typename ref::unit, decltype(lhs.number() + rhs.number())>;
using ret = quantity<ref{}, decltype(lhs.number() + rhs.number())>;
return ret(ret(lhs).number() + ret(rhs).number());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
requires(quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
using ref = detail::common_quantity_reference<Q1, Q2>;
using ret = quantity<typename ref::dimension, typename ref::unit, decltype(lhs.number() - rhs.number())>;
using ret = quantity<ref{}, decltype(lhs.number() - rhs.number())>;
return ret(ret(lhs).number() - ret(rhs).number());
}
@@ -491,17 +510,17 @@ template<Quantity Q1, Quantity Q2>
template<Quantity Q1, Quantity Q2>
requires(!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
(QuantityEquivalentTo<Q2, Q1> || Dimensionless<Q2>) &&
(quantity_equivalent_to<Q2, Q1> || quantity_of<Q2, one_dim>) &&
(quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q2::rep>::zero());
using ret = quantity<typename Q1::dimension, typename Q1::unit,
using ret = quantity<reference<typename Q1::dimension, typename Q1::unit>{},
std::invoke_result_t<std::modulus<>, typename Q1::rep, typename Q2::rep>>;
return ret(lhs.number() % rhs.number());
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
{
@@ -509,7 +528,7 @@ template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
return cq(lhs).number() <=> cq(rhs).number();
}
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
{
@@ -517,16 +536,4 @@ template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
return cq(lhs).number() == cq(rhs).number();
}
// type traits
namespace detail {
template<typename D, typename U, typename Rep>
inline constexpr bool is_quantity<quantity<D, U, Rep>> = true;
template<typename T>
requires units::is_derived_from_specialization_of<T, units::quantity>
inline constexpr bool is_quantity<T> = true;
} // namespace detail
} // namespace units
+42 -49
View File
@@ -22,36 +22,27 @@
#pragma once
#include <units/concepts.h>
#include <units/dimension.h>
#include <units/unit.h>
namespace units {
// TODO Concept for system reference
template<auto R, typename Rep = double>
class quantity {
public:
using reference = decltype(R);
static constexpr auto dimension = reference::dimension;
static constexpr auto unit = reference::unit;
quantity(Rep) {}
};
template<typename Child, Dimension auto Dim, Unit auto CoU>
template<Dimension auto Dim, Unit auto CoU>
struct system_reference;
namespace detail {
// namespace detail {
template<typename Child, auto Dim, auto CoU>
void to_base_specialization_of_system_reference(const volatile system_reference<Child, Dim, CoU>*);
// template<typename Child, auto Dim, auto CoU>
// void to_base_specialization_of_inline constexpr struct const volatile system_reference<Child : system_reference<Dim,
// CoU>*> {} const volatile system_reference<Child;
template<typename T>
// inline constexpr bool // TODO: Replace with concept when it works with MSVC
concept is_derived_from_specialization_of_system_reference =
requires(T* t) { detail::to_base_specialization_of_system_reference(t); };
// template<typename T>
// // inline constexpr bool // TODO: Replace with concept when it works with MSVC
// concept is_derived_from_specialization_of_system_reference =
// requires(T* t) { detail::to_base_specialization_of_system_reference(t); };
} // namespace detail
// } // namespace detail
/**
* @brief The type for quantity references
@@ -92,33 +83,36 @@ concept is_derived_from_specialization_of_system_reference =
* The following syntaxes are not allowed:
* `2 / s`, `km * 3`, `s / 4`, `70 * km / h`.
*/
template<typename T, Unit U>
struct reference;
// template<typename R, Unit U>
// requires detail::is_derived_from_specialization_of_system_reference<R>
// struct reference<R, U> {
// using system_reference = R;
// static constexpr auto dimension = R::dimension;
// static constexpr U unit{};
// // static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
// };
template<typename R, Unit U>
requires detail::is_derived_from_specialization_of_system_reference<R>
struct reference<R, U> {
using system_reference = R;
static constexpr auto dimension = R::dimension;
static constexpr U unit{};
// static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
};
template<DerivedDimension D, Unit U>
struct reference<D, U> {
template<Dimension D, Unit U>
struct reference {
static constexpr D dimension{};
static constexpr U unit{};
// static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
};
// Reference
/**
* @brief A concept matching all references in the library.
*
* Satisfied by all specializations of @c reference.
*/
template<typename T>
concept Reference = is_specialization_of<T, reference>;
template<Magnitude M, Reference R>
[[nodiscard]] consteval reference<decltype(R::dimension), decltype(M{} * R::unit)> operator*(M, R)
{
return {};
}
// template<Magnitude M, Reference R>
// requires requires { typename R::system_reference; }
// [[nodiscard]] consteval reference<typename R::system_reference, decltype(M{} * R::unit)> operator*(M, R)
// {
// return {};
// }
template<Reference R1, Reference R2>
[[nodiscard]] consteval reference<decltype(R1::dimension * R2::dimension), decltype(R1::unit * R2::unit)> operator*(R1,
@@ -134,29 +128,28 @@ template<Reference R1, Reference R2>
return {};
}
// TODO Update when quantity is done
// template<Representation Rep>
// [[nodiscard]] friend constexpr Quantity auto operator*(const Rep& lhs, reference)
template<typename Rep, Reference R>
template<Representation Rep, Reference R>
[[nodiscard]] constexpr quantity<R{}, Rep> operator*(const Rep& lhs, R)
{
return quantity<R{}, Rep>(lhs);
}
// friend void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, reference) = delete;
void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete;
// TODO will use deducing this
template<typename Child, Dimension auto Dim, Unit auto CoU>
template<Dimension auto Dim, Unit auto CoU>
struct system_reference {
static constexpr auto dimension = Dim;
static constexpr auto coherent_unit = CoU;
template<Unit U>
// requires same_unit_reference<CoU, U>
[[nodiscard]] consteval reference<Child, U> operator[](U) const
// requires same_unit_reference<CoU, U>
[[nodiscard]] constexpr reference<std::remove_const_t<decltype(dimension)>, U> operator[](U) const
{
return {};
}
};
inline constexpr struct dimensionless : system_reference<one_dim, one> {
} dimensionless;
} // namespace units
+145 -41
View File
@@ -75,6 +75,12 @@ namespace detail {
template<Magnitude auto M, typename U>
void to_base_scaled_unit(const volatile scaled_unit<M, U>*);
template<typename T>
inline constexpr bool is_specialization_of_scaled_unit = false;
template<Magnitude auto M, typename U>
inline constexpr bool is_specialization_of_scaled_unit<scaled_unit<M, U>> = true;
} // namespace detail
/**
@@ -98,14 +104,106 @@ concept NamedUnit = Unit<T> && detail::is_named<T>;
template<Unit auto U1, Unit auto U2>
struct same_unit_reference : is_same<typename decltype(U1)::reference, typename decltype(U2)::reference> {};
namespace detail {
template<Unit U1, Unit U2>
struct unit_less : std::bool_constant<type_name<U1>() < type_name<U2>()> {};
template<typename T1, typename T2>
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
/**
* @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions
*
* @tparam Es Exponents of potentially derived dimensions
*/
// template<typename NumList, typename DenList>
// struct unit_extract;
// template<>
// struct unit_extract<type_list<>, type_list<>> {
// using num = type_list<>;
// using den = type_list<>;
// };
// template<typename T, typename... NRest, typename... Dens>
// requires BaseUnit<T> || BaseUnit<typename T::factor>
// struct unit_extract<type_list<T, NRest...>, type_list<Dens...>> {
// using impl = unit_extract<type_list<NRest...>, type_list<Dens...>>;
// using num = type_list_push_front<typename impl::num, T>;
// using den = TYPENAME impl::den;
// };
// template<typename T, typename... DRest>
// requires BaseUnit<T> || BaseUnit<typename T::factor>
// struct unit_extract<type_list<>, type_list<T, DRest...>> {
// using impl = unit_extract<type_list<>, type_list<DRest...>>;
// using num = TYPENAME impl::num;
// using den = type_list_push_front<typename impl::den, T>;
// };
// template<DerivedUnit T, typename... NRest, typename... Dens>
// struct unit_extract<type_list<T, NRest...>, type_list<Dens...>> :
// unit_extract<type_list_push_back<typename T::normalized_num, NRest...>,
// type_list_push_back<typename T::normalized_den, Dens...>> {};
// template<DerivedUnit T, int... Ints, typename... NRest, typename... Dens>
// struct unit_extract<type_list<power<T, Ints...>, NRest...>, type_list<Dens...>> :
// unit_extract<type_list_push_back<typename expr_power<typename T::normalized_num, power<T, Ints...>::num,
// power<T, Ints...>::den>::type,
// NRest...>,
// type_list_push_back<typename expr_power<typename T::normalized_den, power<T, Ints...>::num,
// power<T, Ints...>::den>::type,
// Dens...>> {};
// template<DerivedUnit T, typename... DRest>
// struct unit_extract<type_list<>, type_list<T, DRest...>> :
// unit_extract<typename T::normalized_den, type_list_push_back<typename T::normalized_num, DRest...>> {};
// template<DerivedUnit T, int... Ints, typename... DRest>
// struct unit_extract<type_list<>, type_list<power<T, Ints...>, DRest...>> :
// unit_extract<typename expr_power<typename T::normalized_den, power<T, Ints...>::num, power<T,
// Ints...>::den>::type,
// type_list_push_back<typename expr_power<typename T::normalized_num, power<T, Ints...>::num,
// power<T, Ints...>::den>::type,
// DRest...>> {};
/**
* @brief Converts user provided derived dimension specification into a valid units::normalized_dimension definition
*
* User provided definition of a derived dimension may contain the same base dimension repeated more than once on the
* list possibly hidden in other derived units provided by the user. The process here should:
* 1. Extract derived dimensions into exponents of base dimensions.
* 2. Sort the exponents so the same dimensions are placed next to each other.
* 3. Consolidate contiguous range of exponents of the same base dimensions to a one (or possibly zero) exponent for
* this base dimension.
*/
template<typename OneTypeBase, typename... Us>
struct normalized_unit : detail::expr_fractions<OneTypeBase, Us...> {
// private:
// using base = detail::expr_fractions<OneTypeBase, Us...>;
// using extracted = unit_extract<typename base::num, typename base::den>;
// using num_list = expr_consolidate<type_list_sort<typename extracted::num, type_list_of_unit_less>>;
// using den_list = expr_consolidate<type_list_sort<typename extracted::den, type_list_of_unit_less>>;
// using simple = expr_simplify<num_list, den_list, type_list_of_unit_less>;
// public:
// using normalized_num = TYPENAME simple::num;
// using normalized_den = TYPENAME simple::den;
};
} // namespace detail
// TODO add checking for `per` and power elements as well
template<typename T>
concept UnitSpec = Unit<T> || is_specialization_of<T, per> || detail::is_specialization_of_power<T>;
// User should not instantiate this type!!!
// It should not be exported from the module
template<UnitSpec... Us>
struct derived_unit : detail::expr_fractions<derived_unit<>, Us...>, scaled_unit<mag<1>, derived_unit<Us...>> {};
// : detail::normalized_dimension<derived_dimension<>, Ds...> {};
struct derived_unit : detail::normalized_unit<derived_unit<>, Us...>, scaled_unit<mag<1>, derived_unit<Us...>> {
static constexpr bool is_base = false;
};
/**
* @brief Unit one
@@ -129,28 +227,13 @@ struct named_unit;
template<basic_symbol_text Symbol>
struct named_unit<Symbol> : scaled_unit<mag<1>, named_unit<Symbol>> {
static constexpr auto symbol = Symbol;
static constexpr bool is_base = true;
};
template<basic_symbol_text Symbol, auto U>
requires is_specialization_of<std::remove_const_t<decltype(U)>, derived_unit>
template<basic_symbol_text Symbol, Unit auto U>
struct named_unit<Symbol, U> : decltype(U) {
static constexpr auto symbol = Symbol;
};
/**
* @brief A named scaled unit
*
* Defines a new named unit that is a scaled version of another unit.
* A named unit may be composed with a prefix to create a prefixed_unit.
*
* @tparam Symbol a short text representation of the unit
* @tparam M the Magnitude by which to scale U
* @tparam U a reference unit to scale
*/
template<basic_symbol_text Symbol, Magnitude auto M, Unit auto U>
struct named_scaled_unit : scaled_unit<M* decltype(U)::mag, typename decltype(U)::reference> {
static constexpr auto symbol = Symbol;
static constexpr bool is_base = decltype(U)::is_base;
};
/**
@@ -164,15 +247,12 @@ struct named_scaled_unit : scaled_unit<M* decltype(U)::mag, typename decltype(U)
* @tparam P prefix to be appied to the reference unit
* @tparam U reference unit
*/
// template<typename Child, Prefix P, NamedUnit U>
// requires detail::can_be_prefixed<U>
// struct prefixed_unit : downcast_dispatch<Child, scaled_unit<P::mag * U::mag, typename U::reference>> {
// static constexpr auto symbol = P::symbol + U::symbol;
// };
template<basic_symbol_text Symbol, Magnitude auto M, NamedUnit auto U>
struct prefixed_unit : scaled_unit<M* decltype(U)::mag, typename decltype(U)::reference> {};
// requires detail::can_be_prefixed<U>
struct prefixed_unit : scaled_unit<M* decltype(U)::mag, typename decltype(U)::reference> {
// static constexpr auto symbol = symbol + decltype(U)::symbol;
static constexpr bool is_base = decltype(U)::is_base;
};
/**
* @brief A coherent unit of a derived quantity
@@ -187,11 +267,11 @@ struct prefixed_unit : scaled_unit<M* decltype(U)::mag, typename decltype(U)::re
namespace detail {
template<basic_symbol_text Symbol>
void is_named_impl(const volatile named_unit<Symbol>*);
template<basic_symbol_text Symbol, auto... Args>
void is_named_impl(const volatile named_unit<Symbol, Args...>*);
template<basic_symbol_text Symbol, Magnitude auto M, auto U>
void is_named_impl(const volatile named_scaled_unit<Symbol, M, U>*);
template<basic_symbol_text Symbol, Magnitude auto M, NamedUnit auto U>
void is_named_impl(const volatile prefixed_unit<Symbol, M, U>*);
template<Unit U>
inline constexpr bool is_named<U> = requires(U * u) { is_named_impl(u); };
@@ -211,15 +291,20 @@ inline constexpr bool is_named<U> = requires(U * u) { is_named_impl(u); };
// template<Magnitude auto M, typename U>
// inline constexpr bool can_be_prefixed<scaled_unit<M, U>> = can_be_prefixed<typename U::reference>;
template<Unit U1, Unit U2>
struct unit_less : std::bool_constant<type_name<U1>() < type_name<U2>()> {};
template<typename T1, typename T2>
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
} // namespace detail
template<Magnitude M, Unit U>
[[nodiscard]] consteval Unit auto operator*(M mag, U)
{
return scaled_unit<mag, U>{};
}
template<Magnitude M1, Magnitude auto M2, typename U>
[[nodiscard]] consteval Unit auto operator*(M1 mag, scaled_unit<M2, U>)
{
return scaled_unit<mag * M2, U>{};
}
template<Unit U1, Unit U2>
[[nodiscard]] consteval Unit auto operator*(U1, U2)
{
@@ -242,9 +327,16 @@ template<Unit U>
template<Unit U1, Unit U2>
[[nodiscard]] consteval bool operator==(U1, U2)
{
return false;
return is_same_v<U1, U2>;
}
template<Unit U1, Unit U2>
[[nodiscard]] consteval bool equivalent(U1, U2)
{
return true; // TODO implement this
}
// template<BaseDimension D1, BaseDimension D2>
// constexpr bool operator==(D1, D2)
// {
@@ -274,4 +366,16 @@ template<Unit U1, Unit U2>
// std::is_same_v<typename D1::normalized_den, typename D2::normalized_den>;
// }
template<Unit U>
struct square_ : decltype(U{} * U{}) {};
template<Unit auto U>
inline constexpr square_<std::remove_const_t<decltype(U)>> square;
template<Unit U>
struct cubic_ : decltype(U{} * U{} * U{}) {};
template<Unit auto U>
inline constexpr cubic_<std::remove_const_t<decltype(U)>> cubic;
} // namespace units