Downcasting reworked to allow OFF and AUTO modes

This commit is contained in:
Mateusz Pusz
2020-05-08 10:50:34 +02:00
parent 12c0177633
commit 5c98924efb
24 changed files with 386 additions and 216 deletions

View File

@@ -47,7 +47,7 @@ enable_testing()
add_subdirectory(test) add_subdirectory(test)
# add usage example # add usage example
add_subdirectory(example) # add_subdirectory(example)
# generate project documentation # generate project documentation
add_subdirectory(docs) add_subdirectory(docs)

View File

@@ -49,6 +49,12 @@ class UnitsConan(ConanFile):
"fmt/7.0.3", "fmt/7.0.3",
"ms-gsl/3.1.0" "ms-gsl/3.1.0"
) )
options = {
"downcast": ["off", "on", "auto"]
}
default_options = {
"downcast": "auto"
}
# scm = { # scm = {
# "type": "git", # "type": "git",
# "url": "auto", # "url": "auto",
@@ -63,6 +69,13 @@ class UnitsConan(ConanFile):
def _configure_cmake(self, folder="src"): def _configure_cmake(self, folder="src"):
cmake = CMake(self) cmake = CMake(self)
if self.options.downcast_dispatch_mode == "off":
cmake.definitions["UNITS_DOWNCAST"] = 0
elif self.options.downcast_dispatch_mode == "on":
cmake.definitions["UNITS_DOWNCAST"] = 1
elif self.options.downcast_dispatch_mode == "auto":
cmake.definitions["UNITS_DOWNCAST"] = 2
if self._run_tests: if self._run_tests:
# developer's mode (unit tests, examples, documentation, restrictive compilation warnings, ...) # developer's mode (unit tests, examples, documentation, restrictive compilation warnings, ...)
cmake.configure() cmake.configure()

View File

@@ -23,12 +23,12 @@ a few additional member types and functions::
}; };
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires detail::basic_arithmetic<Rep1, Rep2> && equivalent_dim<D1, dim_invert<D2>> requires detail::basic_arithmetic<Rep1, Rep2> && equivalent<D1, dim_invert<D2>>
[[nodiscard]] constexpr Scalar auto operator*(const quantity<D1, U1, Rep1>& lhs, [[nodiscard]] constexpr Scalar auto operator*(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs); const quantity<D2, U2, Rep2>& rhs);
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires detail::basic_arithmetic<Rep1, Rep2> && (!equivalent_dim<D1, dim_invert<D2>>) requires detail::basic_arithmetic<Rep1, Rep2> && (!equivalent<D1, dim_invert<D2>>)
[[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs, [[nodiscard]] constexpr Quantity auto operator*(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs); const quantity<D2, U2, Rep2>& rhs);
@@ -38,17 +38,17 @@ a few additional member types and functions::
const quantity<D, U, Rep>& q); const quantity<D, U, Rep>& q);
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires detail::basic_arithmetic<Rep1, Rep2> && equivalent_dim<D1, D2> requires detail::basic_arithmetic<Rep1, Rep2> && equivalent<D1, D2>
[[nodiscard]] constexpr Scalar auto operator/(const quantity<D1, U1, Rep1>& lhs, [[nodiscard]] constexpr Scalar auto operator/(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs); const quantity<D2, U2, Rep2>& rhs);
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
requires detail::basic_arithmetic<Rep1, Rep2> && (!equivalent_dim<D1, D2>) requires detail::basic_arithmetic<Rep1, Rep2> && (!equivalent<D1, D2>)
[[nodiscard]] constexpr Quantity AUTO operator/(const quantity<D1, U1, Rep1>& lhs, [[nodiscard]] constexpr Quantity AUTO operator/(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs); const quantity<D2, U2, Rep2>& rhs);
Additional functions provide the support for operations that result in a Additional functions provide the support for operations that result in a
different dimension type than those of their arguments. ``equivalent_dim`` different dimension type than those of their arguments. ``equivalent``
constraint requires two dimensions to be either the same or have convertible constraint requires two dimensions to be either the same or have convertible
units of base dimension (with the same reference unit). units of base dimension (with the same reference unit).

View File

