refactor: 💥 is_XXX customization points for representation types removed

This commit is contained in:
Mateusz Pusz
2024-11-26 14:48:08 +01:00
parent 083ef6adb1
commit 465f88d500
21 changed files with 151 additions and 205 deletions

View File

@ -62,9 +62,6 @@ public:
}
};
template<typename T, auto Min, auto Max>
constexpr bool mp_units::is_scalar<ranged_representation<T, Min, Max>> = mp_units::is_scalar<T>;
template<typename T, auto Min, auto Max, typename Char>
struct MP_UNITS_STD_FMT::formatter<ranged_representation<T, Min, Max>, Char> : formatter<T, Char> {
template<typename FormatContext>

View File

@ -113,9 +113,6 @@ public:
= default;
};
template<typename T, typename Validator>
constexpr bool mp_units::is_scalar<validated_type<T, Validator>> = mp_units::is_scalar<T>;
template<typename CharT, typename Traits, typename T, typename Validator>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,

View File

@ -38,10 +38,6 @@ import mp_units;
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex2
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::SystemState auto& initial)

View File

@ -38,10 +38,6 @@ import mp_units;
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex3
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::SystemState auto& initial)

View File

@ -38,10 +38,6 @@ import mp_units;
// Based on: https://www.kalmanfilter.net/alphabeta.html#ex4
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::SystemState auto& initial)

View File

