mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-07 06:04:27 +02:00
feat: unit symbol text output support added
This commit is contained in:
@@ -1,89 +0,0 @@
|
|||||||
// The MIT License (MIT)
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Mateusz Pusz
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <units/bits/external/fixed_string.h>
|
|
||||||
#include <units/bits/external/text_tools.h>
|
|
||||||
#include <units/derived_dimension.h>
|
|
||||||
#include <units/symbol_text.h>
|
|
||||||
|
|
||||||
namespace units::detail {
|
|
||||||
|
|
||||||
template<bool Divide, std::size_t NegativeExpCount, std::size_t Idx>
|
|
||||||
constexpr auto operator_text()
|
|
||||||
{
|
|
||||||
if constexpr (Idx == 0) {
|
|
||||||
if constexpr (Divide && NegativeExpCount == 1) {
|
|
||||||
return basic_fixed_string("1/");
|
|
||||||
} else {
|
|
||||||
return basic_fixed_string("");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if constexpr (Divide && NegativeExpCount == 1) {
|
|
||||||
return basic_fixed_string("/");
|
|
||||||
} else {
|
|
||||||
return basic_symbol_text("⋅", " ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename E, basic_symbol_text Symbol, std::size_t NegativeExpCount, std::size_t Idx>
|
|
||||||
constexpr auto exp_text()
|
|
||||||
{
|
|
||||||
// get calculation operator + symbol
|
|
||||||
const auto txt = operator_text<(E::num < 0), NegativeExpCount, Idx>() + Symbol;
|
|
||||||
if constexpr (E::den != 1) {
|
|
||||||
// add root part
|
|
||||||
return txt + basic_fixed_string("^(") + regular<abs(E::num)>() + basic_fixed_string("/") + regular<E::den>() +
|
|
||||||
basic_fixed_string(")");
|
|
||||||
} else if constexpr (E::num != 1) {
|
|
||||||
// add exponent part
|
|
||||||
if constexpr (NegativeExpCount > 1) { // no '/' sign here (only negative exponents)
|
|
||||||
return txt + superscript<E::num>();
|
|
||||||
} else if constexpr (E::num != -1) { // -1 is replaced with '/' sign here
|
|
||||||
return txt + superscript<abs(E::num)>();
|
|
||||||
} else {
|
|
||||||
return txt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return txt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Es>
|
|
||||||
inline constexpr int negative_exp_count = ((Es::num < 0 ? 1 : 0) + ... + 0);
|
|
||||||
|
|
||||||
template<typename... Us, typename... Es, std::size_t... Idxs>
|
|
||||||
constexpr auto derived_symbol_text(exponent_list<Es...>, std::index_sequence<Idxs...>)
|
|
||||||
{
|
|
||||||
constexpr auto neg_exp = negative_exp_count<Es...>;
|
|
||||||
return (exp_text<Es, Us::symbol, neg_exp, Idxs>() + ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<DerivedDimension Dim, Unit... Us>
|
|
||||||
constexpr auto derived_symbol_text()
|
|
||||||
{
|
|
||||||
return derived_symbol_text<Us...>(typename Dim::recipe(), std::index_sequence_for<Us...>());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace units::detail
|
|
@@ -1,166 +0,0 @@
|
|||||||
// The MIT License (MIT)
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Mateusz Pusz
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <units/bits/derived_symbol_text.h>
|
|
||||||
#include <units/bits/external/text_tools.h>
|
|
||||||
#include <units/derived_dimension.h>
|
|
||||||
#include <units/prefix.h>
|
|
||||||
#include <units/unit.h>
|
|
||||||
|
|
||||||
namespace units::detail {
|
|
||||||
|
|
||||||
inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10");
|
|
||||||
|
|
||||||
template<Magnitude auto M>
|
|
||||||
constexpr auto magnitude_text()
|
|
||||||
{
|
|
||||||
constexpr auto exp10 = extract_power_of_10(M);
|
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
constexpr auto num_value = get_value<std::intmax_t>(num);
|
|
||||||
constexpr auto den_value = get_value<std::intmax_t>(den);
|
|
||||||
|
|
||||||
|
|
||||||
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
|
|
||||||
return base_multiplier + superscript<exp10>();
|
|
||||||
} else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) {
|
|
||||||
auto txt = basic_fixed_string("[") + regular<num_value>();
|
|
||||||
if constexpr (den_value == 1) {
|
|
||||||
if constexpr (exp10 == 0) {
|
|
||||||
return txt + basic_fixed_string("]");
|
|
||||||
} else {
|
|
||||||
return txt + " " + base_multiplier + superscript<exp10>() + basic_fixed_string("]");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if constexpr (exp10 == 0) {
|
|
||||||
return txt + basic_fixed_string("/") + regular<den_value>() + basic_fixed_string("]");
|
|
||||||
} else {
|
|
||||||
return txt + basic_fixed_string("/") + regular<den_value>() + " " + base_multiplier + superscript<exp10>() +
|
|
||||||
basic_fixed_string("]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return basic_fixed_string("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Unit U, Magnitude auto M, std::size_t SymbolLen>
|
|
||||||
constexpr auto prefix_or_magnitude_text()
|
|
||||||
{
|
|
||||||
if constexpr (M == mag<1>()) {
|
|
||||||
// no ratio/prefix
|
|
||||||
return basic_fixed_string("");
|
|
||||||
} else {
|
|
||||||
// try to form a prefix
|
|
||||||
using prefix = downcast<detail::prefix_base<M>>;
|
|
||||||
|
|
||||||
if constexpr (can_be_prefixed<U> && !is_same_v<prefix, prefix_base<M>>) {
|
|
||||||
// print as a prefixed unit
|
|
||||||
return prefix::symbol;
|
|
||||||
} else {
|
|
||||||
// print as a ratio of the coherent unit
|
|
||||||
constexpr auto txt = magnitude_text<M>();
|
|
||||||
if constexpr (SymbolLen > 0 && txt.standard().size() > 0)
|
|
||||||
return txt + basic_fixed_string(" ");
|
|
||||||
else
|
|
||||||
return txt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Es, std::size_t... Idxs>
|
|
||||||
constexpr auto derived_dimension_unit_text(exponent_list<Es...>, std::index_sequence<Idxs...>)
|
|
||||||
{
|
|
||||||
return (exp_text<Es, dimension_unit<typename Es::dimension>::symbol, negative_exp_count<Es...>, Idxs>() + ... +
|
|
||||||
basic_symbol_text(basic_fixed_string("")));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Es>
|
|
||||||
constexpr auto derived_dimension_unit_text(exponent_list<Es...> list)
|
|
||||||
{
|
|
||||||
return derived_dimension_unit_text(list, std::index_sequence_for<Es...>());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Exponent... Es>
|
|
||||||
constexpr auto exponent_list_with_named_units(exponent_list<Es...>);
|
|
||||||
|
|
||||||
template<Exponent Exp>
|
|
||||||
constexpr auto exponent_list_with_named_units(Exp)
|
|
||||||
{
|
|
||||||
using dim = TYPENAME Exp::dimension;
|
|
||||||
if constexpr (NamedUnit<dimension_unit<dim>>) {
|
|
||||||
return exponent_list<Exp>();
|
|
||||||
} else {
|
|
||||||
using recipe = TYPENAME dim::recipe;
|
|
||||||
return exponent_list_with_named_units(recipe());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Exponent... Es>
|
|
||||||
constexpr auto exponent_list_with_named_units(exponent_list<Es...>)
|
|
||||||
{
|
|
||||||
return type_list_join<decltype(exponent_list_with_named_units(Es()))...>();
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto exponent_list_with_named_units(exponent_list<> empty) { return empty; }
|
|
||||||
|
|
||||||
template<Dimension Dim>
|
|
||||||
constexpr auto derived_dimension_unit_text()
|
|
||||||
{
|
|
||||||
using recipe = TYPENAME Dim::recipe;
|
|
||||||
return derived_dimension_unit_text(exponent_list_with_named_units(recipe()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
// TODO replace with `inline constexpr bool has_symbol` when MSVC cathes up
|
|
||||||
concept has_symbol = requires { T::symbol; };
|
|
||||||
|
|
||||||
template<Dimension Dim, Unit U>
|
|
||||||
constexpr auto unit_text()
|
|
||||||
{
|
|
||||||
if constexpr (has_symbol<U>) {
|
|
||||||
// already has a symbol so print it
|
|
||||||
return U::symbol;
|
|
||||||
} else {
|
|
||||||
// print as a prefix or ratio of a coherent unit
|
|
||||||
using coherent_unit = dimension_unit<Dim>;
|
|
||||||
|
|
||||||
constexpr auto symbol_text = []() {
|
|
||||||
if constexpr (has_symbol<coherent_unit>)
|
|
||||||
return coherent_unit::symbol;
|
|
||||||
else
|
|
||||||
return derived_dimension_unit_text<Dim>();
|
|
||||||
}();
|
|
||||||
|
|
||||||
constexpr auto prefix_txt =
|
|
||||||
prefix_or_magnitude_text<U, U::mag / coherent_unit::mag, symbol_text.standard().size()>();
|
|
||||||
return prefix_txt + symbol_text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace units::detail
|
|
@@ -146,15 +146,6 @@ struct power_v {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Deduction guides for base_power: only permit deducing integral bases.
|
|
||||||
*/
|
|
||||||
// template<std::integral T, std::convertible_to<ratio> U>
|
|
||||||
// base_power(T, U) -> base_power<std::intmax_t>;
|
|
||||||
// template<std::integral T>
|
|
||||||
// base_power(T) -> base_power<std::intmax_t>;
|
|
||||||
|
|
||||||
// Implementation for PowerV concept (below).
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline constexpr bool is_specialization_of_power_v = false;
|
inline constexpr bool is_specialization_of_power_v = false;
|
||||||
|
|
||||||
@@ -163,15 +154,39 @@ inline constexpr bool is_specialization_of_power_v<power_v<V, Ints...>> = true;
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept MagnitudeSpec = PowerVBase<T> || detail::is_specialization_of_power_v<T>;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<MagnitudeSpec Element>
|
||||||
|
[[nodiscard]] consteval auto get_base(Element element)
|
||||||
|
{
|
||||||
|
if constexpr (detail::is_specialization_of_power_v<Element>)
|
||||||
|
return Element::base;
|
||||||
|
else
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<MagnitudeSpec Element>
|
||||||
|
[[nodiscard]] consteval ratio get_exponent(Element)
|
||||||
|
{
|
||||||
|
if constexpr (detail::is_specialization_of_power_v<Element>)
|
||||||
|
return Element::exponent;
|
||||||
|
else
|
||||||
|
return ratio{1};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Concept to detect whether a _type_ is a valid base power.
|
* @brief Concept to detect whether a _type_ is a valid base power.
|
||||||
*
|
*
|
||||||
* Note that this is somewhat incomplete. We must also detect whether a _value_ of that type is valid for use with
|
* Note that this is somewhat incomplete. We must also detect whether a _value_ of that type is valid for use with
|
||||||
* `magnitude<...>`. We will defer that second check to the constraints on the `magnitude` template.
|
* `magnitude<...>`. We will defer that second check to the constraints on the `magnitude` template.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
|
||||||
concept PowerV = detail::is_specialization_of_power_v<T>;
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// We do not want magnitude type to have the `l` literal after a value for a small integral number.
|
// We do not want magnitude type to have the `l` literal after a value for a small integral number.
|
||||||
@@ -205,78 +220,82 @@ template<PowerVBase auto V, ratio R>
|
|||||||
} else {
|
} else {
|
||||||
return power_v<shortT, R.num, R.den>{};
|
return power_v<shortT, R.num, R.den>{};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// consteval auto inverse(PowerV auto bp) { return power_v_or_T<bp.base, bp.exponent * (-1)>(); }
|
[[nodiscard]] consteval auto inverse(MagnitudeSpec auto el)
|
||||||
|
{
|
||||||
|
return power_v_or_T<get_base(el), get_exponent(el) * (-1)>();
|
||||||
|
}
|
||||||
|
|
||||||
// `widen_t` gives the widest arithmetic type in the same category, for intermediate computations.
|
// `widen_t` gives the widest arithmetic type in the same category, for intermediate computations.
|
||||||
// template<typename T>
|
template<typename T>
|
||||||
// using widen_t = conditional<std::is_arithmetic_v<T>,
|
using widen_t = conditional<std::is_arithmetic_v<T>,
|
||||||
// conditional<std::is_floating_point_v<T>, long double,
|
conditional<std::is_floating_point_v<T>, long double,
|
||||||
// conditional<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>,
|
conditional<std::is_signed_v<T>, std::intmax_t, std::uintmax_t>>,
|
||||||
// T>;
|
T>;
|
||||||
|
|
||||||
// Raise an arbitrary arithmetic type to a positive integer power at compile time.
|
// Raise an arbitrary arithmetic type to a positive integer power at compile time.
|
||||||
// template<typename T>
|
template<typename T>
|
||||||
// constexpr T int_power(T base, std::integral auto exp)
|
[[nodiscard]] consteval T int_power(T base, std::integral auto exp)
|
||||||
// {
|
{
|
||||||
// // As this function should only be called at compile time, the exceptions herein function as
|
// As this function should only be called at compile time, the exceptions herein function as
|
||||||
// // "parameter-compatible static_asserts", and should not result in exceptions at runtime.
|
// "parameter-compatible static_asserts", and should not result in exceptions at runtime.
|
||||||
// if (exp < 0) {
|
if (exp < 0) {
|
||||||
// throw std::invalid_argument{"int_power only supports positive integer powers"};
|
throw std::invalid_argument{"int_power only supports positive integer powers"};
|
||||||
// }
|
}
|
||||||
|
|
||||||
// constexpr auto checked_multiply = [](auto a, auto b) {
|
constexpr auto checked_multiply = [](auto a, auto b) {
|
||||||
// const auto result = a * b;
|
const auto result = a * b;
|
||||||
// UNITS_DIAGNOSTIC_PUSH
|
UNITS_DIAGNOSTIC_PUSH
|
||||||
// UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL
|
UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL
|
||||||
// if (result / a != b) {
|
if (result / a != b) {
|
||||||
// throw std::overflow_error{"Wraparound detected"};
|
throw std::overflow_error{"Wraparound detected"};
|
||||||
// }
|
}
|
||||||
// UNITS_DIAGNOSTIC_POP
|
UNITS_DIAGNOSTIC_POP
|
||||||
// return result;
|
return result;
|
||||||
// };
|
};
|
||||||
|
|
||||||
// constexpr auto checked_square = [checked_multiply](auto a) { return checked_multiply(a, a); };
|
constexpr auto checked_square = [checked_multiply](auto a) { return checked_multiply(a, a); };
|
||||||
|
|
||||||
// // TODO(chogg): Unify this implementation with the one in pow.h. That one takes its exponent as a
|
// TODO(chogg): Unify this implementation with the one in pow.h. That one takes its exponent as a
|
||||||
// // template parameter, rather than a function parameter.
|
// template parameter, rather than a function parameter.
|
||||||
|
|
||||||
// if (exp == 0) {
|
if (exp == 0) {
|
||||||
// return T{1};
|
return T{1};
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (exp % 2 == 1) {
|
if (exp % 2 == 1) {
|
||||||
// return checked_multiply(base, int_power(base, exp - 1));
|
return checked_multiply(base, int_power(base, exp - 1));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return checked_square(int_power(base, exp / 2));
|
return checked_square(int_power(base, exp / 2));
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|
||||||
// template<typename T>
|
template<typename T>
|
||||||
// constexpr widen_t<T> compute_base_power(PowerV auto bp)
|
[[nodiscard]] consteval widen_t<T> compute_base_power(MagnitudeSpec auto el)
|
||||||
// {
|
{
|
||||||
// // This utility can only handle integer powers. To compute rational powers at compile time, we'll
|
// This utility can only handle integer powers. To compute rational powers at compile time, we'll
|
||||||
// // need to write a custom function.
|
// need to write a custom function.
|
||||||
// //
|
//
|
||||||
// // Note that since this function should only be called at compile time, the point of these
|
// Note that since this function should only be called at compile time, the point of these
|
||||||
// // exceptions is to act as "static_assert substitutes", not to throw actual exceptions at runtime.
|
// exceptions is to act as "static_assert substitutes", not to throw actual exceptions at runtime.
|
||||||
// if (bp.power.den != 1) {
|
const auto exp = get_exponent(el);
|
||||||
// throw std::invalid_argument{"Rational powers not yet supported"};
|
if (exp.den != 1) {
|
||||||
// }
|
throw std::invalid_argument{"Rational powers not yet supported"};
|
||||||
|
}
|
||||||
|
|
||||||
// if (bp.power.num < 0) {
|
if (exp.num < 0) {
|
||||||
// if constexpr (std::is_integral_v<T>) {
|
if constexpr (std::is_integral_v<T>) {
|
||||||
// throw std::invalid_argument{"Cannot represent reciprocal as integer"};
|
throw std::invalid_argument{"Cannot represent reciprocal as integer"};
|
||||||
// } else {
|
} else {
|
||||||
// return T{1} / compute_base_power<T>(inverse(bp));
|
return T{1} / compute_base_power<T>(inverse(el));
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// auto power = bp.power.num;
|
auto power = exp.num;
|
||||||
// return int_power(static_cast<widen_t<T>>(bp.get_base()), power);
|
return int_power(static_cast<widen_t<T>>(get_base(el)), power);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// A converter for the value member variable of magnitude (below).
|
// A converter for the value member variable of magnitude (below).
|
||||||
//
|
//
|
||||||
@@ -285,7 +304,7 @@ template<PowerVBase auto V, ratio R>
|
|||||||
template<typename To, typename From>
|
template<typename To, typename From>
|
||||||
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
|
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
|
||||||
requires(!std::is_integral_v<To> || std::is_integral_v<From>)
|
requires(!std::is_integral_v<To> || std::is_integral_v<From>)
|
||||||
constexpr To checked_static_cast(From x)
|
[[nodiscard]] consteval To checked_static_cast(From x)
|
||||||
{
|
{
|
||||||
// This function should only ever be called at compile time. The purpose of these exceptions is
|
// 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.
|
// to produce compiler errors, because we cannot `static_assert` on function arguments.
|
||||||
@@ -300,39 +319,10 @@ constexpr To checked_static_cast(From x)
|
|||||||
|
|
||||||
} // namespace detail
|
} // 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<typename T>
|
|
||||||
concept MagnitudeSpec = PowerVBase<T> || PowerV<T>;
|
|
||||||
|
|
||||||
// A variety of implementation detail helpers.
|
// A variety of implementation detail helpers.
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<MagnitudeSpec Element>
|
|
||||||
[[nodiscard]] consteval auto get_base(Element element)
|
|
||||||
{
|
|
||||||
if constexpr (PowerV<Element>)
|
|
||||||
return Element::base;
|
|
||||||
else
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<MagnitudeSpec Element>
|
|
||||||
[[nodiscard]] consteval ratio get_exponent(Element)
|
|
||||||
{
|
|
||||||
if constexpr (PowerV<Element>)
|
|
||||||
return Element::exponent;
|
|
||||||
else
|
|
||||||
return ratio{1};
|
|
||||||
}
|
|
||||||
|
|
||||||
// The exponent of `factor` in the prime factorization of `n`.
|
// The exponent of `factor` in the prime factorization of `n`.
|
||||||
[[nodiscard]] consteval std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
|
[[nodiscard]] consteval std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n)
|
||||||
{
|
{
|
||||||
@@ -493,16 +483,16 @@ inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
|
|||||||
/**
|
/**
|
||||||
* @brief The value of a Magnitude in a desired type T.
|
* @brief The value of a Magnitude in a desired type T.
|
||||||
*/
|
*/
|
||||||
// template<typename T, auto... BPs>
|
template<typename T, auto... Ms>
|
||||||
// // TODO(chogg): Migrate this to use `treat_as_floating_point`.
|
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
|
||||||
// requires(!std::integral<T> || is_integral(magnitude<BPs...>{}))
|
requires(!std::integral<T> || is_integral(magnitude<Ms...>{}))
|
||||||
// constexpr T get_value(const magnitude<BPs...>&)
|
constexpr T get_value(const magnitude<Ms...>&)
|
||||||
// {
|
{
|
||||||
// // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
|
// Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
|
||||||
// constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(BPs) * ... * T{1}));
|
constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(Ms) * ... * T{1}));
|
||||||
|
|
||||||
// return result;
|
return result;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -525,7 +515,7 @@ template<Magnitude M1, Magnitude M2>
|
|||||||
// Magnitude rational powers implementation.
|
// Magnitude rational powers implementation.
|
||||||
|
|
||||||
template<ratio E, auto... Ms>
|
template<ratio E, auto... Ms>
|
||||||
constexpr auto pow(magnitude<Ms...>)
|
[[nodiscard]] consteval auto pow(magnitude<Ms...>)
|
||||||
{
|
{
|
||||||
if constexpr (E.num == 0) {
|
if constexpr (E.num == 0) {
|
||||||
return magnitude<>{};
|
return magnitude<>{};
|
||||||
@@ -535,13 +525,13 @@ constexpr auto pow(magnitude<Ms...>)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<auto... Ms>
|
template<auto... Ms>
|
||||||
constexpr auto sqrt(magnitude<Ms...> m)
|
[[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
|
||||||
{
|
{
|
||||||
return pow<ratio{1, 2}>(m);
|
return pow<ratio{1, 2}>(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<auto... Ms>
|
template<auto... Ms>
|
||||||
constexpr auto cbrt(magnitude<Ms...> m)
|
[[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
|
||||||
{
|
{
|
||||||
return pow<ratio{1, 3}>(m);
|
return pow<ratio{1, 3}>(m);
|
||||||
}
|
}
|
||||||
@@ -571,7 +561,7 @@ constexpr Magnitude auto operator*(Magnitude auto m, magnitude<>) { return m; }
|
|||||||
|
|
||||||
// Recursive case for the product of any two non-identity Magnitudes.
|
// Recursive case for the product of any two non-identity Magnitudes.
|
||||||
template<auto H1, auto... T1, auto H2, auto... T2>
|
template<auto H1, auto... T1, auto H2, auto... T2>
|
||||||
constexpr Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
|
[[nodiscard]] consteval Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
|
||||||
{
|
{
|
||||||
using namespace detail;
|
using namespace detail;
|
||||||
|
|
||||||
@@ -611,7 +601,7 @@ constexpr Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Magnitude quotient implementation.
|
// Magnitude quotient implementation.
|
||||||
|
|
||||||
constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
|
[[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Magnitude numerator and denominator implementation.
|
// Magnitude numerator and denominator implementation.
|
||||||
@@ -619,19 +609,15 @@ constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// The largest integer which can be extracted from any magnitude with only a single basis vector.
|
// The largest integer which can be extracted from any magnitude with only a single basis vector.
|
||||||
template<auto BP>
|
template<auto M>
|
||||||
constexpr auto integer_part(magnitude<BP>)
|
[[nodiscard]] consteval auto integer_part(magnitude<M>)
|
||||||
{
|
{
|
||||||
constexpr auto power_num = BP.power.num;
|
constexpr auto power_num = get_exponent(M).num;
|
||||||
constexpr auto power_den = BP.power.den;
|
constexpr auto power_den = get_exponent(M).den;
|
||||||
|
|
||||||
if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) {
|
if constexpr (std::is_integral_v<decltype(get_base(M))> && (power_num >= power_den)) {
|
||||||
constexpr auto largest_integer_power = [=](PowerV auto bp) {
|
// largest integer power
|
||||||
bp.power = (power_num / power_den); // Note: integer division intended.
|
return magnitude<power_v_or_T<get_base(M), power_num / power_den>()>{}; // Note: integer division intended
|
||||||
return bp;
|
|
||||||
}(BP); // Note: lambda is immediately invoked.
|
|
||||||
|
|
||||||
return magnitude<largest_integer_power>{};
|
|
||||||
} else {
|
} else {
|
||||||
return magnitude<>{};
|
return magnitude<>{};
|
||||||
}
|
}
|
||||||
@@ -639,13 +625,13 @@ constexpr auto integer_part(magnitude<BP>)
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template<auto... BPs>
|
template<auto... Ms>
|
||||||
constexpr auto numerator(magnitude<BPs...>)
|
[[nodiscard]] consteval auto numerator(magnitude<Ms...>)
|
||||||
{
|
{
|
||||||
return (detail::integer_part(magnitude<BPs>{}) * ... * magnitude<>{});
|
return (detail::integer_part(magnitude<Ms>{}) * ... * magnitude<>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
|
[[nodiscard]] consteval auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
|
||||||
|
|
||||||
// Implementation of conversion to ratio goes here, because it needs `numerator()` and `denominator()`.
|
// Implementation of conversion to ratio goes here, because it needs `numerator()` and `denominator()`.
|
||||||
// constexpr ratio as_ratio(Magnitude auto m)
|
// constexpr ratio as_ratio(Magnitude auto m)
|
||||||
@@ -677,44 +663,51 @@ constexpr auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
|
|||||||
// minimum power for each base (where absent bases implicitly have a power of 0).
|
// minimum power for each base (where absent bases implicitly have a power of 0).
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template<auto BP>
|
|
||||||
constexpr auto remove_positive_power(magnitude<BP> m)
|
template<auto M>
|
||||||
|
[[nodiscard]] consteval auto remove_positive_power(magnitude<M> m)
|
||||||
{
|
{
|
||||||
if constexpr (BP.power.num < 0) {
|
if constexpr (get_exponent(M).num < 0) {
|
||||||
return m;
|
return m;
|
||||||
} else {
|
} else {
|
||||||
return magnitude<>{};
|
return magnitude<>{};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<auto... BPs>
|
template<auto... Ms>
|
||||||
constexpr auto remove_positive_powers(magnitude<BPs...>)
|
[[nodiscard]] consteval auto remove_positive_powers(magnitude<Ms...>)
|
||||||
{
|
{
|
||||||
return (magnitude<>{} * ... * remove_positive_power(magnitude<BPs>{}));
|
return (magnitude<>{} * ... * remove_positive_power(magnitude<Ms>{}));
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
// Base cases, for when either (or both) inputs are the identity.
|
// Base cases, for when either (or both) inputs are the identity.
|
||||||
constexpr auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
|
[[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
|
||||||
constexpr auto common_magnitude(magnitude<>, Magnitude auto m) { return detail::remove_positive_powers(m); }
|
[[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto m)
|
||||||
constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail::remove_positive_powers(m); }
|
{
|
||||||
|
return detail::remove_positive_powers(m);
|
||||||
|
}
|
||||||
|
[[nodiscard]] consteval auto common_magnitude(Magnitude auto m, magnitude<>)
|
||||||
|
{
|
||||||
|
return detail::remove_positive_powers(m);
|
||||||
|
}
|
||||||
|
|
||||||
// Recursive case for the common Magnitude of any two non-identity Magnitudes.
|
// Recursive case for the common Magnitude of any two non-identity Magnitudes.
|
||||||
template<auto H1, auto... T1, auto H2, auto... T2>
|
template<auto H1, auto... T1, auto H2, auto... T2>
|
||||||
constexpr auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
|
[[nodiscard]] consteval auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
|
||||||
{
|
{
|
||||||
using detail::remove_positive_power;
|
using detail::remove_positive_power;
|
||||||
|
|
||||||
if constexpr (H1.get_base() < H2.get_base()) {
|
if constexpr (get_base(H1) < get_base(H2)) {
|
||||||
// When H1 has the smaller base, prepend to result from recursion.
|
// When H1 has the smaller base, prepend to result from recursion.
|
||||||
return remove_positive_power(magnitude<H1>{}) * common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{});
|
return remove_positive_power(magnitude<H1>{}) * common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{});
|
||||||
} else if constexpr (H2.get_base() < H1.get_base()) {
|
} else if constexpr (get_base(H2) < get_base(H1)) {
|
||||||
// When H2 has the smaller base, prepend to result from recursion.
|
// When H2 has the smaller base, prepend to result from recursion.
|
||||||
return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{});
|
return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{});
|
||||||
} else {
|
} else {
|
||||||
// When the bases are equal, pick whichever has the lower power.
|
// When the bases are equal, pick whichever has the lower power.
|
||||||
constexpr auto common_tail = common_magnitude(magnitude<T1...>{}, magnitude<T2...>{});
|
constexpr auto common_tail = common_magnitude(magnitude<T1...>{}, magnitude<T2...>{});
|
||||||
if constexpr ((H1.power) < (H2.power)) {
|
if constexpr (get_exponent(H1) < get_exponent(H2)) {
|
||||||
return magnitude<H1>{} * common_tail;
|
return magnitude<H1>{} * common_tail;
|
||||||
} else {
|
} else {
|
||||||
return magnitude<H2>{} * common_tail;
|
return magnitude<H2>{} * common_tail;
|
||||||
@@ -740,7 +733,7 @@ namespace detail {
|
|||||||
template<std::intmax_t N>
|
template<std::intmax_t N>
|
||||||
requires(N > 0)
|
requires(N > 0)
|
||||||
struct prime_factorization {
|
struct prime_factorization {
|
||||||
static constexpr std::intmax_t get_or_compute_first_factor()
|
[[nodiscard]] static consteval std::intmax_t get_or_compute_first_factor()
|
||||||
{
|
{
|
||||||
if constexpr (known_first_factor<N>.has_value()) {
|
if constexpr (known_first_factor<N>.has_value()) {
|
||||||
return known_first_factor<N>.value();
|
return known_first_factor<N>.value();
|
||||||
@@ -786,15 +779,16 @@ template<ratio Base, ratio Pow>
|
|||||||
inline constexpr Magnitude auto mag_power = pow<Pow>(mag<Base>);
|
inline constexpr Magnitude auto mag_power = pow<Pow>(mag<Base>);
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template<typename T, PowerV auto... BPs>
|
|
||||||
constexpr ratio get_power(T base, magnitude<BPs...>)
|
template<typename T, auto... Ms>
|
||||||
|
[[nodiscard]] consteval ratio get_power(T base, magnitude<Ms...>)
|
||||||
{
|
{
|
||||||
return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0});
|
return ((get_base(Ms) == base ? get_exponent(Ms) : ratio{0}) + ... + ratio{0});
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::intmax_t integer_part(ratio r) { return r.num / r.den; }
|
[[nodiscard]] consteval std::intmax_t integer_part(ratio r) { return r.num / r.den; }
|
||||||
|
|
||||||
constexpr std::intmax_t extract_power_of_10(Magnitude auto m)
|
[[nodiscard]] consteval std::intmax_t extract_power_of_10(Magnitude auto m)
|
||||||
{
|
{
|
||||||
const auto power_of_2 = get_power(2, m);
|
const auto power_of_2 = get_power(2, m);
|
||||||
const auto power_of_5 = get_power(5, m);
|
const auto power_of_5 = get_power(5, m);
|
||||||
@@ -805,6 +799,6 @@ constexpr std::intmax_t extract_power_of_10(Magnitude auto m)
|
|||||||
|
|
||||||
return integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5);
|
return integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
} // namespace units
|
} // namespace units
|
||||||
|
@@ -22,12 +22,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <units/bits/algorithm.h>
|
||||||
#include <units/bits/expression_template.h>
|
#include <units/bits/expression_template.h>
|
||||||
#include <units/bits/external/fixed_string.h>
|
#include <units/bits/external/fixed_string.h>
|
||||||
|
#include <units/bits/external/text_tools.h>
|
||||||
#include <units/bits/external/type_name.h>
|
#include <units/bits/external/type_name.h>
|
||||||
#include <units/bits/external/type_traits.h>
|
#include <units/bits/external/type_traits.h>
|
||||||
#include <units/magnitude.h>
|
#include <units/magnitude.h>
|
||||||
#include <units/symbol_text.h>
|
#include <units/symbol_text.h>
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// #include <units/bits/derived_symbol_text.h>
|
// #include <units/bits/derived_symbol_text.h>
|
||||||
|
|
||||||
@@ -209,19 +213,16 @@ template<typename T>
|
|||||||
inline constexpr bool is_power_of_unit =
|
inline constexpr bool is_power_of_unit =
|
||||||
requires { requires is_specialization_of_power<T> && Unit<typename T::factor>; };
|
requires { requires is_specialization_of_power<T> && Unit<typename T::factor>; };
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept UnitLike = Unit<T> || is_power_of_unit<T>;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline constexpr bool is_per_of_units = false;
|
inline constexpr bool is_per_of_units = false;
|
||||||
|
|
||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
inline constexpr bool is_per_of_units<per<Ts...>> = (... && UnitLike<Ts>);
|
inline constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_power_of_unit<Ts>));
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
concept DerivedUnitSpec = detail::UnitLike<T> || detail::is_per_of_units<T>;
|
concept DerivedUnitSpec = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Measurement unit for a derived quantity
|
* @brief Measurement unit for a derived quantity
|
||||||
@@ -312,63 +313,75 @@ inline constexpr bool is_unit<T> = true;
|
|||||||
* @tparam U a unit to use as a `reference_unit`
|
* @tparam U a unit to use as a `reference_unit`
|
||||||
* @tparam M a Magnitude representing an absolute scaling factor of this unit
|
* @tparam M a Magnitude representing an absolute scaling factor of this unit
|
||||||
*/
|
*/
|
||||||
template<Magnitude M, UnitLike U>
|
template<Magnitude M, Unit U>
|
||||||
struct canonical_unit {
|
struct canonical_unit {
|
||||||
M mag;
|
M mag;
|
||||||
U reference_unit;
|
U reference_unit;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto get_canonical_unit(UnitLike auto u);
|
template<Unit T, basic_symbol_text Symbol>
|
||||||
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&);
|
||||||
|
|
||||||
template<UnitLike T, auto M, typename U>
|
template<Unit T, basic_symbol_text Symbol, Unit auto U>
|
||||||
[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile scaled_unit<M, U>&)
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&);
|
||||||
|
|
||||||
|
template<typename T, typename F, int Num, int... Den>
|
||||||
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&);
|
||||||
|
|
||||||
|
template<Unit T, typename... Us>
|
||||||
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Us...>&);
|
||||||
|
|
||||||
|
template<Unit T, auto M, typename U>
|
||||||
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit<M, U>&)
|
||||||
{
|
{
|
||||||
auto base = get_canonical_unit(U{});
|
auto base = get_canonical_unit_impl(U{}, U{});
|
||||||
return canonical_unit{M * base.mag, base.reference_unit};
|
return canonical_unit{M * base.mag, base.reference_unit};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<UnitLike T, basic_symbol_text Symbol>
|
template<Unit T, basic_symbol_text Symbol>
|
||||||
[[nodiscard]] constexpr auto get_canonical_unit_impl(T t, const volatile named_unit<Symbol>&)
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&)
|
||||||
{
|
{
|
||||||
return canonical_unit{mag<1>, t};
|
return canonical_unit{mag<1>, t};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<UnitLike T, basic_symbol_text Symbol, Unit auto U>
|
template<Unit T, basic_symbol_text Symbol, Unit auto U>
|
||||||
[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile named_unit<Symbol, U>&)
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U>&)
|
||||||
{
|
{
|
||||||
return get_canonical_unit(U);
|
return get_canonical_unit_impl(U, U);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<UnitLike T, typename F, int Num, int... Den>
|
template<typename T, typename F, int Num, int... Den>
|
||||||
[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile power<F, Num, Den...>&)
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&)
|
||||||
{
|
{
|
||||||
auto base = get_canonical_unit(F{});
|
auto base = get_canonical_unit_impl(F{}, F{});
|
||||||
return canonical_unit{
|
return canonical_unit{
|
||||||
pow<power<F, Num, Den...>::exponent>(base.mag),
|
pow<power<F, Num, Den...>::exponent>(base.mag),
|
||||||
derived_unit<power_or_T<std::remove_const_t<decltype(base.reference_unit)>, power<F, Num, Den...>::exponent>>{}};
|
derived_unit<power_or_T<std::remove_const_t<decltype(base.reference_unit)>, power<F, Num, Den...>::exponent>>{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<UnitLike T, DerivedUnitSpec... Us>
|
template<Unit T, typename... Us>
|
||||||
[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile derived_unit<Us...>&)
|
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Us...>&)
|
||||||
{
|
{
|
||||||
if constexpr (type_list_size<typename derived_unit<Us...>::_den_> != 0) {
|
if constexpr (type_list_size<typename derived_unit<Us...>::_den_> != 0) {
|
||||||
auto num = get_canonical_unit(type_list_map<typename derived_unit<Us...>::_num_, derived_unit>{});
|
using num_type = type_list_map<typename derived_unit<Us...>::_num_, derived_unit>;
|
||||||
auto den = get_canonical_unit(type_list_map<typename derived_unit<Us...>::_den_, derived_unit>{});
|
using den_type = type_list_map<typename derived_unit<Us...>::_den_, derived_unit>;
|
||||||
|
auto num = get_canonical_unit_impl(num_type{}, num_type{});
|
||||||
|
auto den = get_canonical_unit_impl(den_type{}, den_type{});
|
||||||
return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit};
|
return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit};
|
||||||
} else {
|
} else {
|
||||||
auto num = (one * ... * get_canonical_unit(Us{}).reference_unit);
|
auto num = (one * ... * get_canonical_unit_impl(Us{}, Us{}).reference_unit);
|
||||||
auto mag = (units::mag<1> * ... * get_canonical_unit(Us{}).mag);
|
auto mag = (units::mag<1> * ... * get_canonical_unit_impl(Us{}, Us{}).mag);
|
||||||
return canonical_unit{mag, num};
|
return canonical_unit{mag, num};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto get_canonical_unit(UnitLike auto u) { return get_canonical_unit_impl(u, u); }
|
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return get_canonical_unit_impl(u, u); }
|
||||||
|
|
||||||
// TODO What if the same unit will have different types (i.e. user will inherit its own type from `metre`)?
|
// TODO What if the same unit will have different types (i.e. user will inherit its own type from `metre`)?
|
||||||
// Is there a better way to sort units here? Some of them may not have symbol at all (like all units of
|
// Is there a better way to sort units here? Some of them may not have symbol at all (like all units of
|
||||||
// dimensionless quantities).
|
// dimensionless quantities).
|
||||||
template<Unit U1, Unit U2>
|
template<Unit Lhs, Unit Rhs>
|
||||||
struct unit_less : std::bool_constant<type_name<U1>() < type_name<U2>()> {};
|
struct unit_less : std::bool_constant<type_name<Lhs>() < type_name<Rhs>()> {};
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
|
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
|
||||||
@@ -398,17 +411,17 @@ template<Magnitude M, Unit U>
|
|||||||
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
|
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
|
||||||
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
|
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
|
||||||
*/
|
*/
|
||||||
template<Unit U1, Unit U2>
|
template<Unit Lhs, Unit Rhs>
|
||||||
[[nodiscard]] consteval Unit auto operator*(U1 u1, U2 u2)
|
[[nodiscard]] consteval Unit auto operator*(Lhs lhs, Rhs rhs)
|
||||||
{
|
{
|
||||||
if constexpr (detail::is_specialization_of_scaled_unit<U1> && detail::is_specialization_of_scaled_unit<U2>)
|
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
|
||||||
return (U1::mag * U2::mag) * (U1::reference_unit * U2::reference_unit);
|
return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit);
|
||||||
else if constexpr (detail::is_specialization_of_scaled_unit<U1>)
|
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
|
||||||
return U1::mag * (U1::reference_unit * u2);
|
return Lhs::mag * (Lhs::reference_unit * rhs);
|
||||||
else if constexpr (detail::is_specialization_of_scaled_unit<U2>)
|
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
|
||||||
return U2::mag * (u1 * U2::reference_unit);
|
return Rhs::mag * (lhs * Rhs::reference_unit);
|
||||||
else
|
else
|
||||||
return detail::expr_multiply<U1, U2, struct one, detail::type_list_of_unit_less, derived_unit>();
|
return detail::expr_multiply<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -416,31 +429,31 @@ template<Unit U1, Unit U2>
|
|||||||
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
|
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
|
||||||
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
|
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
|
||||||
*/
|
*/
|
||||||
template<Unit U1, Unit U2>
|
template<Unit Lhs, Unit Rhs>
|
||||||
[[nodiscard]] consteval Unit auto operator/(U1 u1, U2 u2)
|
[[nodiscard]] consteval Unit auto operator/(Lhs lhs, Rhs rhs)
|
||||||
{
|
{
|
||||||
if constexpr (detail::is_specialization_of_scaled_unit<U1> && detail::is_specialization_of_scaled_unit<U2>)
|
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
|
||||||
return (U1::mag / U2::mag) * (U1::reference_unit / U2::reference_unit);
|
return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit);
|
||||||
else if constexpr (detail::is_specialization_of_scaled_unit<U1>)
|
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
|
||||||
return U1::mag * (U1::reference_unit / u2);
|
return Lhs::mag * (Lhs::reference_unit / rhs);
|
||||||
else if constexpr (detail::is_specialization_of_scaled_unit<U2>)
|
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
|
||||||
return U2::mag * (u1 / U2::reference_unit);
|
return Rhs::mag * (lhs / Rhs::reference_unit);
|
||||||
else
|
else
|
||||||
return detail::expr_divide<U1, U2, struct one, detail::type_list_of_unit_less, derived_unit>();
|
return detail::expr_divide<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Unit U>
|
template<Unit U>
|
||||||
[[nodiscard]] consteval Unit auto operator/(int value, U)
|
[[nodiscard]] consteval Unit auto operator/(int value, U u)
|
||||||
{
|
{
|
||||||
gsl_Assert(value == 1);
|
gsl_Assert(value == 1);
|
||||||
return detail::expr_invert<U, struct one, derived_unit>();
|
return detail::expr_invert<derived_unit, struct one>(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Unit U>
|
template<Unit U>
|
||||||
[[nodiscard]] consteval Unit auto operator/(U, int) = delete;
|
[[nodiscard]] consteval Unit auto operator/(U, int) = delete;
|
||||||
|
|
||||||
template<Unit U1, Unit U2>
|
template<Unit Lhs, Unit Rhs>
|
||||||
[[nodiscard]] consteval bool operator==(U1 lhs, U2 rhs)
|
[[nodiscard]] consteval bool operator==(Lhs lhs, Rhs rhs)
|
||||||
{
|
{
|
||||||
auto canonical_lhs = detail::get_canonical_unit(lhs);
|
auto canonical_lhs = detail::get_canonical_unit(lhs);
|
||||||
auto canonical_rhs = detail::get_canonical_unit(rhs);
|
auto canonical_rhs = detail::get_canonical_unit(rhs);
|
||||||
@@ -450,8 +463,8 @@ template<Unit U1, Unit U2>
|
|||||||
|
|
||||||
|
|
||||||
// Convertible
|
// Convertible
|
||||||
template<Unit U1, Unit U2>
|
template<Unit Lhs, Unit Rhs>
|
||||||
[[nodiscard]] consteval bool convertible(U1 lhs, U2 rhs)
|
[[nodiscard]] consteval bool convertible(Lhs lhs, Rhs rhs)
|
||||||
{
|
{
|
||||||
auto canonical_lhs = detail::get_canonical_unit(lhs);
|
auto canonical_lhs = detail::get_canonical_unit(lhs);
|
||||||
auto canonical_rhs = detail::get_canonical_unit(rhs);
|
auto canonical_rhs = detail::get_canonical_unit(rhs);
|
||||||
@@ -465,9 +478,232 @@ inline constexpr decltype(U * U) square;
|
|||||||
template<Unit auto U>
|
template<Unit auto U>
|
||||||
inline constexpr decltype(U * U * U) cubic;
|
inline constexpr decltype(U * U * U) cubic;
|
||||||
|
|
||||||
|
|
||||||
|
// get_unit_symbol
|
||||||
|
|
||||||
|
enum class text_encoding {
|
||||||
|
unicode, // m³; µs
|
||||||
|
ascii, // m^3; us
|
||||||
|
default_encoding = unicode
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class unit_symbol_denominator {
|
||||||
|
solidus_one, // m/s; kg m-1 s-1
|
||||||
|
always_solidus, // m/s; kg/(m s)
|
||||||
|
always_negative, // m s-1; kg m-1 s-1
|
||||||
|
default_denominator = solidus_one
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class unit_symbol_separator {
|
||||||
|
space, // kg m²/s²
|
||||||
|
dot, // kg⋅m²/s² (valid only for unicode encoding)
|
||||||
|
default_separator = space
|
||||||
|
};
|
||||||
|
|
||||||
|
struct unit_symbol_formatting {
|
||||||
|
text_encoding encoding = text_encoding::default_encoding;
|
||||||
|
unit_symbol_denominator denominator = unit_symbol_denominator::default_denominator;
|
||||||
|
unit_symbol_separator separator = unit_symbol_separator::default_separator;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// TODO Should `basic_symbol_text` be fixed to use `char` type for both encodings?
|
||||||
|
template<typename CharT, typename UnicodeCharT, std::size_t N, std::size_t M, std::output_iterator<CharT> Out>
|
||||||
|
constexpr Out copy(const basic_symbol_text<UnicodeCharT, N, M>& txt, text_encoding encoding, Out out)
|
||||||
|
{
|
||||||
|
if (encoding == text_encoding::unicode) {
|
||||||
|
if (is_same_v<CharT, UnicodeCharT>)
|
||||||
|
return copy(txt.unicode(), out).out;
|
||||||
|
else
|
||||||
|
static_assert("Unicode text can't be copied to CharT output");
|
||||||
|
} else {
|
||||||
|
if (is_same_v<CharT, char>)
|
||||||
|
return copy(txt.ascii(), out).out;
|
||||||
|
else
|
||||||
|
static_assert("ASCII text can't be copied to CharT output");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10");
|
||||||
|
|
||||||
|
template<Magnitude auto M>
|
||||||
|
constexpr auto magnitude_text()
|
||||||
|
{
|
||||||
|
constexpr auto exp10 = extract_power_of_10(M);
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
constexpr auto num_value = get_value<std::intmax_t>(num);
|
||||||
|
constexpr auto den_value = get_value<std::intmax_t>(den);
|
||||||
|
|
||||||
|
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
|
||||||
|
return base_multiplier + superscript<exp10>();
|
||||||
|
} else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) {
|
||||||
|
auto txt = basic_fixed_string("[") + regular<num_value>();
|
||||||
|
if constexpr (den_value == 1) {
|
||||||
|
if constexpr (exp10 == 0) {
|
||||||
|
return txt + basic_fixed_string("]");
|
||||||
|
} else {
|
||||||
|
return txt + " " + base_multiplier + superscript<exp10>() + basic_fixed_string("]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (exp10 == 0) {
|
||||||
|
return txt + basic_fixed_string("/") + regular<den_value>() + basic_fixed_string("]");
|
||||||
|
} else {
|
||||||
|
return txt + basic_fixed_string("/") + regular<den_value>() + " " + base_multiplier + superscript<exp10>() +
|
||||||
|
basic_fixed_string("]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return basic_fixed_string("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out>
|
||||||
|
constexpr Out print_separator(Out out, unit_symbol_formatting fmt)
|
||||||
|
{
|
||||||
|
if (fmt.separator == unit_symbol_separator::dot) {
|
||||||
|
if (fmt.encoding != text_encoding::unicode)
|
||||||
|
throw std::invalid_argument("'unit_symbol_separator::dot' can be only used with 'text_encoding::unicode'");
|
||||||
|
copy(std::string_view("⋅"), out);
|
||||||
|
} else {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out, Unit U>
|
||||||
|
requires requires { U::symbol; }
|
||||||
|
constexpr Out unit_symbol_impl(Out out, U, unit_symbol_formatting fmt, bool negative_power)
|
||||||
|
{
|
||||||
|
out = copy<CharT>(U::symbol, fmt.encoding, out);
|
||||||
|
if (negative_power) {
|
||||||
|
constexpr auto txt = superscript<-1>();
|
||||||
|
out = copy<CharT>(txt, fmt.encoding, out);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out, auto M, typename U>
|
||||||
|
constexpr Out unit_symbol_impl(Out out, const scaled_unit<M, U>& u, unit_symbol_formatting fmt, bool negative_power)
|
||||||
|
{
|
||||||
|
if constexpr (M == mag<1>) {
|
||||||
|
// no ratio/prefix
|
||||||
|
return unit_symbol_impl<CharT>(out, u.reference_unit, fmt, negative_power);
|
||||||
|
} else {
|
||||||
|
constexpr auto mag_txt = magnitude_text<M>();
|
||||||
|
out = copy<CharT>(mag_txt, fmt.encoding, out);
|
||||||
|
|
||||||
|
if constexpr (std::derived_from<std::remove_const_t<decltype(u.reference_unit)>, derived_unit<>>)
|
||||||
|
return out;
|
||||||
|
else {
|
||||||
|
*out++ = ' ';
|
||||||
|
return unit_symbol_impl<CharT>(out, u.reference_unit, fmt, negative_power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out, typename F, int Num, int... Den>
|
||||||
|
constexpr auto unit_symbol_impl(Out out, const power<F, Num, Den...>&, unit_symbol_formatting fmt, bool negative_power)
|
||||||
|
{
|
||||||
|
out = unit_symbol_impl<CharT>(out, F{}, fmt, false); // negative power component will be added below if needed
|
||||||
|
|
||||||
|
constexpr ratio r = power<F, Num, Den...>::exponent;
|
||||||
|
if constexpr (r.den != 1) {
|
||||||
|
// add root part
|
||||||
|
constexpr auto txt = txt + basic_fixed_string("^(") + regular<r.num>() + basic_fixed_string("/") +
|
||||||
|
regular<r.den>() + basic_fixed_string(")");
|
||||||
|
return copy<CharT>(txt, fmt.encoding, out);
|
||||||
|
} else if constexpr (r.num != 1) {
|
||||||
|
// add exponent part
|
||||||
|
if (negative_power) {
|
||||||
|
constexpr auto txt = superscript<-r.num>();
|
||||||
|
return copy<CharT>(txt, fmt.encoding, out);
|
||||||
|
} else {
|
||||||
|
constexpr auto txt = superscript<r.num>();
|
||||||
|
return copy<CharT>(txt, fmt.encoding, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitSpec M>
|
||||||
|
constexpr Out unit_symbol_impl(Out out, M m, std::size_t Idx, unit_symbol_formatting fmt, bool negative_power)
|
||||||
|
{
|
||||||
|
if (Idx > 0) out = print_separator<CharT>(out, fmt);
|
||||||
|
return unit_symbol_impl<CharT>(out, m, fmt, negative_power);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitSpec... Ms, std::size_t... Idxs>
|
||||||
|
constexpr Out unit_symbol_impl(Out out, const type_list<Ms...>&, std::index_sequence<Idxs...>,
|
||||||
|
unit_symbol_formatting fmt, bool negative_power)
|
||||||
|
{
|
||||||
|
return (..., (out = unit_symbol_impl<CharT>(out, Ms{}, Idxs, fmt, negative_power)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitSpec... Nums, DerivedUnitSpec... Dens>
|
||||||
|
constexpr Out unit_symbol_impl(Out out, const type_list<Nums...>& nums, const type_list<Dens...>& dens,
|
||||||
|
unit_symbol_formatting fmt)
|
||||||
|
{
|
||||||
|
if constexpr (sizeof...(Nums) == 0 && sizeof...(Dens) == 0) {
|
||||||
|
// dimensionless quantity
|
||||||
|
return out;
|
||||||
|
} else if constexpr (sizeof...(Dens) == 0) {
|
||||||
|
// no denominator
|
||||||
|
return unit_symbol_impl<CharT>(out, nums, std::index_sequence_for<Nums...>(), fmt, false);
|
||||||
|
} else {
|
||||||
|
using enum unit_symbol_denominator;
|
||||||
|
if constexpr (sizeof...(Nums) > 0) {
|
||||||
|
unit_symbol_impl<CharT>(out, nums, std::index_sequence_for<Nums...>(), fmt, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.denominator == always_solidus || (fmt.denominator == solidus_one && sizeof...(Dens) == 1)) {
|
||||||
|
if constexpr (sizeof...(Nums) == 0) *out++ = '1';
|
||||||
|
*out++ = '/';
|
||||||
|
} else {
|
||||||
|
out = print_separator<CharT>(out, fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.denominator == always_solidus && sizeof...(Dens) > 1) *out++ = '(';
|
||||||
|
bool negative_power = fmt.denominator == always_negative || (fmt.denominator == solidus_one && sizeof...(Dens) > 1);
|
||||||
|
out = unit_symbol_impl<CharT>(out, dens, std::index_sequence_for<Dens...>(), fmt, negative_power);
|
||||||
|
if (fmt.denominator == always_solidus && sizeof...(Dens) > 1) *out++ = ')';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, std::output_iterator<CharT> Out, typename... Us>
|
||||||
|
constexpr Out unit_symbol_impl(Out out, const derived_unit<Us...>&, unit_symbol_formatting fmt, bool negative_power)
|
||||||
|
{
|
||||||
|
gsl_Assert(negative_power == false);
|
||||||
|
return unit_symbol_impl<CharT>(out, typename derived_unit<Us...>::_num_{}, typename derived_unit<Us...>::_den_{},
|
||||||
|
fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
template<typename CharT = char, std::output_iterator<CharT> Out, Unit U>
|
||||||
|
constexpr Out unit_symbol_to(Out out, U u, unit_symbol_formatting fmt = unit_symbol_formatting{})
|
||||||
|
{
|
||||||
|
return detail::unit_symbol_impl<CharT>(out, u, fmt, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT = char, Unit U>
|
||||||
|
[[nodiscard]] constexpr std::basic_string<CharT> unit_symbol(U u, unit_symbol_formatting fmt = unit_symbol_formatting{})
|
||||||
|
{
|
||||||
|
std::basic_string<CharT> buffer;
|
||||||
|
unit_symbol_to<CharT>(std::back_inserter(buffer), u, fmt);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace units
|
} // namespace units
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
|
||||||
// TODO implement this
|
// TODO implement this
|
||||||
template<units::Unit U1, units::Unit U2>
|
template<units::Unit U1, units::Unit U2>
|
||||||
requires(units::convertible(U1{}, U2{}))
|
requires(units::convertible(U1{}, U2{}))
|
||||||
|
@@ -235,6 +235,9 @@ static_assert(is_of_type<square<metre>, derived_unit<power<metre_, 2>>>);
|
|||||||
static_assert(is_of_type<cubic<metre>, derived_unit<power<metre_, 3>>>);
|
static_assert(is_of_type<cubic<metre>, derived_unit<power<metre_, 3>>>);
|
||||||
static_assert(is_of_type<square<metre> * metre, derived_unit<power<metre_, 3>>>);
|
static_assert(is_of_type<square<metre> * metre, derived_unit<power<metre_, 3>>>);
|
||||||
static_assert(is_of_type<metre * square<metre>, derived_unit<power<metre_, 3>>>);
|
static_assert(is_of_type<metre * square<metre>, derived_unit<power<metre_, 3>>>);
|
||||||
|
static_assert(is_of_type<square<metre> / metre, metre_>);
|
||||||
|
static_assert(is_of_type<cubic<metre> / metre, derived_unit<power<metre_, 2>>>);
|
||||||
|
static_assert(is_of_type<cubic<metre> / square<metre>, metre_>);
|
||||||
|
|
||||||
static_assert(is_of_type<metre / second, derived_unit<metre_, per<second_>>>);
|
static_assert(is_of_type<metre / second, derived_unit<metre_, per<second_>>>);
|
||||||
static_assert(is_of_type<metre / square<second>, derived_unit<metre_, per<power<second_, 2>>>>);
|
static_assert(is_of_type<metre / square<second>, derived_unit<metre_, per<power<second_, 2>>>>);
|
||||||
@@ -350,8 +353,72 @@ static_assert(joule == newton * metre);
|
|||||||
static_assert(watt == joule / second);
|
static_assert(watt == joule / second);
|
||||||
static_assert(watt == kilogram * square<metre> / cubic<second>);
|
static_assert(watt == kilogram * square<metre> / cubic<second>);
|
||||||
|
|
||||||
// static_assert(centimetre::symbol == "cm");
|
// unit symbols
|
||||||
// static_assert(kilometre::symbol == "km");
|
#ifdef __cpp_lib_constexpr_string
|
||||||
// static_assert(kilometre_per_hour::symbol == "km/h");
|
|
||||||
|
using enum text_encoding;
|
||||||
|
using enum unit_symbol_denominator;
|
||||||
|
using enum unit_symbol_separator;
|
||||||
|
|
||||||
|
// named units
|
||||||
|
static_assert(unit_symbol(metre) == "m");
|
||||||
|
static_assert(unit_symbol(second) == "s");
|
||||||
|
static_assert(unit_symbol(joule) == "J");
|
||||||
|
static_assert(unit_symbol(degree_Celsius) == "\u00B0C");
|
||||||
|
static_assert(unit_symbol(degree_Celsius, {.encoding = ascii}) == "`C");
|
||||||
|
static_assert(unit_symbol(kilometre) == "km");
|
||||||
|
static_assert(unit_symbol(si::milli<metre>) == "mm");
|
||||||
|
static_assert(unit_symbol(si::micro<metre>) == "µm");
|
||||||
|
static_assert(unit_symbol(si::micro<metre>, {.encoding = ascii}) == "um");
|
||||||
|
static_assert(unit_symbol(kilojoule) == "kJ");
|
||||||
|
static_assert(unit_symbol(hour) == "h");
|
||||||
|
|
||||||
|
// scaled units
|
||||||
|
static_assert(unit_symbol(mag<100> * metre) == "× 10² m");
|
||||||
|
static_assert(unit_symbol(mag<100> * metre, {.encoding = ascii}) == "x 10^2 m");
|
||||||
|
static_assert(unit_symbol(mag<60> * second) == "[6 × 10¹] s");
|
||||||
|
static_assert(unit_symbol(mag<60> * second, {.encoding = ascii}) == "[6 x 10^1] s");
|
||||||
|
|
||||||
|
static_assert(unit_symbol(one) == "");
|
||||||
|
static_assert(unit_symbol(square<metre>) == "m²");
|
||||||
|
static_assert(unit_symbol(square<metre>, {.encoding = ascii}) == "m^2");
|
||||||
|
static_assert(unit_symbol(cubic<metre>) == "m³");
|
||||||
|
static_assert(unit_symbol(cubic<metre>, {.encoding = ascii}) == "m^3");
|
||||||
|
static_assert(unit_symbol(metre / second) == "m/s");
|
||||||
|
static_assert(unit_symbol(metre / second, {.denominator = always_solidus}) == "m/s");
|
||||||
|
static_assert(unit_symbol(metre / second, {.denominator = always_negative}) == "m s⁻¹");
|
||||||
|
static_assert(unit_symbol(metre / second, {.encoding = ascii, .denominator = always_negative}) == "m s^-1");
|
||||||
|
static_assert(unit_symbol(metre / second, {.denominator = always_negative, .separator = dot}) == "m⋅s⁻¹");
|
||||||
|
static_assert(unit_symbol(metre / square<second>) == "m/s²");
|
||||||
|
static_assert(unit_symbol(metre / square<second>, {.encoding = ascii}) == "m/s^2");
|
||||||
|
static_assert(unit_symbol(metre / square<second>, {.denominator = always_solidus}) == "m/s²");
|
||||||
|
static_assert(unit_symbol(metre / square<second>, {.encoding = ascii, .denominator = always_solidus}) == "m/s^2");
|
||||||
|
static_assert(unit_symbol(metre / square<second>, {.denominator = always_negative}) == "m s⁻²");
|
||||||
|
static_assert(unit_symbol(metre / square<second>, {.encoding = ascii, .denominator = always_negative}) == "m s^-2");
|
||||||
|
static_assert(unit_symbol(metre / square<second>, {.denominator = always_negative, .separator = dot}) == "m⋅s⁻²");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>) == "kg m/s²");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>, {.separator = dot}) == "kg⋅m/s²");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>, {.encoding = ascii}) == "kg m/s^2");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>, {.denominator = always_solidus}) == "kg m/s²");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>, {.encoding = ascii, .denominator = always_solidus}) ==
|
||||||
|
"kg m/s^2");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>, {.denominator = always_negative}) == "kg m s⁻²");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>, {.encoding = ascii, .denominator = always_negative}) ==
|
||||||
|
"kg m s^-2");
|
||||||
|
static_assert(unit_symbol(kilogram * metre / square<second>, {.denominator = always_negative, .separator = dot}) ==
|
||||||
|
"kg⋅m⋅s⁻²");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>) == "kg m⁻¹ s⁻²");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>, {.separator = dot}) == "kg⋅m⁻¹⋅s⁻²");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>, {.encoding = ascii}) == "kg m^-1 s^-2");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>, {.denominator = always_solidus}) == "kg/(m s²)");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>, {.encoding = ascii, .denominator = always_solidus}) ==
|
||||||
|
"kg/(m s^2)");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>, {.denominator = always_negative}) == "kg m⁻¹ s⁻²");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>, {.encoding = ascii, .denominator = always_negative}) ==
|
||||||
|
"kg m^-1 s^-2");
|
||||||
|
static_assert(unit_symbol(kilogram / metre / square<second>, {.denominator = always_negative, .separator = dot}) ==
|
||||||
|
"kg⋅m⁻¹⋅s⁻²");
|
||||||
|
|
||||||
|
#endif // __cpp_lib_constexpr_string
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user