refactor: V2 design update
This commit is contained in:
1 parent
4411b8ea6d
commit
4a49bdda05
146 files changed
+3076
-13622
No files matched your search
@@ -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
@@ -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__
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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...>{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
Reference in new issue
Block a user