@@ -28,7 +28,7 @@
namespace { namespace {
template<units::Quantity Target, units::Quantity Source> template<units::Quantity Target, units::Quantity Source>
requires units::equivalent_dim<typename Source::dimension, typename Target::dimension> requires units::equivalent<typename Source::dimension, typename Target::dimension>
inline constexpr std::common_type_t<typename Target::rep, typename Source::rep> conversion_factor(Target, Source) inline constexpr std::common_type_t<typename Target::rep, typename Source::rep> conversion_factor(Target, Source)
{ {
// get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion // get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion

View File

@@ -27,7 +27,7 @@
namespace { namespace {
template<units::Quantity Target, units::Quantity Source> template<units::Quantity Target, units::Quantity Source>
requires units::equivalent_dim<typename Source::dimension, typename Target::dimension> requires units::equivalent<typename Source::dimension, typename Target::dimension>
inline constexpr std::common_type_t<typename Target::rep, typename Source::rep> conversion_factor(Target, Source) inline constexpr std::common_type_t<typename Target::rep, typename Source::rep> conversion_factor(Target, Source)
{ {
// get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion // get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion

View File

@@ -27,6 +27,9 @@ project(mp-units
LANGUAGES CXX LANGUAGES CXX
) )
set(DOWNCAST_MODE AUTO CACHE STRING "Select downcasting mode")
set_property(CACHE DOWNCAST_MODE PROPERTY STRINGS AUTO ON OFF)
# set path to custom cmake modules # set path to custom cmake modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
@@ -63,6 +66,7 @@ target_include_directories(units
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include> $<INSTALL_INTERFACE:include>
) )
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_link_libraries(units target_link_libraries(units
INTERFACE INTERFACE
@@ -86,6 +90,18 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
) )
endif() endif()
endif() endif()
if(DOWNCAST_MODE STREQUAL "AUTO")
message(STATUS "Configuring DOWNCAST_MODE=AUTOMATIC")
target_compile_definitions(units INTERFACE DOWNCAST_MODE=2)
elseif(DOWNCAST_MODE)
message(STATUS "Configuring DOWNCAST_MODE=ON")
target_compile_definitions(units INTERFACE DOWNCAST_MODE=1)
else()
message(STATUS "Configuring DOWNCAST_MODE=OFF")
target_compile_definitions(units INTERFACE DOWNCAST_MODE=0)
endif()
add_library(mp::units ALIAS units) add_library(mp::units ALIAS units)
# installation info # installation info

View File

@@ -23,15 +23,11 @@
#pragma once #pragma once
#include <units/bits/dimension_op.h> #include <units/bits/dimension_op.h>
#include <units/bits/equivalent.h>
#include <units/quantity_cast.h>
namespace units { namespace units {
template<Dimension D, UnitOf<D> U, Scalar Rep>
class quantity;
template<Dimension D, UnitOf<D> U, Scalar Rep>
class quantity_point;
namespace detail { namespace detail {
template<typename Q1, typename Q2, typename Rep> template<typename Q1, typename Q2, typename Rep>
@@ -55,9 +51,8 @@ struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep>
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep>
struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> { struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> {
static constexpr ratio r1 = D1::base_units_ratio * U1::ratio; using dimension = conditional<is_instantiation_of<D1, unknown_dimension>, D2, D1>;
static constexpr ratio r2 = D2::base_units_ratio * U2::ratio; using type = quantity<dimension, downcast_unit<dimension, common_ratio(U1::ratio, U2::ratio)>, Rep>;
using type = quantity<D1, downcast_unit<D1, common_ratio(r1, r2)>, Rep>;
}; };
template<typename D, typename U, typename Rep> template<typename D, typename U, typename Rep>
@@ -66,7 +61,7 @@ quantity_point<D, U, Rep> common_quantity_point_impl(quantity<D, U, Rep>);
} // namespace detail } // namespace detail
template<Quantity Q1, Quantity Q2, Scalar Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>> template<Quantity Q1, Quantity Q2, Scalar Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
requires equivalent_dim<typename Q1::dimension, typename Q2::dimension> requires equivalent<typename Q1::dimension, typename Q2::dimension>
using common_quantity = detail::common_quantity_impl<Q1, Q2, Rep>::type; using common_quantity = detail::common_quantity_impl<Q1, Q2, Rep>::type;
template<QuantityPoint QP1, QuantityPoint QP2> template<QuantityPoint QP1, QuantityPoint QP2>
@@ -87,7 +82,7 @@ namespace concepts {
#endif #endif
template<units::Quantity Q1, units::Quantity Q2> template<units::Quantity Q1, units::Quantity Q2>
requires units::equivalent_dim<typename Q1::dimension, typename Q2::dimension> requires units::equivalent<typename Q1::dimension, typename Q2::dimension>
struct common_type<Q1, Q2> { struct common_type<Q1, Q2> {
using type = units::common_quantity<Q1, Q2>; using type = units::common_quantity<Q1, Q2>;
}; };

View File

@@ -27,40 +27,6 @@
namespace units { namespace units {
// equivalent_dim
namespace detail {
template<BaseDimension D1, BaseDimension D2>
using equivalent_base_dim = std::conjunction<std::bool_constant<D1::symbol == D2::symbol>,
same_unit_reference<typename D1::base_unit, typename D2::base_unit>>;
template<Dimension D1, Dimension D2>
struct equivalent_dim_impl : std::false_type {};
template<BaseDimension D1, BaseDimension D2>
struct equivalent_dim_impl<D1, D2> : std::disjunction<is_same<D1, D2>, equivalent_base_dim<D1, D2>> {};
template<Exponent E1, Exponent E2>
struct equivalent_exp : std::false_type {};
template<BaseDimension Dim1, std::intmax_t Num, std::intmax_t Den, BaseDimension Dim2>
struct equivalent_exp<exp<Dim1, Num, Den>, exp<Dim2, Num, Den>> : equivalent_dim_impl<Dim1, Dim2> {};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_derived_dim : std::false_type {};
template<typename... Es1, typename... Es2>
requires (sizeof...(Es1) == sizeof...(Es2))
struct equivalent_derived_dim<derived_dimension_base<Es1...>, derived_dimension_base<Es2...>> : std::conjunction<equivalent_exp<Es1, Es2>...> {};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_dim_impl<D1, D2> : std::disjunction<is_same<D1, D2>, equivalent_derived_dim<downcast_base_t<D1>, downcast_base_t<D2>>> {};
} // namespace detail
template<Dimension D1, Dimension D2>
inline constexpr bool equivalent_dim = detail::equivalent_dim_impl<D1, D2>::value;
/** /**
* @brief Unknown dimension * @brief Unknown dimension
* *
@@ -72,9 +38,7 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl<D1, D2>::valu
* @tparam ERest the list of exponents of ingredient dimensions * @tparam ERest the list of exponents of ingredient dimensions
*/ */
template<Exponent E, Exponent... ERest> template<Exponent E, Exponent... ERest>
struct unknown_dimension : derived_dimension<unknown_dimension<E, ERest...>, scaled_unit<ratio(1), unknown_coherent_unit>, E, ERest...> { struct unknown_dimension : derived_dimension<unknown_dimension<E, ERest...>, unknown_coherent_unit, E, ERest...> {};
using coherent_unit = scaled_unit<ratio(1), unknown_coherent_unit>;
};
namespace detail { namespace detail {

View File

@@ -0,0 +1,98 @@
// 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/concepts.h>
#include <units/unit.h>
namespace units {
namespace detail {
template<typename T, typename U>
struct equivalent_impl : std::false_type {
};
// units
template<Unit U1, Unit U2>
struct equivalent_impl<U1, U2> : std::disjunction<std::is_same<U1, U2>, std::is_base_of<U1, U2>, std::is_base_of<U2, U1>> {};
// dimensions
template<BaseDimension D1, BaseDimension D2>
struct equivalent_base_dim :
std::conjunction<std::bool_constant<D1::symbol == D2::symbol>,
same_unit_reference<typename D1::base_unit, typename D2::base_unit>> {
};
template<BaseDimension D1, BaseDimension D2>
struct equivalent_impl<D1, D2> : std::disjunction<std::is_same<D1, D2>, equivalent_base_dim<D1, D2>> {
};
template<Exponent E1, Exponent E2>
struct equivalent_exp : std::false_type {
};
template<BaseDimension Dim1, std::intmax_t Num, std::intmax_t Den, BaseDimension Dim2>
struct equivalent_exp<exp<Dim1, Num, Den>, exp<Dim2, Num, Den>> : equivalent_impl<Dim1, Dim2> {
};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_derived_dim : std::false_type {
};
template<typename... Es1, typename... Es2>
requires(sizeof...(Es1) == sizeof...(Es2))
struct equivalent_derived_dim<derived_dimension_base<Es1...>, derived_dimension_base<Es2...>> :
std::conjunction<equivalent_exp<Es1, Es2>...> {
};
template<DerivedDimension D1, DerivedDimension D2>
struct equivalent_impl<D1, D2> :
std::disjunction<std::is_same<D1, D2>, std::is_base_of<D1, D2>, std::is_base_of<D2, D1>,
equivalent_derived_dim<downcast_base_t<D1>, downcast_base_t<D2>>> {
};
// additionally accounts for unknown dimensions
template<Unit U1, Dimension D1, Unit U2, Dimension D2>
struct equivalent_unit : std::disjunction<equivalent_impl<U1, U2>,
std::bool_constant<U1::ratio / dimension_unit<D1>::ratio == U2::ratio / dimension_unit<D2>::ratio>> {};
// quantities and quantity points
template<typename Q1, typename Q2>
requires (Quantity<Q1> && Quantity<Q2>) || (QuantityPoint<Q1> && QuantityPoint<Q2>)
struct equivalent_impl<Q1, Q2> : std::disjunction<std::is_same<Q1, Q2>,
std::conjunction<equivalent_impl<typename Q1::dimension, typename Q2::dimension>,
equivalent_unit<typename Q1::unit, typename Q1::dimension,
typename Q2::unit, typename Q2::dimension>>> {};
} // namespace detail
template<typename T, typename U>
inline constexpr bool equivalent = detail::equivalent_impl<T, U>::value;
} // namespace units

View File

@@ -25,44 +25,75 @@
#include <units/bits/external/hacks.h> #include <units/bits/external/hacks.h>
#include <type_traits> #include <type_traits>
#ifdef DOWNCAST_MODE
#if DOWNCAST_MODE < 0 || DOWNCAST_MODE > 2
#error "Invalid DOWNCAST_MODE value"
#endif
#else
#define DOWNCAST_MODE 2
#endif
namespace units { namespace units {
template<typename BaseType> template<typename BaseType>
struct downcast_base { struct downcast_base {
using downcast_base_type = BaseType; using downcast_base_type = BaseType;
friend auto downcast_guide(downcast_base); friend auto downcast_guide(downcast_base);
friend auto downcast_poison_pill(downcast_base);
}; };
template<typename T> template<typename T>
concept Downcastable = concept Downcastable =
requires { requires { typename T::downcast_base_type; } &&
typename T::downcast_base_type;
} &&
std::derived_from<T, downcast_base<typename T::downcast_base_type>>; std::derived_from<T, downcast_base<typename T::downcast_base_type>>;
template<typename Target, Downcastable T>
struct downcast_child : T {
friend auto downcast_guide(typename downcast_child::downcast_base /* base */) { return Target(); }
};
namespace detail {
template<typename T> template<typename T>
concept has_downcast = concept has_downcast_guide =
requires { requires {
downcast_guide(std::declval<downcast_base<T>>()); downcast_guide(std::declval<downcast_base<T>>());
}; };
template<typename T>
concept has_downcast_poison_pill =
requires {
downcast_poison_pill(std::declval<downcast_base<T>>());
};
template<typename Target, Downcastable T>
struct downcast_child : T {
friend auto downcast_guide(typename T::downcast_base)
{ return Target(); }
};
template<Downcastable T>
struct downcast_poison : T {
friend auto downcast_poison_pill(typename T::downcast_base)
{ return true; }
};
enum class downcast_mode {
off = 0, // no downcasting at all
on = 1, // downcasting always forced -> compile-time errors in case of duplicated definitions
automatic = 2 // downcasting automatically enabled if no collisions are present
};
template<typename Target, Downcastable T, downcast_mode mode = static_cast<downcast_mode>(DOWNCAST_MODE)>
struct downcast_dispatch : std::conditional_t<mode == downcast_mode::off, T,
std::conditional_t<mode == downcast_mode::automatic && has_downcast_guide<T>,
downcast_poison<T>, downcast_child<Target, T>>> {};
namespace detail {
template<typename T> template<typename T>
constexpr auto downcast_impl() constexpr auto downcast_impl()
{ {
if constexpr (has_downcast<T>) if constexpr(has_downcast_guide<T> && !has_downcast_poison_pill<T>)
return decltype(downcast_guide(std::declval<downcast_base<T>>()))(); return decltype(downcast_guide(std::declval<downcast_base<T>>()))();
else else
return T(); return T();
} }
} // namespace detail }
template<Downcastable T> template<Downcastable T>
using downcast = decltype(detail::downcast_impl<T>()); using downcast = decltype(detail::downcast_impl<T>());

View File

@@ -79,7 +79,7 @@ using make_dimension = to_derived_dimension_base<typename dim_consolidate<type_l
* @tparam ERest the list of exponents of ingredient dimensions * @tparam ERest the list of exponents of ingredient dimensions
*/ */
template<typename Child, Unit U, Exponent E, Exponent... ERest> template<typename Child, Unit U, Exponent E, Exponent... ERest>
struct derived_dimension : downcast_child<Child, typename detail::make_dimension<E, ERest...>> { struct derived_dimension : downcast_dispatch<Child, typename detail::make_dimension<E, ERest...>> {
using recipe = exp_list<E, ERest...>; using recipe = exp_list<E, ERest...>;
using coherent_unit = U; using coherent_unit = U;
static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents()); static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents());

View File

@@ -70,7 +70,7 @@ struct prefix_base : downcast_base<prefix_base<PF, R>> {
*/ */
template<typename Child, PrefixFamily PF, basic_symbol_text Symbol, ratio R> template<typename Child, PrefixFamily PF, basic_symbol_text Symbol, ratio R>
requires (!std::same_as<PF, no_prefix>) requires (!std::same_as<PF, no_prefix>)
struct prefix : downcast_child<Child, detail::prefix_base<PF, R>> { struct prefix : downcast_dispatch<Child, detail::prefix_base<PF, R>, downcast_mode::on> {
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;
}; };

View File

@@ -43,10 +43,10 @@ concept safe_convertible = // exposition only
std::convertible_to<From, To> && std::convertible_to<From, To> &&
(treat_as_floating_point<To> || (!treat_as_floating_point<From>)); (treat_as_floating_point<To> || (!treat_as_floating_point<From>));
template<typename Rep, typename UnitFrom, typename UnitTo> template<typename Rep, typename QuantityFrom, typename QuantityTo>
concept safe_divisible = // exposition only concept safe_divisible = // exposition only
treat_as_floating_point<Rep> || treat_as_floating_point<Rep> ||
is_integral(UnitFrom::ratio / UnitTo::ratio); is_integral(quantity_ratio(QuantityFrom{}) / quantity_ratio(QuantityTo{}));
} // namespace detail } // namespace detail
@@ -78,9 +78,9 @@ public:
constexpr explicit quantity(const Value& v) : value_{static_cast<rep>(v)} {} constexpr explicit quantity(const Value& v) : value_{static_cast<rep>(v)} {}
template<Quantity Q2> template<Quantity Q2>
requires equivalent_dim<D, typename Q2::dimension> && requires equivalent<D, typename Q2::dimension> &&
detail::safe_convertible<typename Q2::rep, rep> && detail::safe_convertible<typename Q2::rep, rep> &&
detail::safe_divisible<rep, typename Q2::unit, unit> detail::safe_divisible<rep, Q2, quantity>
constexpr quantity(const Q2& q) : value_{quantity_cast<quantity>(q).count()} {} constexpr quantity(const Q2& q) : value_{quantity_cast<quantity>(q).count()} {}
quantity& operator=(const quantity&) = default; quantity& operator=(const quantity&) = default;
@@ -223,7 +223,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::three_way_comparable_with<Rep, Rep2> std::three_way_comparable_with<Rep, Rep2>
{ {
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>; using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
@@ -232,7 +232,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::equality_comparable_with<Rep, Rep2> std::equality_comparable_with<Rep, Rep2>
{ {
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>; using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
@@ -243,7 +243,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::equality_comparable_with<Rep, Rep2> std::equality_comparable_with<Rep, Rep2>
{ {
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>; using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
@@ -252,7 +252,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::equality_comparable_with<Rep, Rep2> std::equality_comparable_with<Rep, Rep2>
{ {
return !(lhs == rhs); return !(lhs == rhs);
@@ -260,7 +260,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::totally_ordered_with<Rep, Rep2> std::totally_ordered_with<Rep, Rep2>
{ {
using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>; using cq = common_quantity<quantity, quantity<D2, U2, Rep2>>;
@@ -269,7 +269,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::totally_ordered_with<Rep, Rep2> std::totally_ordered_with<Rep, Rep2>
{ {
return !(rhs < lhs); return !(rhs < lhs);
@@ -277,7 +277,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::totally_ordered_with<Rep, Rep2> std::totally_ordered_with<Rep, Rep2>
{ {
return rhs < lhs; return rhs < lhs;
@@ -285,7 +285,7 @@ public:
template<typename D2, typename U2, typename Rep2> template<typename D2, typename U2, typename Rep2>
[[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity<D2, U2, Rep2>& rhs)
requires equivalent_dim<D, D2> && requires equivalent<D, D2> &&
std::totally_ordered_with<Rep, Rep2> std::totally_ordered_with<Rep, Rep2>
{ {
return !(lhs < rhs); return !(lhs < rhs);
@@ -337,14 +337,14 @@ template<Scalar Value, typename D, typename U, typename Rep>
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
[[nodiscard]] constexpr Scalar AUTO operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] constexpr Scalar AUTO operator*(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
requires std::regular_invocable<std::multiplies<>, Rep1, Rep2> && requires std::regular_invocable<std::multiplies<>, Rep1, Rep2> &&
equivalent_dim<D1, dim_invert<D2>> equivalent<D1, dim_invert<D2>>
{ {
using common_rep = decltype(lhs.count() * rhs.count()); using common_rep = decltype(lhs.count() * rhs.count());
const ratio r = U1::ratio * U2::ratio; const ratio r = U1::ratio * U2::ratio;
if constexpr (treat_as_floating_point<common_rep>) { if constexpr (treat_as_floating_point<common_rep>) {
return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * fpow10<common_rep>(r.exp)) / static_cast<common_rep>(r.den); return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * detail::fpow10<common_rep>(r.exp)) / static_cast<common_rep>(r.den);
} else { } else {
return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * ipow10(r.exp)) / static_cast<common_rep>(r.den); return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * detail::ipow10(r.exp)) / static_cast<common_rep>(r.den);
} }
} }
@@ -386,7 +386,7 @@ template<typename D, typename U, typename Rep, Scalar Value>
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2>
[[nodiscard]] constexpr Scalar AUTO operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs) [[nodiscard]] constexpr Scalar AUTO operator/(const quantity<D1, U1, Rep1>& lhs, const quantity<D2, U2, Rep2>& rhs)
requires std::regular_invocable<std::divides<>, Rep1, Rep2> && requires std::regular_invocable<std::divides<>, Rep1, Rep2> &&
equivalent_dim<D1, D2> equivalent<D1, D2>
{ {
Expects(rhs.count() != 0); Expects(rhs.count() != 0);

View File

@@ -30,6 +30,14 @@
namespace units { namespace units {
template<Dimension D, UnitOf<D> U, Scalar Rep>
class quantity;
template<Dimension D, UnitOf<D> U, Scalar Rep>
class quantity_point;
namespace detail {
constexpr std::intmax_t ipow10(std::intmax_t exp) constexpr std::intmax_t ipow10(std::intmax_t exp)
{ {
assert(exp >= 0); assert(exp >= 0);
@@ -61,10 +69,22 @@ constexpr Rep fpow10(std::intmax_t exp)
return result; return result;
} }
template<typename D, typename U, typename Rep>
constexpr auto quantity_ratio(const quantity<D, U, Rep>&)
{
if constexpr(BaseDimension<D>) {
return U::ratio;
}
else {
return D::base_units_ratio * U::ratio / D::coherent_unit::ratio;
}
}
} // namespace detail
// QuantityOf // QuantityOf
template<typename T, typename Dim> template<typename T, typename Dim>
concept QuantityOf = Quantity<T> && Dimension<Dim> && equivalent_dim<typename T::dimension, Dim>; concept QuantityOf = Quantity<T> && Dimension<Dim> && equivalent<typename T::dimension, Dim>;
// quantity_cast // quantity_cast
namespace detail { namespace detail {
@@ -290,17 +310,10 @@ struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
} }
}; };
template<Dimension FromD, Unit FromU, Dimension ToD, Unit ToU> template<typename Q1, typename Q2>
constexpr ratio cast_ratio() constexpr ratio cast_ratio(const Q1& from, const Q2& to)
{ {
if constexpr(BaseDimension<FromD> || same_unit_reference<FromU, ToU>::value) { return quantity_ratio(from) / quantity_ratio(to);
return FromU::ratio / ToU::ratio;
}
else {
const ratio from_ratio = FromD::base_units_ratio * FromU::ratio;
const ratio to_ratio = ToD::base_units_ratio * ToU::ratio;
return from_ratio / to_ratio;
}
} }
} // namespace detail } // namespace detail
@@ -321,7 +334,7 @@ template<Quantity To, typename D, typename U, typename Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
requires QuantityOf<To, D> requires QuantityOf<To, D>
{ {
using c_ratio = std::integral_constant<ratio, detail::cast_ratio<D, U, typename To::dimension, typename To::unit>()>; using c_ratio = std::integral_constant<ratio, detail::cast_ratio(quantity<D, U, Rep>(), To())>;
using c_rep = std::common_type_t<typename To::rep, Rep>; using c_rep = std::common_type_t<typename To::rep, Rep>;
using ret_unit = downcast_unit<typename To::dimension, To::unit::ratio>; using ret_unit = downcast_unit<typename To::dimension, To::unit::ratio>;
using ret = quantity<typename To::dimension, ret_unit, typename To::rep>; using ret = quantity<typename To::dimension, ret_unit, typename To::rep>;
@@ -343,7 +356,7 @@ template<Quantity To, typename D, typename U, typename Rep>
*/ */
template<Dimension ToD, typename D, typename U, typename Rep> template<Dimension ToD, typename D, typename U, typename Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
requires equivalent_dim<ToD, D> requires equivalent<ToD, D>
{ {
return quantity_cast<quantity<ToD, dimension_unit<ToD>, Rep>>(q); return quantity_cast<quantity<ToD, dimension_unit<ToD>, Rep>>(q);
} }

View File

@@ -74,18 +74,11 @@ struct same_unit_reference : is_same<typename U1::reference, typename U2::refere
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
*/ */
template<typename Child> template<typename Child>
struct unit : downcast_child<Child, scaled_unit<ratio(1), Child>> { struct unit : downcast_dispatch<Child, scaled_unit<ratio(1), Child>> {
static constexpr bool is_named = false; static constexpr bool is_named = false;
using prefix_family = no_prefix; using prefix_family = no_prefix;
}; };
/**
* @brief Unknown unit
*
* Used as a coherent unit of an unknown dimension.
*/
struct unknown_coherent_unit : unit<unknown_coherent_unit> {};
/** /**
* @brief A named unit * @brief A named unit
* *
@@ -99,7 +92,7 @@ struct unknown_coherent_unit : unit<unknown_coherent_unit> {};
* @tparam PF no_prefix or a type of prefix family * @tparam PF no_prefix or a type of prefix family
*/ */
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF> template<typename Child, basic_symbol_text Symbol, PrefixFamily PF>
struct named_unit : downcast_child<Child, scaled_unit<ratio(1), Child>> { struct named_unit : downcast_dispatch<Child, scaled_unit<ratio(1), Child>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;
using prefix_family = PF; using prefix_family = PF;
@@ -121,7 +114,7 @@ struct named_unit : downcast_child<Child, scaled_unit<ratio(1), Child>> {
*/ */
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF, ratio R, Unit U> template<typename Child, basic_symbol_text Symbol, PrefixFamily PF, ratio R, Unit U>
requires UnitRatio<R> requires UnitRatio<R>
struct named_scaled_unit : downcast_child<Child, scaled_unit<R * U::ratio, typename U::reference>> { struct named_scaled_unit : downcast_dispatch<Child, scaled_unit<R * U::ratio, typename U::reference>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;
using prefix_family = PF; using prefix_family = PF;
@@ -140,7 +133,7 @@ struct named_scaled_unit : downcast_child<Child, scaled_unit<R * U::ratio, typen
*/ */
template<typename Child, Prefix P, Unit U> template<typename Child, Prefix P, Unit U>
requires U::is_named && std::same_as<typename P::prefix_family, typename U::prefix_family> requires U::is_named && std::same_as<typename P::prefix_family, typename U::prefix_family>
struct prefixed_unit : downcast_child<Child, scaled_unit<P::ratio * U::ratio, typename U::reference>> { struct prefixed_unit : downcast_dispatch<Child, scaled_unit<P::ratio * U::ratio, typename U::reference>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = P::symbol + U::symbol; static constexpr auto symbol = P::symbol + U::symbol;
using prefix_family = no_prefix; using prefix_family = no_prefix;
@@ -162,7 +155,7 @@ struct prefixed_unit : downcast_child<Child, scaled_unit<P::ratio * U::ratio, ty
template<typename Child, DerivedDimension Dim, Unit U, Unit... URest> template<typename Child, DerivedDimension Dim, Unit U, Unit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...> && requires detail::same_scaled_units<typename Dim::recipe, U, URest...> &&
(U::is_named && (URest::is_named && ... && true)) (U::is_named && (URest::is_named && ... && true))
struct deduced_unit : downcast_child<Child, detail::deduced_unit<Dim, U, URest...>> { struct deduced_unit : downcast_dispatch<Child, detail::deduced_unit<Dim, U, URest...>> {
static constexpr bool is_named = false; static constexpr bool is_named = false;
static constexpr auto symbol = detail::deduced_symbol_text<Dim, U, URest...>(); static constexpr auto symbol = detail::deduced_symbol_text<Dim, U, URest...>();
using prefix_family = no_prefix; using prefix_family = no_prefix;
@@ -186,7 +179,7 @@ template<typename Child, DerivedDimension Dim, Unit U, Unit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...> && requires detail::same_scaled_units<typename Dim::recipe, U, URest...> &&
(U::is_named && (URest::is_named && ... && true)) (U::is_named && (URest::is_named && ... && true))
// TODO - 'noble' is placeholder to sort of mean can pass its name on to other deduced units // TODO - 'noble' is placeholder to sort of mean can pass its name on to other deduced units
struct noble_deduced_unit : downcast_child<Child, detail::deduced_unit<Dim, U, URest...>> { struct noble_deduced_unit : downcast_dispatch<Child, detail::deduced_unit<Dim, U, URest...>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = detail::deduced_symbol_text<Dim, U, URest...>(); static constexpr auto symbol = detail::deduced_symbol_text<Dim, U, URest...>();
using prefix_family = no_prefix; using prefix_family = no_prefix;
@@ -210,7 +203,7 @@ struct noble_deduced_unit : downcast_child<Child, detail::deduced_unit<Dim, U, U
*/ */
template<typename Child, DerivedDimension Dim, basic_symbol_text Symbol, PrefixFamily PF, Unit U, Unit... URest> template<typename Child, DerivedDimension Dim, basic_symbol_text Symbol, PrefixFamily PF, Unit U, Unit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...> requires detail::same_scaled_units<typename Dim::recipe, U, URest...>
struct named_deduced_unit : downcast_child<Child, detail::deduced_unit<Dim, U, URest...>> { struct named_deduced_unit : downcast_dispatch<Child, detail::deduced_unit<Dim, U, URest...>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;
using prefix_family = PF; using prefix_family = PF;
@@ -259,4 +252,11 @@ struct prefixed_alias_unit : U {
using prefix_family = no_prefix; using prefix_family = no_prefix;
}; };
/**
* @brief Unknown unit
*
* Used as a coherent unit of an unknown dimension.
*/
struct unknown_coherent_unit : unit<unknown_coherent_unit> {};
} // namespace units } // namespace units

View File

@@ -50,6 +50,8 @@ static_assert(centimetre::symbol == "cm");
// speed // speed
static_assert((10q_cm / 5q_s).count() == 2);
static_assert((2q_cm_per_s).count() == 2);
static_assert(10q_cm / 5q_s == 2q_cm_per_s); static_assert(10q_cm / 5q_s == 2q_cm_per_s);
static_assert(10q_cm / 2q_cm_per_s == 5q_s); static_assert(10q_cm / 2q_cm_per_s == 5q_s);
static_assert(10q_cm == 2q_cm_per_s * 5q_s); static_assert(10q_cm == 2q_cm_per_s * 5q_s);
@@ -59,7 +61,11 @@ static_assert(detail::unit_text<dim_speed, centimetre_per_second>() == "cm/s");
// area // area
static_assert(centimetre::ratio / dimension_unit<dim_length>::ratio == ratio(1)); static_assert(centimetre::ratio / dimension_unit<dim_length>::ratio == ratio(1));
static_assert((1q_cm * 1q_cm).count() == 1);
static_assert((1q_cm2).count() == 1);
static_assert(1q_cm * 1q_cm == 1q_cm2); static_assert(1q_cm * 1q_cm == 1q_cm2);
static_assert(100q_cm * 100q_cm == area<physical::si::square_metre>(1));
static_assert(100q_cm * 100q_cm == length<physical::si::metre>(1) * length<physical::si::metre>(1));
static_assert(100q_cm2 / 10q_cm == 10q_cm); static_assert(100q_cm2 / 10q_cm == 10q_cm);
static_assert(detail::unit_text<dim_area, square_centimetre>() == basic_symbol_text("cm²", "cm^2")); static_assert(detail::unit_text<dim_area, square_centimetre>() == basic_symbol_text("cm²", "cm^2"));

View File

@@ -49,11 +49,14 @@ using amplitude_spectral_density = quantity<dim_amplitude_spectral_density, U, R
namespace { namespace {
static_assert(is_same_v<dimension_sqrt<dim_power_spectral_density>, dim_amplitude_spectral_density>); template<typename T, typename U>
static_assert(is_same_v<dimension_pow<dim_amplitude_spectral_density, 2>, dim_power_spectral_density>); inline constexpr bool compare = DOWNCAST_MODE != 0 ? is_same_v<T, U> : (is_same_v<T, U> || units::equivalent<T, U>);
static_assert(is_same_v<decltype(pow<2>(amplitude_spectral_density<volt_per_sqrt_hertz>(4))), decltype(power_spectral_density<sq_volt_per_hertz>(16))>); static_assert(compare<dimension_sqrt<dim_power_spectral_density>, dim_amplitude_spectral_density>);
static_assert(is_same_v<decltype(sqrt(power_spectral_density<sq_volt_per_hertz>(16))), decltype(amplitude_spectral_density<volt_per_sqrt_hertz>(4))>); static_assert(compare<dimension_pow<dim_amplitude_spectral_density, 2>, dim_power_spectral_density>);
static_assert(compare<decltype(pow<2>(amplitude_spectral_density<volt_per_sqrt_hertz>(4))), decltype(power_spectral_density<sq_volt_per_hertz>(16))>);
static_assert(compare<decltype(sqrt(power_spectral_density<sq_volt_per_hertz>(16))), decltype(amplitude_spectral_density<volt_per_sqrt_hertz>(4))>);
} }
@@ -63,6 +66,5 @@ struct kilogram_per_second : unit<kilogram_per_second> {};
struct dim_mass_rate : derived_dimension<dim_mass_rate, kilogram_per_second, units::exp<dim_mass, 1>, units::exp<dim_time, -1>> {}; struct dim_mass_rate : derived_dimension<dim_mass_rate, kilogram_per_second, units::exp<dim_mass, 1>, units::exp<dim_time, -1>> {};
struct kilogram_per_hour : deduced_unit<kilogram_per_hour, dim_mass_rate, kilogram, hour> {}; struct kilogram_per_hour : deduced_unit<kilogram_per_hour, dim_mass_rate, kilogram, hour> {};
constexpr auto a = 1q_kg / 1q_h; constexpr auto a = 1q_kg / 1q_h;
static_assert(is_same_v<decltype(a)::unit, kilogram_per_hour>);
} }

View File

@@ -20,6 +20,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include "units/math.h"
#include "units/physical/international/area.h"
#include "units/physical/si/area.h" #include "units/physical/si/area.h"
#include "units/physical/si/speed.h" #include "units/physical/si/speed.h"
#include "units/physical/international/area.h" #include "units/physical/international/area.h"
@@ -27,18 +29,20 @@
namespace { namespace {
using namespace units; using namespace units;
using namespace units::physical::si::literals; using namespace units::physical::si::literals;
using namespace units::physical::international::literals; using namespace units::physical::international::literals;
static_assert(is_same_v<decltype(pow<0>(2q_m)), std::int64_t>); template<typename T, typename U>
static_assert(is_same_v<decltype(pow<1>(2q_m)), decltype(2q_m)>); inline constexpr bool compare = DOWNCAST_MODE != 0 ? is_same_v<T, U> : (is_same_v<T, U> || units::equivalent<T, U>);
static_assert(is_same_v<decltype(pow<2>(2q_m)), decltype(4q_m2)>);
static_assert(is_same_v<decltype(pow<2>(2q_km)), decltype(4q_km2)>);
static_assert(is_same_v<decltype(pow<2>(2q_ft)), decltype(4q_ft2)>);
static_assert(is_same_v<decltype(sqrt(4q_m2)), decltype(2q_m)>);
static_assert(is_same_v<decltype(sqrt(4q_km2)), decltype(2q_km)>);
static_assert(is_same_v<decltype(sqrt(4q_ft2)), decltype(2q_ft)>);
static_assert(compare<decltype(pow<0>(2q_m)), std::int64_t>);
static_assert(compare<decltype(pow<1>(2q_m)), decltype(2q_m)>);
static_assert(compare<decltype(pow<2>(2q_m)), decltype(4q_m2)>);
static_assert(compare<decltype(pow<2>(2q_km)), decltype(4q_km2)>);
static_assert(compare<decltype(pow<2>(2q_ft)), decltype(4q_ft2)>);
static_assert(compare<decltype(sqrt(4q_m2)), decltype(2q_m)>);
static_assert(compare<decltype(sqrt(4q_km2)), decltype(2q_km)>);
static_assert(compare<decltype(sqrt(4q_ft2)), decltype(2q_ft)>);
} // namespace } // namespace

View File

@@ -35,6 +35,9 @@ namespace {
using namespace units; using namespace units;
using namespace units::physical::si; using namespace units::physical::si;
template<typename T, typename U>
inline constexpr bool compare = DOWNCAST_MODE != 0 ? std::is_same_v<T, U> : (std::is_same_v<T, U> || units::equivalent<T, U>);
// class invariants // class invariants
template<typename DimLength> template<typename DimLength>
@@ -119,23 +122,23 @@ static_assert((quantity_point(2q_m) -= 1q_m).relative().count() == 1);
// non-member arithmetic operators // non-member arithmetic operators
static_assert(is_same_v<decltype(quantity_point<dim_length, metre, int>() + length<metre, double>()), static_assert(compare<decltype(quantity_point<dim_length, metre, int>() + length<metre, double>()),
quantity_point<dim_length, metre, double>>); quantity_point<dim_length, metre, double>>);
static_assert(is_same_v<decltype(length<metre, int>() + quantity_point<dim_length, metre, double>()), static_assert(compare<decltype(length<metre, int>() + quantity_point<dim_length, metre, double>()),
quantity_point<dim_length, metre, double>>); quantity_point<dim_length, metre, double>>);
static_assert(is_same_v<decltype(quantity_point<dim_length, kilometre, int>() + length<metre, double>()), static_assert(compare<decltype(quantity_point<dim_length, kilometre, int>() + length<metre, double>()),
quantity_point<dim_length, metre, double>>); quantity_point<dim_length, metre, double>>);
static_assert(is_same_v<decltype(length<kilometre, int>() + quantity_point<dim_length, metre, double>()), static_assert(compare<decltype(length<kilometre, int>() + quantity_point<dim_length, metre, double>()),
quantity_point<dim_length, metre, double>>); quantity_point<dim_length, metre, double>>);
static_assert(is_same_v<decltype(quantity_point<dim_length, metre, double>() - length<metre, int>()), static_assert(compare<decltype(quantity_point<dim_length, metre, double>() - length<metre, int>()),
quantity_point<dim_length, metre, double>>); quantity_point<dim_length, metre, double>>);
static_assert(is_same_v<decltype(quantity_point<dim_length, kilometre, double>() - length<metre, int>()), static_assert(compare<decltype(quantity_point<dim_length, kilometre, double>() - length<metre, int>()),
quantity_point<dim_length, metre, double>>); quantity_point<dim_length, metre, double>>);
static_assert( static_assert(
is_same_v<decltype(quantity_point<dim_length, metre, double>() - quantity_point<dim_length, metre, int>()), compare<decltype(quantity_point<dim_length, metre, double>() - quantity_point<dim_length, metre, int>()),
length<metre, double>>); length<metre, double>>);
static_assert( static_assert(
is_same_v<decltype(quantity_point<dim_length, kilometre, double>() - quantity_point<dim_length, metre, int>()), compare<decltype(quantity_point<dim_length, kilometre, double>() - quantity_point<dim_length, metre, int>()),
length<metre, double>>); length<metre, double>>);
static_assert((1q_m + km).relative().count() == 1001); static_assert((1q_m + km).relative().count() == 1001);
@@ -187,13 +190,13 @@ static_assert(QuantityPoint<quantity_point<dim_length, millimetre, int>>);
// common_quantity_point // common_quantity_point
static_assert(is_same_v< static_assert(compare<
common_quantity_point<quantity_point<dim_length, metre, int>, quantity_point<dim_length, kilometre, int>>, common_quantity_point<quantity_point<dim_length, metre, int>, quantity_point<dim_length, kilometre, int>>,
quantity_point<dim_length, metre, int>>); quantity_point<dim_length, metre, int>>);
static_assert(is_same_v<common_quantity_point<quantity_point<dim_length, kilometre, long long>, static_assert(compare<common_quantity_point<quantity_point<dim_length, kilometre, long long>,
quantity_point<dim_length, metre, int>>, quantity_point<dim_length, metre, int>>,
quantity_point<dim_length, metre, long long>>); quantity_point<dim_length, metre, long long>>);
static_assert(is_same_v<common_quantity_point<quantity_point<dim_length, kilometre, long long>, static_assert(compare<common_quantity_point<quantity_point<dim_length, kilometre, long long>,
quantity_point<dim_length, millimetre, double>>, quantity_point<dim_length, millimetre, double>>,
quantity_point<dim_length, millimetre, double>>); quantity_point<dim_length, millimetre, double>>);
@@ -209,7 +212,7 @@ static_assert(std::equality_comparable_with<decltype(quantity_point(1q_m)), decl
// quantity_cast // quantity_cast
static_assert( static_assert(
is_same_v<decltype(quantity_point_cast<scaled_unit<ratio(1), metre>>(quantity_point(2q_km)))::unit, metre>); compare<decltype(quantity_point_cast<scaled_unit<ratio(1), metre>>(quantity_point(2q_km)))::unit, metre>);
static_assert(quantity_point_cast<quantity_point<dim_length, metre, int>>(quantity_point(2q_km)).relative().count() == static_assert(quantity_point_cast<quantity_point<dim_length, metre, int>>(quantity_point(2q_km)).relative().count() ==
2000); 2000);

View File

@@ -34,6 +34,9 @@ namespace {
using namespace units; using namespace units;
using namespace units::physical::si; using namespace units::physical::si;
template<typename T, typename U>
inline constexpr bool compare = DOWNCAST_MODE != 0 ? std::is_same_v<T, U> : (std::is_same_v<T, U> || units::equivalent<T, U>);
// class invariants // class invariants
// constexpr quantity<si::dim_length, second, int> error(0); // should not compile (unit of a different dimension) // constexpr quantity<si::dim_length, second, int> error(0); // should not compile (unit of a different dimension)
@@ -67,7 +70,7 @@ static_assert(length<metre, double>(1000.0q_m).count() == 1000.0);
static_assert(length<metre, double>(km).count() == 1000.0); static_assert(length<metre, double>(km).count() == 1000.0);
static_assert(length<metre, int>(1q_km).count() == 1000); static_assert(length<metre, int>(1q_km).count() == 1000);
// static_assert(length<metre, int>(1q_s).count() == 1); // should not compile (different dimensions) // static_assert(length<metre, int>(1q_s).count() == 1); // should not compile (different dimensions)
//static_assert(length<kilometre, int>(1010q_m).count() == 1); // should not compile (truncating conversion) // static_assert(length<kilometre, int>(1010q_m).count() == 1); // should not compile (truncating conversion)
// assignment operator // assignment operator
@@ -134,37 +137,34 @@ static_assert((2.5q_m *= 3.5).count() == 8.75);
// non-member arithmetic operators // non-member arithmetic operators
static_assert(is_same_v<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>); static_assert(compare<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>);
static_assert(is_same_v<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>); static_assert(compare<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>);
static_assert(compare<decltype(length<kilometre, int>() + length<metre, double>()), length<metre, double>>);
static_assert(compare<decltype(length<metre, double>() - length<metre, int>()), length<metre, double>>);
static_assert(compare<decltype(length<kilometre, double>() - length<metre, int>()), length<metre, double>>);
static_assert(compare<decltype(length<metre, int>() * 1.0), length<metre, double>>);
static_assert(compare<decltype(1.0 * length<metre, int>()), length<metre, double>>);
static_assert( static_assert(
is_same_v<decltype(length<kilometre, int>() + length<metre, double>()), length<metre, double>>); compare<decltype(speed<metre_per_second, int>() * physical::si::time<second, int>()), length<metre, int>>);
static_assert(is_same_v<decltype(length<metre, double>() - length<metre, int>()), length<metre, double>>);
static_assert( static_assert(
is_same_v<decltype(length<kilometre, double>() - length<metre, int>()), length<metre, double>>); compare<decltype(speed<metre_per_second, int>() * physical::si::time<hour, int>()), length<scaled_unit<ratio(36, 1, 2), metre>, int>>);
static_assert(is_same_v<decltype(length<metre, int>() * 1.0), length<metre, double>>); static_assert(compare<decltype(length<metre>() * physical::si::time<minute>()),
static_assert(is_same_v<decltype(1.0 * length<metre, int>()), length<metre, double>>);
static_assert(
is_same_v<decltype(speed<metre_per_second, int>() * physical::si::time<second, int>()), length<metre, int>>);
static_assert(
is_same_v<decltype(speed<metre_per_second, int>() * physical::si::time<hour, int>()), length<scaled_unit<ratio(36, 1, 2), metre>, int>>);
static_assert(is_same_v<decltype(length<metre>() * physical::si::time<minute>()),
quantity<unknown_dimension<units::exp<dim_length, 1>, units::exp<dim_time, 1>>, scaled_unit<ratio(6, 1, 1), unknown_coherent_unit>>>); quantity<unknown_dimension<units::exp<dim_length, 1>, units::exp<dim_time, 1>>, scaled_unit<ratio(6, 1, 1), unknown_coherent_unit>>>);
static_assert(is_same_v<decltype(1 / physical::si::time<second, int>()), frequency<hertz, int>>); static_assert(compare<decltype(1 / physical::si::time<second, int>()), frequency<hertz, int>>);
static_assert(is_same_v<decltype(1 / physical::si::time<minute, int>()), frequency<scaled_unit<ratio(1, 6, -1), hertz>, int>>); static_assert(compare<decltype(1 / physical::si::time<minute, int>()), frequency<scaled_unit<ratio(1, 6, -1), hertz>, int>>);
static_assert(is_same_v<decltype(1 / frequency<hertz, int>()), physical::si::time<second, int>>); static_assert(compare<decltype(1 / frequency<hertz, int>()), physical::si::time<second, int>>);
static_assert(is_same_v<decltype(1 / length<kilometre>()), static_assert(compare<decltype(1 / length<kilometre>()),
quantity<unknown_dimension<units::exp<dim_length, -1>>, scaled_unit<ratio(1, 1, -3), unknown_coherent_unit>>>); quantity<unknown_dimension<units::exp<dim_length, -1>>, scaled_unit<ratio(1, 1, -3), unknown_coherent_unit>>>);
static_assert(is_same_v<decltype(length<metre, int>() / 1.0), length<metre, double>>); static_assert(std::is_same_v<decltype(length<metre, int>() / 1.0), length<metre, double>>);
static_assert(is_same_v<decltype(length<metre, int>() / length<metre, double>()), double>); static_assert(std::is_same_v<decltype(length<metre, int>() / length<metre, double>()), double>);
static_assert(is_same_v<decltype(length<kilometre, int>() / length<metre, double>()), double>); static_assert(std::is_same_v<decltype(length<kilometre, int>() / length<metre, double>()), double>);
static_assert(compare<decltype(length<metre, int>() / physical::si::time<second, int>()), speed<metre_per_second, int>>);
static_assert( static_assert(
is_same_v<decltype(length<metre, int>() / physical::si::time<second, int>()), speed<metre_per_second, int>>); compare<decltype(length<metre>() / physical::si::time<minute>()), speed<scaled_unit<ratio(1, 6, -1), metre_per_second>>>);
static_assert( static_assert(compare<decltype(physical::si::time<minute>() / length<metre>()),
is_same_v<decltype(length<metre>() / physical::si::time<minute>()), speed<scaled_unit<ratio(1, 6, -1), metre_per_second>>>); quantity<unknown_dimension<units::exp<dim_length, -1>, units::exp<dim_time, 1>>, scaled_unit<ratio(6, 1, 1), unknown_coherent_unit>>>);
static_assert(is_same_v<decltype(physical::si::time<minute>() / length<metre>()), static_assert(std::is_same_v<decltype(length<metre, int>() % short(1)), length<metre, int>>);
quantity<unknown_dimension<units::exp<dim_length, -1>, units::exp<dim_time, 1>>, scaled_unit<ratio(6 ,1 , 1), unknown_coherent_unit>>>); static_assert(std::is_same_v<decltype(length<metre, int>() % length<metre, short>(1)), length<metre, int>>);
static_assert(is_same_v<decltype(length<metre, int>() % short(1)), length<metre, int>>);
static_assert(is_same_v<decltype(length<metre, int>() % length<metre, short>(1)), length<metre, int>>);
static_assert((1q_m + km).count() == 1001); static_assert((1q_m + km).count() == 1001);
static_assert((1q_m + 1q_km).count() == 1001); static_assert((1q_m + 1q_km).count() == 1001);
@@ -225,11 +225,10 @@ static_assert(Quantity<length<millimetre, int>>);
// common_quantity // common_quantity
static_assert(is_same_v<common_quantity<length<metre, int>, length<kilometre, int>>, length<metre, int>>); static_assert(compare<common_quantity<length<metre, int>, length<kilometre, int>>, length<metre, int>>);
static_assert(compare<common_quantity<length<kilometre, long long>, length<metre, int>>, length<metre, long long>>);
static_assert( static_assert(
is_same_v<common_quantity<length<kilometre, long long>, length<metre, int>>, length<metre, long long>>); compare<common_quantity<length<kilometre, long long>, length<millimetre, double>>, length<millimetre, double>>);
static_assert(is_same_v<common_quantity<length<kilometre, long long>, length<millimetre, double>>,
length<millimetre, double>>);
// common_type // common_type
@@ -242,7 +241,7 @@ static_assert(std::equality_comparable_with<decltype(1q_m), decltype(1q_ft_us)>)
// quantity_cast // quantity_cast
static_assert(is_same_v<decltype(quantity_cast<scaled_unit<ratio(1), metre>>(2q_km))::unit, metre>); static_assert(compare<decltype(quantity_cast<scaled_unit<ratio(1), metre>>(2q_km))::unit, metre>);
static_assert(quantity_cast<length<metre, int>>(2q_km).count() == 2000); static_assert(quantity_cast<length<metre, int>>(2q_km).count() == 2000);
static_assert(quantity_cast<length<kilometre, int>>(2000q_m).count() == 2); static_assert(quantity_cast<length<kilometre, int>>(2000q_m).count() == 2);
@@ -271,6 +270,20 @@ static_assert(1q_km / 1q_s == 1000q_m_per_s);
static_assert(2q_km_per_h * 2q_h == 4q_km); static_assert(2q_km_per_h * 2q_h == 4q_km);
static_assert(2q_km / 2q_km_per_h == 1q_h); static_assert(2q_km / 2q_km_per_h == 1q_h);
static_assert(is_same_v<decltype(pow<2>(2q_m)), decltype(4q_m2)>); static_assert(compare<decltype(pow<2>(2q_m)), decltype(4q_m2)>);
// downcasting
#if DOWNCAST_MODE == 0
static_assert(std::is_same_v<decltype(10q_m / 5q_s), quantity<unknown_dimension<units::exp<dim_length, 1>, units::exp<dim_time, -1>>, scaled_unit<ratio(1), unknown_coherent_unit>, std::int64_t>>);
static_assert(std::is_same_v<decltype(1q_mm + 1q_km), length<scaled_unit<ratio(1, 1, -3), metre>, std::int64_t>>);
#else
static_assert(std::is_same_v<decltype(10q_m / 5q_s), speed<metre_per_second, std::int64_t>>);
static_assert(std::is_same_v<decltype(1q_mm + 1q_km), length<millimetre, std::int64_t>>);
#endif
} // namespace } // namespace

View File

@@ -46,6 +46,13 @@ namespace {
using namespace units::physical; using namespace units::physical;
static_assert(units::detail::quantity_ratio(si::length<si::metre>(1)) == units::ratio(1));
static_assert(units::detail::quantity_ratio(cgs::length<cgs::centimetre>(1)) == units::ratio(1, 100));
static_assert(units::detail::quantity_ratio(si::speed<si::metre_per_second>(1)) == units::ratio(1));
static_assert(units::detail::quantity_ratio(cgs::speed<cgs::centimetre_per_second>(1)) == units::ratio(1, 100));
static_assert(units::detail::quantity_ratio(si::force<si::newton>(1)) == units::ratio(1000)); // defined in terms of kilogram that are 1000 * gram
static_assert(units::detail::quantity_ratio(cgs::force<cgs::dyne>(1)) == units::ratio(1, 100)); // defined in terms of gram so only centimetre ratio counts here
static_assert(cgs::length<cgs::centimetre>(100) == si::length<si::metre>(1)); static_assert(cgs::length<cgs::centimetre>(100) == si::length<si::metre>(1));
static_assert(cgs::mass<cgs::gram>(1'000) == si::mass<si::kilogram>(1)); static_assert(cgs::mass<cgs::gram>(1'000) == si::mass<si::kilogram>(1));
static_assert(cgs::time<cgs::second>(1) == si::time<si::second>(1)); static_assert(cgs::time<cgs::second>(1) == si::time<si::second>(1));

View File

@@ -52,14 +52,14 @@ static_assert(fps::time<fps::second>(1) == si::time<si::second>(1));
static_assert(fps::speed<fps::foot_per_second>(1) == si::speed<si::metre_per_second>(0.3048)); static_assert(fps::speed<fps::foot_per_second>(1) == si::speed<si::metre_per_second>(0.3048));
static_assert(fps::area<fps::square_foot>(1) == si::area<si::square_metre>(0.09290304)); static_assert(fps::area<fps::square_foot>(1) == si::area<si::square_metre>(0.09290304));
static_assert(fps::acceleration<fps::foot_per_second_sq>(1) == si::acceleration<si::metre_per_second_sq>(0.3048)); static_assert(fps::acceleration<fps::foot_per_second_sq>(1) == si::acceleration<si::metre_per_second_sq>(0.3048));
static_assert(fps::force<fps::poundal>(1) >= si::force<si::newton>(0.138254) && static_assert(fps::force<fps::poundal>(1) > si::force<si::newton>(0.138254) &&
fps::force<fps::poundal>(1) <= si::force<si::newton>(0.138256)); fps::force<fps::poundal>(1) < si::force<si::newton>(0.138256));
static_assert(fps::energy<fps::foot_poundal>(1) >= si::energy<si::joule>(0.042140110093804) && static_assert(fps::energy<fps::foot_poundal>(1) > si::energy<si::joule>(0.042140110093804) &&
fps::energy<fps::foot_poundal>(1) <= si::energy<si::joule>(0.042140110093806)); fps::energy<fps::foot_poundal>(1) < si::energy<si::joule>(0.042140110093806));
static_assert(fps::power<fps::foot_poundal_per_second>(1) >= si::power<si::watt>(0.042140110093804) && static_assert(fps::power<fps::foot_poundal_per_second>(1) > si::power<si::watt>(0.042140110093804) &&
fps::power<fps::foot_poundal_per_second>(1) <= si::power<si::watt>(0.042140110093806)); fps::power<fps::foot_poundal_per_second>(1) < si::power<si::watt>(0.042140110093806));
static_assert(fps::pressure<fps::poundal_per_foot_sq>(1) >= si::pressure<si::pascal>(1.4881639435) && static_assert(fps::pressure<fps::poundal_per_foot_sq>(1) > si::pressure<si::pascal>(1.4881639435) &&
fps::pressure<fps::poundal_per_foot_sq>(1) <= si::pressure<si::pascal>(1.4881639437)); fps::pressure<fps::poundal_per_foot_sq>(1) < si::pressure<si::pascal>(1.4881639437));
namespace si_literals { namespace si_literals {
@@ -69,16 +69,17 @@ static_assert(fps::length<fps::foot>(1) == 0.3048q_m);
static_assert(fps::mass<fps::pound>(1) == 0.45359237q_kg); static_assert(fps::mass<fps::pound>(1) == 0.45359237q_kg);
static_assert(fps::time<fps::second>(1) == 1q_s); static_assert(fps::time<fps::second>(1) == 1q_s);
static_assert(fps::speed<fps::foot_per_second>(1) == 0.3048q_m_per_s); static_assert(fps::speed<fps::foot_per_second>(1) == 0.3048q_m_per_s);
static_assert(fps::area<fps::square_foot>(1) == 0.09290304q_m2); static_assert(fps::area<fps::square_foot>(1) > 0.09290303q_m2 &&
fps::area<fps::square_foot>(1) < 0.09290305q_m2);
static_assert(fps::acceleration<fps::foot_per_second_sq>(1) == 0.3048q_m_per_s2); static_assert(fps::acceleration<fps::foot_per_second_sq>(1) == 0.3048q_m_per_s2);
static_assert(fps::force<fps::poundal>(1) >= 0.138254q_N && static_assert(fps::force<fps::poundal>(1) > 0.138254q_N &&
fps::force<fps::poundal>(1) <= 0.138256q_N); fps::force<fps::poundal>(1) < 0.138256q_N);
static_assert(fps::energy<fps::foot_poundal>(1) >= 0.042140110093804q_J && static_assert(fps::energy<fps::foot_poundal>(1) > 0.042140110093804q_J &&
fps::energy<fps::foot_poundal>(1) <= 0.042140110093806q_J); fps::energy<fps::foot_poundal>(1) < 0.042140110093806q_J);
static_assert(fps::power<fps::foot_poundal_per_second>(1) >= 0.042140110093804q_W && static_assert(fps::power<fps::foot_poundal_per_second>(1) > 0.042140110093804q_W &&
fps::power<fps::foot_poundal_per_second>(1) <= 0.042140110093806q_W); fps::power<fps::foot_poundal_per_second>(1) < 0.042140110093806q_W);
static_assert(fps::pressure<fps::poundal_per_foot_sq>(1) >= 1.4881639435q_Pa && static_assert(fps::pressure<fps::poundal_per_foot_sq>(1) > 1.4881639435q_Pa &&
fps::pressure<fps::poundal_per_foot_sq>(1) <= 1.4881639437q_Pa); fps::pressure<fps::poundal_per_foot_sq>(1) < 1.4881639437q_Pa);
} }
namespace fps_literals { namespace fps_literals {
@@ -91,14 +92,14 @@ static_assert(1q_s == si::time<si::second>(1));
static_assert(1q_ft_per_s == si::speed<si::metre_per_second>(0.3048)); static_assert(1q_ft_per_s == si::speed<si::metre_per_second>(0.3048));
static_assert(1q_ft2 == si::area<si::square_metre>(0.09290304)); static_assert(1q_ft2 == si::area<si::square_metre>(0.09290304));
static_assert(1q_ft_per_s2 == si::acceleration<si::metre_per_second_sq>(0.3048)); static_assert(1q_ft_per_s2 == si::acceleration<si::metre_per_second_sq>(0.3048));
static_assert(1q_pdl >= si::force<si::newton>(0.138254) && static_assert(1q_pdl > si::force<si::newton>(0.138254) &&
1q_pdl <= si::force<si::newton>(0.138256)); 1q_pdl < si::force<si::newton>(0.138256));
static_assert(1q_ft_pdl >= si::energy<si::joule>(0.042140110093804) && static_assert(1q_ft_pdl > si::energy<si::joule>(0.042140110093804) &&
1q_ft_pdl <= si::energy<si::joule>(0.042140110093806)); 1q_ft_pdl < si::energy<si::joule>(0.042140110093806));
static_assert(1q_ft_pdl_per_s >= si::power<si::watt>(0.042140110093804) && static_assert(1q_ft_pdl_per_s > si::power<si::watt>(0.042140110093804) &&
1q_ft_pdl_per_s <= si::power<si::watt>(0.042140110093806)); 1q_ft_pdl_per_s < si::power<si::watt>(0.042140110093806));
static_assert(1q_pdl_per_ft2>= si::pressure<si::pascal>(1.4881639435) && static_assert(1q_pdl_per_ft2> si::pressure<si::pascal>(1.4881639435) &&
1q_pdl_per_ft2 <= si::pressure<si::pascal>(1.4881639437)); 1q_pdl_per_ft2 < si::pressure<si::pascal>(1.4881639437));
} }
namespace fps_plus_si_literals { namespace fps_plus_si_literals {
@@ -112,16 +113,17 @@ static_assert(1q_ft == 0.3048q_m);
static_assert(1q_lb == 0.45359237q_kg); static_assert(1q_lb == 0.45359237q_kg);
static_assert(1q_s == 1q_s); static_assert(1q_s == 1q_s);
static_assert(1q_ft_per_s == 0.3048q_m_per_s); static_assert(1q_ft_per_s == 0.3048q_m_per_s);
static_assert(1q_ft2 == 0.09290304q_m2); static_assert(1q_ft2 > 0.09290303q_m2 &&
1q_ft2 < 0.09290305q_m2);
static_assert(1q_ft_per_s2 == 0.3048q_m_per_s2); static_assert(1q_ft_per_s2 == 0.3048q_m_per_s2);
static_assert(1q_pdl >= 0.138254q_N && static_assert(1q_pdl > 0.138254q_N &&
1q_pdl <= 0.138256q_N); 1q_pdl < 0.138256q_N);
static_assert(1q_ft_pdl >= 0.042140110093804q_J && static_assert(1q_ft_pdl > 0.042140110093804q_J &&
1q_ft_pdl <= 0.042140110093806q_J); 1q_ft_pdl < 0.042140110093806q_J);
static_assert(1q_ft_pdl_per_s >= 0.042140110093804q_W && static_assert(1q_ft_pdl_per_s > 0.042140110093804q_W &&
1q_ft_pdl_per_s <= 0.042140110093806q_W); 1q_ft_pdl_per_s < 0.042140110093806q_W);
static_assert(1q_pdl_per_ft2>= 1.4881639435q_Pa && static_assert(1q_pdl_per_ft2 > 1.4881639435q_Pa &&
1q_pdl_per_ft2 <=1.4881639437q_Pa); 1q_pdl_per_ft2 < 1.4881639437q_Pa);
} }

View File

@@ -106,6 +106,7 @@ static_assert(10q_Hz * 1q_min == 600);
static_assert(2 / 1q_Hz == 2q_s); static_assert(2 / 1q_Hz == 2q_s);
// force // force
static_assert(10q_kg * 10q_m_per_s2 == 100q_N); static_assert(10q_kg * 10q_m_per_s2 == 100q_N);
static_assert(100q_N / 1q_m_per_s2 == 100q_kg); static_assert(100q_N / 1q_m_per_s2 == 100q_kg);
static_assert(100q_N / 1q_kg == 100q_m_per_s2); static_assert(100q_N / 1q_kg == 100q_m_per_s2);
@@ -237,8 +238,6 @@ static_assert(kilogray::symbol == "kGy");
// speed // speed
static_assert(is_same_v<decltype(1q_km / 1q_s), speed<scaled_unit<ratio(1, 1, 3), metre_per_second>, std::int64_t>>);
static_assert(10q_m / 5q_s == 2q_m_per_s); static_assert(10q_m / 5q_s == 2q_m_per_s);
static_assert(10 / 5q_s * 1q_m == 2q_m_per_s); static_assert(10 / 5q_s * 1q_m == 2q_m_per_s);
static_assert(1q_km / 1q_s == 1000q_m_per_s); static_assert(1q_km / 1q_s == 1000q_m_per_s);

View File

@@ -21,6 +21,7 @@
// SOFTWARE. // SOFTWARE.
#include "units/unit.h" #include "units/unit.h"
#include "units/bits/equivalent.h"
#include "units/physical/si/prefixes.h" #include "units/physical/si/prefixes.h"
namespace { namespace {
@@ -28,6 +29,9 @@ namespace {
using namespace units; using namespace units;
using namespace units::physical; using namespace units::physical;
template<typename T, typename U>
inline constexpr bool compare = DOWNCAST_MODE != 0 ? std::is_same_v<T, U> : (std::is_same_v<T, U> || units::equivalent<T, U>);
struct metre : named_unit<metre, "m", si::prefix> {}; struct metre : named_unit<metre, "m", si::prefix> {};
struct centimetre : prefixed_unit<centimetre, si::centi, metre> {}; struct centimetre : prefixed_unit<centimetre, si::centi, metre> {};
struct kilometre : prefixed_unit<kilometre, si::kilo, metre> {}; struct kilometre : prefixed_unit<kilometre, si::kilo, metre> {};
@@ -46,11 +50,11 @@ struct metre_per_second : unit<metre_per_second> {};
struct dim_speed : derived_dimension<dim_speed, metre_per_second, units::exp<dim_length, 1>, units::exp<dim_time, -1>> {}; struct dim_speed : derived_dimension<dim_speed, metre_per_second, units::exp<dim_length, 1>, units::exp<dim_time, -1>> {};
struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {}; struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {};
static_assert(is_same_v<downcast<scaled_unit<ratio(1), metre>>, metre>); static_assert(compare<downcast<scaled_unit<ratio(1), metre>>, metre>);
static_assert(is_same_v<downcast<scaled_unit<ratio(1, 1, -2), metre>>, centimetre>); static_assert(compare<downcast<scaled_unit<ratio(1, 1, -2), metre>>, centimetre>);
static_assert(is_same_v<downcast<scaled_unit<ratio(yard::ratio.num, yard::ratio.den, yard::ratio.exp), metre>>, yard>); static_assert(compare<downcast<scaled_unit<ratio(yard::ratio.num, yard::ratio.den, yard::ratio.exp), metre>>, yard>);
static_assert(is_same_v<downcast<scaled_unit<yard::ratio * ratio(1, 3), metre>>, foot>); static_assert(compare<downcast<scaled_unit<yard::ratio * ratio(1, 3), metre>>, foot>);
static_assert(is_same_v<downcast<scaled_unit<kilometre::ratio / hour::ratio, metre_per_second>>, kilometre_per_hour>); static_assert(compare<downcast<scaled_unit<kilometre::ratio / hour::ratio, metre_per_second>>, kilometre_per_hour>);
static_assert(centimetre::symbol == "cm"); static_assert(centimetre::symbol == "cm");
static_assert(kilometre::symbol == "km"); static_assert(kilometre::symbol == "km");