@ -124,6 +124,13 @@ public:
return os << v.value() << " ± " << v.uncertainty();
}
[[nodiscard]] friend constexpr measurement abs(const measurement& v)
requires requires { abs(v.value()); } || requires { std::abs(v.value()); }
{
using std::abs;
return measurement(abs(v.value()), v.uncertainty());
}
private:
value_type value_{};
value_type uncertainty_{};
@ -131,12 +138,8 @@ private:
} // namespace
template<typename T>
constexpr bool mp_units::is_scalar<measurement<T>> = true;
template<typename T>
constexpr bool mp_units::is_vector<measurement<T>> = true;
static_assert(mp_units::RepresentationOf<measurement<double>, mp_units::quantity_character::scalar>);
static_assert(mp_units::RepresentationOf<measurement<double>, mp_units::quantity_character::vector>);
namespace {

View File

@ -40,12 +40,6 @@ import mp_units;
#include <mp-units/systems/si.h>
#endif
// allows standard gravity (acceleration) and weight (force) to be expressed with scalar representation
// types instead of requiring the usage of Linear Algebra library for this simple example
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
namespace {
using namespace mp_units;

View File

@ -33,10 +33,6 @@ import mp_units;
#include <mp-units/systems/si.h>
#endif
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
int main()
{
using namespace mp_units;

View File

@ -37,10 +37,6 @@ import mp_units;
#include <mp-units/systems/si.h>
#endif
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
namespace {
using namespace mp_units;

View File

@ -215,7 +215,7 @@ public:
lhs._coordinates_[2] == rhs._coordinates_[2];
}
[[nodiscard]] friend constexpr T norm(const cartesian_vector& vec)
[[nodiscard]] friend constexpr T magnitude(const cartesian_vector& vec)
requires treat_as_floating_point<T>
{
return vec.magnitude();
@ -263,9 +263,6 @@ template<typename Arg, typename... Args>
requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t<Arg, Args...>; }
cartesian_vector(Arg, Args...) -> cartesian_vector<std::common_type_t<Arg, Args...>>;
template<class T>
constexpr bool is_vector<cartesian_vector<T>> = true;
} // namespace mp_units
#if MP_UNITS_HOSTED

View File

@ -38,6 +38,6 @@ import std;
namespace mp_units {
template<typename T>
constexpr bool is_complex<std::complex<T>> = true;
constexpr bool disable_scalar<std::complex<T>> = true;
}
} // namespace mp_units

View File

@ -60,49 +60,20 @@ constexpr bool treat_as_floating_point =
std::is_floating_point_v<value_type_t<Rep>>;
#endif
/**
* @brief Specifies a type to have a scalar character
*
* A scalar is a physical quantity that has magnitude but no direction.
*/
template<typename Rep>
constexpr bool is_scalar = std::is_floating_point_v<Rep> || (std::is_integral_v<Rep> && !is_same_v<Rep, bool>);
[[deprecated("`is_scalar` is no longer necessary and can simply be removed")]]
constexpr bool is_scalar = false;
/**
* @brief Specifies a type to have a complex character
*
* A complex is a physical quantity that has a complex representation type.
*/
template<typename Rep>
[[deprecated("`is_complex` is no longer necessary and can simply be removed")]]
constexpr bool is_complex = false;
/**
* @brief Specifies a type to have a vector character
*
* Vectors are physical quantities that possess both magnitude and direction
* and whose operations obey the axioms of a vector space.
*
* In specific cases a scalar can represent a vector with the default direction.
* If that is the intent, a user should provide a partial specialization:
*
* @code{.cpp}
* template<class T>
* requires mp_units::is_scalar<T>
* constexpr bool mp_units::is_vector<T> = true;
* @endcode
*/
template<typename Rep>
[[deprecated("`is_vector` is no longer necessary and can simply be removed")]]
constexpr bool is_vector = false;
/**
* @brief Specifies a type to have a tensor character
*
* Tensors can be used to describe more general physical quantities.
*
* A vector is a tensor of the first order and a scalar is a tensor of order zero.
* Similarly to `is_vector` a partial specialization is needed in such cases.
*/
template<typename Rep>
[[deprecated("`is_tensor` is no longer necessary and can simply be removed")]]
constexpr bool is_tensor = false;
/**

View File

@ -43,6 +43,10 @@ template<typename T>
constexpr bool is_derived_from_specialization_of_quantity =
requires(T* type) { to_base_specialization_of_quantity(type); };
template<typename T>
requires is_derived_from_specialization_of_quantity<T>
constexpr bool is_quantity<T> = true;
} // namespace detail
/**

View File

@ -62,44 +62,26 @@ namespace mp_units {
*/
MP_UNITS_EXPORT enum class quantity_character : std::int8_t { scalar, complex, vector, tensor };
MP_UNITS_EXPORT template<typename T>
constexpr bool disable_scalar = false;
// TODO clang-18 (and possibly other compilers) complain about the duplicated definition if `inline` is not used
template<>
inline constexpr bool disable_scalar<bool> = true;
namespace detail {
template<typename T>
concept WeaklyRegular = std::copyable<T> && std::equality_comparable<T>;
template<typename T>
concept Scalar = is_scalar<T>;
template<typename T>
concept Complex = is_complex<T>;
template<typename T>
concept Vector = is_vector<T>;
template<typename T>
concept Tensor = is_tensor<T>;
template<typename T, quantity_character Ch>
concept IsOfCharacter =
(Ch == quantity_character::scalar && is_scalar<T>) || (Ch == quantity_character::complex && is_complex<T>) ||
(Ch == quantity_character::vector && is_vector<T>) || (Ch == quantity_character::tensor && is_tensor<T>);
template<typename T>
using scaling_factor_type_t = conditional<treat_as_floating_point<T>, long double, std::intmax_t>;
template<typename T>
concept ScalarRepresentation = Scalar<T> && WeaklyRegular<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
{ a* f } -> Scalar;
{ f* a } -> Scalar;
{ a / f } -> Scalar;
concept Scalar = (!disable_scalar<T>) && WeaklyRegular<T> && requires(T a, T b) {
// scalar operations
{ -a } -> Scalar;
{ a + b } -> Scalar;
{ a - b } -> Scalar;
{ a* b } -> Scalar;
{ a / b } -> Scalar;
{ -a } -> std::common_with<T>;
{ a + b } -> std::common_with<T>;
{ a - b } -> std::common_with<T>;
{ a* b } -> std::common_with<T>;
{ a / b } -> std::common_with<T>;
};
namespace real_impl {
@ -107,7 +89,8 @@ namespace real_impl {
void real() = delete; // poison pill
struct real_t {
[[nodiscard]] constexpr ScalarRepresentation auto operator()(const WeaklyRegular auto& clx) const
[[nodiscard]] constexpr Scalar auto operator()(const WeaklyRegular auto& clx) const
requires requires { clx.real(); } || requires { real(clx); }
{
if constexpr (requires { clx.real(); })
return clx.real();
@ -131,7 +114,8 @@ namespace detail::imag_impl {
void imag() = delete; // poison pill
struct imag_t {
[[nodiscard]] constexpr ScalarRepresentation auto operator()(const WeaklyRegular auto& clx) const
[[nodiscard]] constexpr Scalar auto operator()(const WeaklyRegular auto& clx) const
requires requires { clx.imag(); } || requires { imag(clx); }
{
if constexpr (requires { clx.imag(); })
return clx.imag();
@ -153,13 +137,17 @@ namespace detail::modulus_impl {
void modulus() = delete; // poison pill
struct modulus_t {
[[nodiscard]] constexpr ScalarRepresentation auto operator()(const WeaklyRegular auto& clx) const
[[nodiscard]] constexpr Scalar auto operator()(const WeaklyRegular auto& clx) const
requires requires { clx.modulus(); } || requires { modulus(clx); } || requires { clx.abs(); } ||
requires { abs(clx); }
{
if constexpr (requires { clx.modulus(); })
return clx.modulus();
else if constexpr (requires { modulus(clx); })
return modulus(clx);
// `std` made a precedence of using `abs` for modulo on `std::complex`
else if constexpr (requires { clx.abs(); })
return clx.abs();
else if constexpr (requires { abs(clx); })
return abs(clx);
}
@ -173,100 +161,140 @@ MP_UNITS_EXPORT inline constexpr ::mp_units::detail::modulus_impl::modulus_t mod
}
MP_UNITS_EXPORT template<typename T>
constexpr bool disable_complex = false;
namespace detail {
template<typename T>
concept ComplexRepresentation = Complex<T> && WeaklyRegular<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
// TODO The below conversion to `T` is an exception compared to other representation types
// `std::complex<T>` * `U` do not work, but `std::complex<T>` is convertible from `U`
// Maybe expose this as a customization point?
{ a* T(f) } -> Complex;
{ T(f) * a } -> Complex;
{ a / T(f) } -> Complex;
concept Complex =
(!disable_complex<T>) && WeaklyRegular<T> && Scalar<value_type_t<T>> &&
std::constructible_from<T, value_type_t<T>, value_type_t<T>> && requires(T a, T b, value_type_t<T> s) {
// complex operations
{ -a } -> std::common_with<T>;
{ a + b } -> std::common_with<T>;
{ a - b } -> std::common_with<T>;
{ a* b } -> std::common_with<T>;
{ a / b } -> std::common_with<T>;
{ a* s } -> std::common_with<T>;
{ s* a } -> std::common_with<T>;
{ a / s } -> std::common_with<T>;
::mp_units::real(a);
::mp_units::imag(a);
::mp_units::modulus(a);
};
// complex operations
{ -a } -> Complex;
{ a + b } -> Complex;
{ a - b } -> Complex;
{ a* b } -> Complex;
{ a / b } -> Complex;
::mp_units::real(a);
::mp_units::imag(a);
::mp_units::modulus(a);
};
namespace magnitude_impl {
// TODO how to check for a complex(Scalar, Scalar) -> Complex?
void magnitude() = delete; // poison pill
namespace norm_impl {
void norm() = delete; // poison pill
struct norm_t {
[[nodiscard]] constexpr ScalarRepresentation auto operator()(const WeaklyRegular auto& vec) const
struct magnitude_t {
template<WeaklyRegular T>
[[nodiscard]] constexpr Scalar auto operator()(const T& vec) const
requires requires { vec.magnitude(); } || requires { magnitude(vec); } ||
(Scalar<T> &&
(requires { vec.abs(); } || requires { abs(vec); } || (std::is_arithmetic_v<T> && (!is_same_v<T, bool>))))
{
if constexpr (requires { vec.norm(); })
return vec.norm();
else if constexpr (requires { norm(vec); })
return norm(vec);
else if constexpr (requires { vec.magnitude(); })
if constexpr (requires { vec.magnitude(); })
return vec.magnitude();
else if constexpr (requires { magnitude(vec); })
return magnitude(vec);
// TODO Is it a good idea to enable fundamental types to represent vector quantities?
// else if constexpr (is_scalar<T>)
// return std::abs(vec);
// allow scalar types to represent one dimensional vector quantities
if constexpr (Scalar<T>) {
if constexpr (requires { vec.abs(); })
return vec.abs();
else if constexpr (requires { abs(vec); })
return abs(vec);
else if constexpr (std::is_arithmetic_v<T> && (!is_same_v<T, bool>))
return std::abs(vec);
}
}
};
} // namespace norm_impl
} // namespace magnitude_impl
} // namespace detail
inline namespace cpo {
MP_UNITS_EXPORT inline constexpr ::mp_units::detail::norm_impl::norm_t norm;
MP_UNITS_EXPORT inline constexpr ::mp_units::detail::magnitude_impl::magnitude_t magnitude;
}
MP_UNITS_EXPORT template<typename T>
constexpr bool disable_vector = false;
namespace detail {
template<typename T>
concept VectorRepresentation = Vector<T> && WeaklyRegular<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
{ a* f } -> Vector;
{ f* a } -> Vector;
{ a / f } -> Vector;
concept Vector =
(!disable_vector<T>) && WeaklyRegular<T> && Scalar<value_type_t<T>> && requires(T a, T b, value_type_t<T> s) {
// vector operations
{ -a } -> std::common_with<T>;
{ a + b } -> std::common_with<T>;
{ a - b } -> std::common_with<T>;
{ a* s } -> std::common_with<T>;
{ s* a } -> std::common_with<T>;
{ a / s } -> std::common_with<T>;
::mp_units::magnitude(a);
// TODO should we check for the below as well (e.g., when `size() > 1` or `2`)
// { zero_vector<T>() } -> Vector;
// { unit_vector(a) } -> Vector;
// { scalar_product(a, b) } -> Scalar;
// { vector_product(a, b) } -> Vector;
// { tensor_product(a, b) } -> Tensor2;
};
// vector operations
{ -a } -> Vector;
{ a + b } -> Vector;
{ a - b } -> Vector;
::mp_units::norm(a);
// TBD
// { zero_vector<T>() } -> Vector;
// { unit_vector(a) } -> Vector;
// { scalar_product(a, b) } -> Scalar;
// { vector_product(a, b) } -> Vector;
// { tensor_product(a, b) } -> Tensor2;
// divergence(a)
// rotation(a)
// TODO provide when some actual operations will be required
// template<typename T>
// concept Tensor = is_tensor<T> && WeaklyRegular<T>; // && requires(T a, T b) {
// // tensor operations
// // { tensor_product(a, b) } -> Tensor4;
// // { inner_product(a, b) } -> Tensor2;
// // { scalar_product(a, b) } -> Scalar;
// //};
template<typename T>
constexpr bool is_quantity = false;
template<typename T>
using scaling_factor_type_t = conditional<treat_as_floating_point<T>, long double, std::intmax_t>;
template<typename T>
concept ScalarRepresentation = (!is_quantity<T>) && Scalar<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
{ a* f } -> std::common_with<T>;
{ f* a } -> std::common_with<T>;
{ a / f } -> std::common_with<T>;
};
template<typename T>
concept TensorRepresentation = Tensor<T> && WeaklyRegular<T>; // && requires(T a, T b) {
// TBD
// tensor operations
// { tensor_product(a, b) } -> Tensor4;
// { inner_product(a, b) } -> Tensor2;
// { scalar_product(a, b) } -> Scalar;
//};
concept ComplexRepresentation = (!is_quantity<T>) && Complex<T> && requires(T a, T b, scaling_factor_type_t<T> f) {
// scaling
// TODO The below conversion to `T` is an exception compared to other representation types
// `std::complex<T>` * `U` do not work, but `std::complex<T>` is convertible from `U`
// Maybe expose this as a customization point?
{ a* T(f) } -> std::common_with<T>;
{ T(f) * a } -> std::common_with<T>;
{ a / T(f) } -> std::common_with<T>;
};
template<typename T>
concept VectorRepresentation = (!is_quantity<T>) && Vector<T>;
// template<typename T>
// concept TensorRepresentation = (!is_quantity<T>) && Tensor<T>;
template<typename T, quantity_character Ch>
concept IsOfCharacter =
(Ch == quantity_character::scalar && Scalar<T>) || (Ch == quantity_character::complex && Complex<T>) ||
(Ch == quantity_character::vector && Vector<T>); // || (Ch == quantity_character::tensor && Tensor<T>);
} // namespace detail
MP_UNITS_EXPORT template<typename T>
concept Representation = detail::ScalarRepresentation<T> || detail::ComplexRepresentation<T> ||
detail::VectorRepresentation<T> || detail::TensorRepresentation<T>;
detail::VectorRepresentation<T>; // || detail::TensorRepresentation<T>;
MP_UNITS_EXPORT template<typename T, auto V>
concept RepresentationOf =

View File

@ -43,12 +43,6 @@ import mp_units;
template<typename Rep = double>
using vector = STD_LA::fixed_size_column_vector<Rep, 3>;
template<class Rep>
constexpr bool mp_units::treat_as_floating_point<vector<Rep>> = mp_units::treat_as_floating_point<Rep>;
template<typename Rep>
constexpr bool mp_units::is_vector<vector<Rep>> = true;
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const vector<Rep>& v)
{
@ -87,8 +81,6 @@ template<typename T, typename U>
}
template<Quantity Q1, Quantity Q2>
requires is_vector<typename Q1::rep> && is_vector<typename Q2::rep> &&
requires(typename Q1::rep v1, typename Q2::rep v2) { cross_product(v1, v2); }
[[nodiscard]] constexpr QuantityOf<Q1::quantity_spec * Q2::quantity_spec> auto cross_product(const Q1& q1, const Q2& q2)
{
return cross_product(q1.numerical_value_ref_in(q1.unit), q2.numerical_value_ref_in(q2.unit)) *
@ -299,10 +291,6 @@ TEST_CASE("vector quantity", "[la]")
}
}
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
TEST_CASE("vector of quantities", "[la]")
{
SECTION("cast of unit")

View File

@ -278,13 +278,16 @@ static_assert(!Representation<std::chrono::seconds>);
// RepresentationOf
static_assert(RepresentationOf<int, quantity_character::scalar>);
static_assert(!RepresentationOf<int, quantity_character::complex>);
static_assert(!RepresentationOf<int, quantity_character::vector>);
static_assert(RepresentationOf<int, quantity_character::vector>);
static_assert(!RepresentationOf<int, quantity_character::tensor>);
static_assert(RepresentationOf<double, quantity_character::scalar>);
static_assert(!RepresentationOf<double, quantity_character::complex>);
static_assert(!RepresentationOf<double, quantity_character::vector>);
static_assert(RepresentationOf<double, quantity_character::vector>);
static_assert(!RepresentationOf<double, quantity_character::tensor>);
static_assert(!RepresentationOf<bool, quantity_character::scalar>);
static_assert(!RepresentationOf<bool, quantity_character::complex>);
static_assert(!RepresentationOf<bool, quantity_character::vector>);
static_assert(!RepresentationOf<bool, quantity_character::tensor>);
static_assert(!RepresentationOf<std::optional<int>, quantity_character::scalar>);
#if MP_UNITS_HOSTED
static_assert(RepresentationOf<std::complex<double>, quantity_character::complex>);

View File

@ -60,9 +60,6 @@ public:
} // namespace
template<typename T>
constexpr bool mp_units::is_scalar<min_impl<T>> = true;
template<typename T, typename U>
struct std::common_type<min_impl<T>, min_impl<U>> : std::type_identity<min_impl<std::common_type_t<T, U>>> {};
template<typename T, typename U>

View File

@ -24,10 +24,6 @@
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/si.h>
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
namespace {
using namespace mp_units;

View File

@ -26,10 +26,6 @@
#include <mp-units/systems/si/constants.h>
#include <mp-units/systems/si/units.h>
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
namespace {
using namespace mp_units;

View File

@ -22,10 +22,6 @@
#include <mp-units/systems/natural.h>
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
namespace {
using namespace mp_units;

View File

@ -47,9 +47,6 @@ import std;
#endif
#endif
template<>
constexpr bool mp_units::is_vector<int> = true;
namespace {
using namespace mp_units;
@ -74,8 +71,10 @@ concept invalid_types = requires {
requires !requires { typename Q<isq::length, double>; }; // quantity_spec instead of reference
requires !requires { typename Q<isq::length[m], bool>; }; // bool is not a valid representation type
requires !requires { typename Q<isq::length[m], quantity<isq::length[m]>>; }; // quantity used as Rep
requires !requires { typename Q<isq::position_vector[si::metre], double>; }; // vector representation expected
#if MP_UNITS_HOSTED
requires !requires {
typename Q<isq::position_vector[si::metre], std::complex<double>>;
}; // vector representation expected
requires !requires {
typename Q<isq::length[si::metre], cartesian_vector<double>>;
}; // scalar representation expected