diff --git a/CMakeLists.txt b/CMakeLists.txt index ce7bf7c0..820622dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ enable_testing() add_subdirectory(test) # add usage example -add_subdirectory(example) +# add_subdirectory(example) # generate project documentation add_subdirectory(docs) diff --git a/conanfile.py b/conanfile.py index d5672cd9..d8342456 100644 --- a/conanfile.py +++ b/conanfile.py @@ -49,6 +49,12 @@ class UnitsConan(ConanFile): "fmt/7.0.3", "ms-gsl/3.1.0" ) + options = { + "downcast": ["off", "on", "auto"] + } + default_options = { + "downcast": "auto" + } # scm = { # "type": "git", # "url": "auto", @@ -63,6 +69,13 @@ class UnitsConan(ConanFile): def _configure_cmake(self, folder="src"): 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: # developer's mode (unit tests, examples, documentation, restrictive compilation warnings, ...) cmake.configure() diff --git a/docs/design/quantity.rst b/docs/design/quantity.rst index 6212886b..e60d7b52 100644 --- a/docs/design/quantity.rst +++ b/docs/design/quantity.rst @@ -23,12 +23,12 @@ a few additional member types and functions:: }; template - requires detail::basic_arithmetic && equivalent_dim> + requires detail::basic_arithmetic && equivalent> [[nodiscard]] constexpr Scalar auto operator*(const quantity& lhs, const quantity& rhs); template - requires detail::basic_arithmetic && (!equivalent_dim>) + requires detail::basic_arithmetic && (!equivalent>) [[nodiscard]] constexpr Quantity auto operator*(const quantity& lhs, const quantity& rhs); @@ -38,17 +38,17 @@ a few additional member types and functions:: const quantity& q); template - requires detail::basic_arithmetic && equivalent_dim + requires detail::basic_arithmetic && equivalent [[nodiscard]] constexpr Scalar auto operator/(const quantity& lhs, const quantity& rhs); template - requires detail::basic_arithmetic && (!equivalent_dim) + requires detail::basic_arithmetic && (!equivalent) [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs); 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 units of base dimension (with the same reference unit). diff --git a/example/alternative_namespaces/conversion_factor.cpp b/example/alternative_namespaces/conversion_factor.cpp index 8e11a66e..43c8e273 100644 --- a/example/alternative_namespaces/conversion_factor.cpp +++ b/example/alternative_namespaces/conversion_factor.cpp @@ -28,7 +28,7 @@ namespace { template - requires units::equivalent_dim + requires units::equivalent inline constexpr std::common_type_t conversion_factor(Target, Source) { // get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion diff --git a/example/conversion_factor.cpp b/example/conversion_factor.cpp index 3f9ed02d..86bd36c7 100644 --- a/example/conversion_factor.cpp +++ b/example/conversion_factor.cpp @@ -27,7 +27,7 @@ namespace { template - requires units::equivalent_dim + requires units::equivalent inline constexpr std::common_type_t conversion_factor(Target, Source) { // get quantities looking like inputs but with Q::rep that doesn't have narrowing conversion diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fe22dba3..bafc5417 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,9 @@ project(mp-units 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 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") @@ -63,6 +66,7 @@ target_include_directories(units $ $ ) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_link_libraries(units INTERFACE @@ -86,6 +90,18 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") ) 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) # installation info diff --git a/src/include/units/bits/common_quantity.h b/src/include/units/bits/common_quantity.h index e9fc7a58..33cf6970 100644 --- a/src/include/units/bits/common_quantity.h +++ b/src/include/units/bits/common_quantity.h @@ -23,15 +23,11 @@ #pragma once #include +#include +#include namespace units { -template U, Scalar Rep> -class quantity; - -template U, Scalar Rep> -class quantity_point; - namespace detail { template @@ -55,9 +51,8 @@ struct common_quantity_impl, quantity, Rep> template struct common_quantity_impl, quantity, Rep> { - static constexpr ratio r1 = D1::base_units_ratio * U1::ratio; - static constexpr ratio r2 = D2::base_units_ratio * U2::ratio; - using type = quantity, Rep>; + using dimension = conditional, D2, D1>; + using type = quantity, Rep>; }; template @@ -66,7 +61,7 @@ quantity_point common_quantity_point_impl(quantity); } // namespace detail template> - requires equivalent_dim + requires equivalent using common_quantity = detail::common_quantity_impl::type; template @@ -87,7 +82,7 @@ namespace concepts { #endif template - requires units::equivalent_dim + requires units::equivalent struct common_type { using type = units::common_quantity; }; diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h index 7d6478cf..a838a049 100644 --- a/src/include/units/bits/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -27,40 +27,6 @@ namespace units { -// equivalent_dim -namespace detail { - -template -using equivalent_base_dim = std::conjunction, - same_unit_reference>; - -template -struct equivalent_dim_impl : std::false_type {}; - -template -struct equivalent_dim_impl : std::disjunction, equivalent_base_dim> {}; - -template -struct equivalent_exp : std::false_type {}; - -template -struct equivalent_exp, exp> : equivalent_dim_impl {}; - -template -struct equivalent_derived_dim : std::false_type {}; - -template - requires (sizeof...(Es1) == sizeof...(Es2)) -struct equivalent_derived_dim, derived_dimension_base> : std::conjunction...> {}; - -template -struct equivalent_dim_impl : std::disjunction, equivalent_derived_dim, downcast_base_t>> {}; - -} // namespace detail - -template -inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::value; - /** * @brief Unknown dimension * @@ -72,9 +38,7 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::valu * @tparam ERest the list of exponents of ingredient dimensions */ template -struct unknown_dimension : derived_dimension, scaled_unit, E, ERest...> { - using coherent_unit = scaled_unit; -}; +struct unknown_dimension : derived_dimension, unknown_coherent_unit, E, ERest...> {}; namespace detail { diff --git a/src/include/units/bits/equivalent.h b/src/include/units/bits/equivalent.h new file mode 100644 index 00000000..98ce0c92 --- /dev/null +++ b/src/include/units/bits/equivalent.h @@ -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 +#include + +namespace units { + +namespace detail { + +template +struct equivalent_impl : std::false_type { +}; + +// units + +template +struct equivalent_impl : std::disjunction, std::is_base_of, std::is_base_of> {}; + + +// dimensions + +template +struct equivalent_base_dim : + std::conjunction, + same_unit_reference> { +}; + +template +struct equivalent_impl : std::disjunction, equivalent_base_dim> { +}; + +template +struct equivalent_exp : std::false_type { +}; + +template +struct equivalent_exp, exp> : equivalent_impl { +}; + +template +struct equivalent_derived_dim : std::false_type { +}; + +template + requires(sizeof...(Es1) == sizeof...(Es2)) +struct equivalent_derived_dim, derived_dimension_base> : + std::conjunction...> { +}; + +template +struct equivalent_impl : + std::disjunction, std::is_base_of, std::is_base_of, + equivalent_derived_dim, downcast_base_t>> { +}; + + +// additionally accounts for unknown dimensions +template +struct equivalent_unit : std::disjunction, + std::bool_constant::ratio == U2::ratio / dimension_unit::ratio>> {}; + +// quantities and quantity points + +template + requires (Quantity && Quantity) || (QuantityPoint && QuantityPoint) +struct equivalent_impl : std::disjunction, + std::conjunction, + equivalent_unit>> {}; + +} // namespace detail + +template +inline constexpr bool equivalent = detail::equivalent_impl::value; + +} // namespace units diff --git a/src/include/units/bits/external/downcasting.h b/src/include/units/bits/external/downcasting.h index 469f1556..5d575cab 100644 --- a/src/include/units/bits/external/downcasting.h +++ b/src/include/units/bits/external/downcasting.h @@ -25,44 +25,75 @@ #include #include +#ifdef DOWNCAST_MODE +#if DOWNCAST_MODE < 0 || DOWNCAST_MODE > 2 +#error "Invalid DOWNCAST_MODE value" +#endif +#else +#define DOWNCAST_MODE 2 +#endif + namespace units { template struct downcast_base { using downcast_base_type = BaseType; friend auto downcast_guide(downcast_base); + friend auto downcast_poison_pill(downcast_base); }; template concept Downcastable = - requires { - typename T::downcast_base_type; - } && + requires { typename T::downcast_base_type; } && std::derived_from>; -template -struct downcast_child : T { - friend auto downcast_guide(typename downcast_child::downcast_base /* base */) { return Target(); } -}; - -namespace detail { - template -concept has_downcast = +concept has_downcast_guide = requires { downcast_guide(std::declval>()); }; +template +concept has_downcast_poison_pill = + requires { + downcast_poison_pill(std::declval>()); + }; + +template +struct downcast_child : T { + friend auto downcast_guide(typename T::downcast_base) + { return Target(); } +}; + +template +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(DOWNCAST_MODE)> +struct downcast_dispatch : std::conditional_t, + downcast_poison, downcast_child>> {}; + +namespace detail { + template constexpr auto downcast_impl() { - if constexpr (has_downcast) + if constexpr(has_downcast_guide && !has_downcast_poison_pill) return decltype(downcast_guide(std::declval>()))(); else return T(); } -} // namespace detail +} template using downcast = decltype(detail::downcast_impl()); diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index acc0585b..fb60e288 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -79,7 +79,7 @@ using make_dimension = to_derived_dimension_base -struct derived_dimension : downcast_child> { +struct derived_dimension : downcast_dispatch> { using recipe = exp_list; using coherent_unit = U; static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents()); diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index fbf45665..58138c28 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -70,7 +70,7 @@ struct prefix_base : downcast_base> { */ template requires (!std::same_as) -struct prefix : downcast_child> { +struct prefix : downcast_dispatch, downcast_mode::on> { static constexpr auto symbol = Symbol; }; diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index bc3c3bdf..36910647 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -43,10 +43,10 @@ concept safe_convertible = // exposition only std::convertible_to && (treat_as_floating_point || (!treat_as_floating_point)); -template +template concept safe_divisible = // exposition only treat_as_floating_point || - is_integral(UnitFrom::ratio / UnitTo::ratio); + is_integral(quantity_ratio(QuantityFrom{}) / quantity_ratio(QuantityTo{})); } // namespace detail @@ -78,9 +78,9 @@ public: constexpr explicit quantity(const Value& v) : value_{static_cast(v)} {} template - requires equivalent_dim && + requires equivalent && detail::safe_convertible && - detail::safe_divisible + detail::safe_divisible constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} {} quantity& operator=(const quantity&) = default; @@ -223,7 +223,7 @@ public: template [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::three_way_comparable_with { using cq = common_quantity>; @@ -232,7 +232,7 @@ public: template [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::equality_comparable_with { using cq = common_quantity>; @@ -243,7 +243,7 @@ public: template [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::equality_comparable_with { using cq = common_quantity>; @@ -252,7 +252,7 @@ public: template [[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::equality_comparable_with { return !(lhs == rhs); @@ -260,7 +260,7 @@ public: template [[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::totally_ordered_with { using cq = common_quantity>; @@ -269,7 +269,7 @@ public: template [[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::totally_ordered_with { return !(rhs < lhs); @@ -277,7 +277,7 @@ public: template [[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::totally_ordered_with { return rhs < lhs; @@ -285,7 +285,7 @@ public: template [[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && + requires equivalent && std::totally_ordered_with { return !(lhs < rhs); @@ -337,14 +337,14 @@ template template [[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> && - equivalent_dim> + equivalent> { using common_rep = decltype(lhs.count() * rhs.count()); const ratio r = U1::ratio * U2::ratio; if constexpr (treat_as_floating_point) { - return lhs.count() * rhs.count() * static_cast(r.num * fpow10(r.exp)) / static_cast(r.den); + return lhs.count() * rhs.count() * static_cast(r.num * detail::fpow10(r.exp)) / static_cast(r.den); } else { - return lhs.count() * rhs.count() * static_cast(r.num * ipow10(r.exp)) / static_cast(r.den); + return lhs.count() * rhs.count() * static_cast(r.num * detail::ipow10(r.exp)) / static_cast(r.den); } } @@ -386,7 +386,7 @@ template template [[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) requires std::regular_invocable, Rep1, Rep2> && - equivalent_dim + equivalent { Expects(rhs.count() != 0); diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index 2d4281b1..965f1159 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -30,6 +30,14 @@ namespace units { +template U, Scalar Rep> +class quantity; + +template U, Scalar Rep> +class quantity_point; + +namespace detail { + constexpr std::intmax_t ipow10(std::intmax_t exp) { assert(exp >= 0); @@ -61,10 +69,22 @@ constexpr Rep fpow10(std::intmax_t exp) return result; } +template +constexpr auto quantity_ratio(const quantity&) +{ + if constexpr(BaseDimension) { + return U::ratio; + } + else { + return D::base_units_ratio * U::ratio / D::coherent_unit::ratio; + } +} + +} // namespace detail // QuantityOf template -concept QuantityOf = Quantity && Dimension && equivalent_dim; +concept QuantityOf = Quantity && Dimension && equivalent; // quantity_cast namespace detail { @@ -290,17 +310,10 @@ struct quantity_cast_impl { } }; -template -constexpr ratio cast_ratio() +template +constexpr ratio cast_ratio(const Q1& from, const Q2& to) { - if constexpr(BaseDimension || same_unit_reference::value) { - 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; - } + return quantity_ratio(from) / quantity_ratio(to); } } // namespace detail @@ -321,7 +334,7 @@ template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) requires QuantityOf { - using c_ratio = std::integral_constant()>; + using c_ratio = std::integral_constant(), To())>; using c_rep = std::common_type_t; using ret_unit = downcast_unit; using ret = quantity; @@ -343,7 +356,7 @@ template */ template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires equivalent_dim + requires equivalent { return quantity_cast, Rep>>(q); } diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 8010dba1..d4d5fe3c 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -74,18 +74,11 @@ struct same_unit_reference : is_same -struct unit : downcast_child> { +struct unit : downcast_dispatch> { static constexpr bool is_named = false; using prefix_family = no_prefix; }; -/** - * @brief Unknown unit - * - * Used as a coherent unit of an unknown dimension. - */ -struct unknown_coherent_unit : unit {}; - /** * @brief A named unit * @@ -99,7 +92,7 @@ struct unknown_coherent_unit : unit {}; * @tparam PF no_prefix or a type of prefix family */ template -struct named_unit : downcast_child> { +struct named_unit : downcast_dispatch> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_family = PF; @@ -121,7 +114,7 @@ struct named_unit : downcast_child> { */ template requires UnitRatio -struct named_scaled_unit : downcast_child> { +struct named_scaled_unit : downcast_dispatch> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_family = PF; @@ -140,7 +133,7 @@ struct named_scaled_unit : downcast_child requires U::is_named && std::same_as -struct prefixed_unit : downcast_child> { +struct prefixed_unit : downcast_dispatch> { static constexpr bool is_named = true; static constexpr auto symbol = P::symbol + U::symbol; using prefix_family = no_prefix; @@ -162,7 +155,7 @@ struct prefixed_unit : downcast_child requires detail::same_scaled_units && (U::is_named && (URest::is_named && ... && true)) -struct deduced_unit : downcast_child> { +struct deduced_unit : downcast_dispatch> { static constexpr bool is_named = false; static constexpr auto symbol = detail::deduced_symbol_text(); using prefix_family = no_prefix; @@ -186,7 +179,7 @@ template requires detail::same_scaled_units && (U::is_named && (URest::is_named && ... && true)) // TODO - 'noble' is placeholder to sort of mean can pass its name on to other deduced units -struct noble_deduced_unit : downcast_child> { +struct noble_deduced_unit : downcast_dispatch> { static constexpr bool is_named = true; static constexpr auto symbol = detail::deduced_symbol_text(); using prefix_family = no_prefix; @@ -210,7 +203,7 @@ struct noble_deduced_unit : downcast_child requires detail::same_scaled_units -struct named_deduced_unit : downcast_child> { +struct named_deduced_unit : downcast_dispatch> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_family = PF; @@ -259,4 +252,11 @@ struct prefixed_alias_unit : U { using prefix_family = no_prefix; }; +/** + * @brief Unknown unit + * + * Used as a coherent unit of an unknown dimension. + */ +struct unknown_coherent_unit : unit {}; + } // namespace units diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 8cdd0540..5ee16386 100644 --- a/test/unit_test/static/cgs_test.cpp +++ b/test/unit_test/static/cgs_test.cpp @@ -50,6 +50,8 @@ static_assert(centimetre::symbol == "cm"); // 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 / 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() == "cm/s"); // area static_assert(centimetre::ratio / dimension_unit::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(100q_cm * 100q_cm == area(1)); +static_assert(100q_cm * 100q_cm == length(1) * length(1)); static_assert(100q_cm2 / 10q_cm == 10q_cm); static_assert(detail::unit_text() == basic_symbol_text("cm²", "cm^2")); diff --git a/test/unit_test/static/custom_unit_test.cpp b/test/unit_test/static/custom_unit_test.cpp index 5f773cb2..afdb7891 100644 --- a/test/unit_test/static/custom_unit_test.cpp +++ b/test/unit_test/static/custom_unit_test.cpp @@ -49,11 +49,14 @@ using amplitude_spectral_density = quantity, dim_amplitude_spectral_density>); -static_assert(is_same_v, dim_power_spectral_density>); +template +inline constexpr bool compare = DOWNCAST_MODE != 0 ? is_same_v : (is_same_v || units::equivalent); -static_assert(is_same_v(amplitude_spectral_density(4))), decltype(power_spectral_density(16))>); -static_assert(is_same_v(16))), decltype(amplitude_spectral_density(4))>); +static_assert(compare, dim_amplitude_spectral_density>); +static_assert(compare, dim_power_spectral_density>); + +static_assert(compare(amplitude_spectral_density(4))), decltype(power_spectral_density(16))>); +static_assert(compare(16))), decltype(amplitude_spectral_density(4))>); } @@ -63,6 +66,5 @@ struct kilogram_per_second : unit {}; struct dim_mass_rate : derived_dimension, units::exp> {}; struct kilogram_per_hour : deduced_unit {}; constexpr auto a = 1q_kg / 1q_h; -static_assert(is_same_v); } diff --git a/test/unit_test/static/math_test.cpp b/test/unit_test/static/math_test.cpp index e32967de..2d3c26bb 100644 --- a/test/unit_test/static/math_test.cpp +++ b/test/unit_test/static/math_test.cpp @@ -20,6 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include "units/math.h" +#include "units/physical/international/area.h" #include "units/physical/si/area.h" #include "units/physical/si/speed.h" #include "units/physical/international/area.h" @@ -27,18 +29,20 @@ namespace { - using namespace units; - using namespace units::physical::si::literals; - using namespace units::physical::international::literals; +using namespace units; +using namespace units::physical::si::literals; +using namespace units::physical::international::literals; - static_assert(is_same_v(2q_m)), std::int64_t>); - static_assert(is_same_v(2q_m)), decltype(2q_m)>); - static_assert(is_same_v(2q_m)), decltype(4q_m2)>); - static_assert(is_same_v(2q_km)), decltype(4q_km2)>); - static_assert(is_same_v(2q_ft)), decltype(4q_ft2)>); - static_assert(is_same_v); - static_assert(is_same_v); - static_assert(is_same_v); +template +inline constexpr bool compare = DOWNCAST_MODE != 0 ? is_same_v : (is_same_v || units::equivalent); +static_assert(compare(2q_m)), std::int64_t>); +static_assert(compare(2q_m)), decltype(2q_m)>); +static_assert(compare(2q_m)), decltype(4q_m2)>); +static_assert(compare(2q_km)), decltype(4q_km2)>); +static_assert(compare(2q_ft)), decltype(4q_ft2)>); +static_assert(compare); +static_assert(compare); +static_assert(compare); } // namespace diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index ba5d3aa0..ce65e92f 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -35,6 +35,9 @@ namespace { using namespace units; using namespace units::physical::si; +template +inline constexpr bool compare = DOWNCAST_MODE != 0 ? std::is_same_v : (std::is_same_v || units::equivalent); + // class invariants template @@ -119,23 +122,23 @@ static_assert((quantity_point(2q_m) -= 1q_m).relative().count() == 1); // non-member arithmetic operators -static_assert(is_same_v() + length()), +static_assert(compare() + length()), quantity_point>); -static_assert(is_same_v() + quantity_point()), +static_assert(compare() + quantity_point()), quantity_point>); -static_assert(is_same_v() + length()), +static_assert(compare() + length()), quantity_point>); -static_assert(is_same_v() + quantity_point()), +static_assert(compare() + quantity_point()), quantity_point>); -static_assert(is_same_v() - length()), +static_assert(compare() - length()), quantity_point>); -static_assert(is_same_v() - length()), +static_assert(compare() - length()), quantity_point>); static_assert( - is_same_v() - quantity_point()), + compare() - quantity_point()), length>); static_assert( - is_same_v() - quantity_point()), + compare() - quantity_point()), length>); static_assert((1q_m + km).relative().count() == 1001); @@ -187,13 +190,13 @@ static_assert(QuantityPoint>); // common_quantity_point -static_assert(is_same_v< +static_assert(compare< common_quantity_point, quantity_point>, quantity_point>); -static_assert(is_same_v, +static_assert(compare, quantity_point>, quantity_point>); -static_assert(is_same_v, +static_assert(compare, quantity_point>, quantity_point>); @@ -209,7 +212,7 @@ static_assert(std::equality_comparable_with>(quantity_point(2q_km)))::unit, metre>); + compare>(quantity_point(2q_km)))::unit, metre>); static_assert(quantity_point_cast>(quantity_point(2q_km)).relative().count() == 2000); diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 3e4b20f4..e7b987fc 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -34,6 +34,9 @@ namespace { using namespace units; using namespace units::physical::si; +template +inline constexpr bool compare = DOWNCAST_MODE != 0 ? std::is_same_v : (std::is_same_v || units::equivalent); + // class invariants // constexpr quantity error(0); // should not compile (unit of a different dimension) @@ -67,7 +70,7 @@ static_assert(length(1000.0q_m).count() == 1000.0); static_assert(length(km).count() == 1000.0); static_assert(length(1q_km).count() == 1000); // static_assert(length(1q_s).count() == 1); // should not compile (different dimensions) -//static_assert(length(1010q_m).count() == 1); // should not compile (truncating conversion) +// static_assert(length(1010q_m).count() == 1); // should not compile (truncating conversion) // assignment operator @@ -134,37 +137,34 @@ static_assert((2.5q_m *= 3.5).count() == 8.75); // non-member arithmetic operators -static_assert(is_same_v() + length()), length>); -static_assert(is_same_v() + length()), length>); +static_assert(compare() + length()), length>); +static_assert(compare() + length()), length>); +static_assert(compare() + length()), length>); +static_assert(compare() - length()), length>); +static_assert(compare() - length()), length>); +static_assert(compare() * 1.0), length>); +static_assert(compare()), length>); static_assert( - is_same_v() + length()), length>); -static_assert(is_same_v() - length()), length>); + compare() * physical::si::time()), length>); static_assert( - is_same_v() - length()), length>); -static_assert(is_same_v() * 1.0), length>); -static_assert(is_same_v()), length>); -static_assert( - is_same_v() * physical::si::time()), length>); -static_assert( - is_same_v() * physical::si::time()), length, int>>); -static_assert(is_same_v() * physical::si::time()), + compare() * physical::si::time()), length, int>>); +static_assert(compare() * physical::si::time()), quantity, units::exp>, scaled_unit>>); -static_assert(is_same_v()), frequency>); -static_assert(is_same_v()), frequency, int>>); -static_assert(is_same_v()), physical::si::time>); -static_assert(is_same_v()), +static_assert(compare()), frequency>); +static_assert(compare()), frequency, int>>); +static_assert(compare()), physical::si::time>); +static_assert(compare()), quantity>, scaled_unit>>); -static_assert(is_same_v() / 1.0), length>); -static_assert(is_same_v() / length()), double>); -static_assert(is_same_v() / length()), double>); +static_assert(std::is_same_v() / 1.0), length>); +static_assert(std::is_same_v() / length()), double>); +static_assert(std::is_same_v() / length()), double>); +static_assert(compare() / physical::si::time()), speed>); static_assert( - is_same_v() / physical::si::time()), speed>); -static_assert( - is_same_v() / physical::si::time()), speed>>); -static_assert(is_same_v() / length()), - quantity, units::exp>, scaled_unit>>); -static_assert(is_same_v() % short(1)), length>); -static_assert(is_same_v() % length(1)), length>); + compare() / physical::si::time()), speed>>); +static_assert(compare() / length()), + quantity, units::exp>, scaled_unit>>); +static_assert(std::is_same_v() % short(1)), length>); +static_assert(std::is_same_v() % length(1)), length>); static_assert((1q_m + km).count() == 1001); static_assert((1q_m + 1q_km).count() == 1001); @@ -225,11 +225,10 @@ static_assert(Quantity>); // common_quantity -static_assert(is_same_v, length>, length>); +static_assert(compare, length>, length>); +static_assert(compare, length>, length>); static_assert( - is_same_v, length>, length>); -static_assert(is_same_v, length>, - length>); + compare, length>, length>); // common_type @@ -242,7 +241,7 @@ static_assert(std::equality_comparable_with) // quantity_cast -static_assert(is_same_v>(2q_km))::unit, metre>); +static_assert(compare>(2q_km))::unit, metre>); static_assert(quantity_cast>(2q_km).count() == 2000); static_assert(quantity_cast>(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 / 2q_km_per_h == 1q_h); -static_assert(is_same_v(2q_m)), decltype(4q_m2)>); +static_assert(compare(2q_m)), decltype(4q_m2)>); + +// downcasting + +#if DOWNCAST_MODE == 0 + +static_assert(std::is_same_v, units::exp>, scaled_unit, std::int64_t>>); +static_assert(std::is_same_v, std::int64_t>>); + +#else + +static_assert(std::is_same_v>); +static_assert(std::is_same_v>); + +#endif } // namespace diff --git a/test/unit_test/static/si_cgs_test.cpp b/test/unit_test/static/si_cgs_test.cpp index f828a691..d0a215ee 100644 --- a/test/unit_test/static/si_cgs_test.cpp +++ b/test/unit_test/static/si_cgs_test.cpp @@ -46,6 +46,13 @@ namespace { using namespace units::physical; +static_assert(units::detail::quantity_ratio(si::length(1)) == units::ratio(1)); +static_assert(units::detail::quantity_ratio(cgs::length(1)) == units::ratio(1, 100)); +static_assert(units::detail::quantity_ratio(si::speed(1)) == units::ratio(1)); +static_assert(units::detail::quantity_ratio(cgs::speed(1)) == units::ratio(1, 100)); +static_assert(units::detail::quantity_ratio(si::force(1)) == units::ratio(1000)); // defined in terms of kilogram that are 1000 * gram +static_assert(units::detail::quantity_ratio(cgs::force(1)) == units::ratio(1, 100)); // defined in terms of gram so only centimetre ratio counts here + static_assert(cgs::length(100) == si::length(1)); static_assert(cgs::mass(1'000) == si::mass(1)); static_assert(cgs::time(1) == si::time(1)); diff --git a/test/unit_test/static/si_fps_test.cpp b/test/unit_test/static/si_fps_test.cpp index 49435b1a..b857400f 100644 --- a/test/unit_test/static/si_fps_test.cpp +++ b/test/unit_test/static/si_fps_test.cpp @@ -52,14 +52,14 @@ static_assert(fps::time(1) == si::time(1)); static_assert(fps::speed(1) == si::speed(0.3048)); static_assert(fps::area(1) == si::area(0.09290304)); static_assert(fps::acceleration(1) == si::acceleration(0.3048)); -static_assert(fps::force(1) >= si::force(0.138254) && - fps::force(1) <= si::force(0.138256)); -static_assert(fps::energy(1) >= si::energy(0.042140110093804) && - fps::energy(1) <= si::energy(0.042140110093806)); -static_assert(fps::power(1) >= si::power(0.042140110093804) && - fps::power(1) <= si::power(0.042140110093806)); -static_assert(fps::pressure(1) >= si::pressure(1.4881639435) && - fps::pressure(1) <= si::pressure(1.4881639437)); +static_assert(fps::force(1) > si::force(0.138254) && + fps::force(1) < si::force(0.138256)); +static_assert(fps::energy(1) > si::energy(0.042140110093804) && + fps::energy(1) < si::energy(0.042140110093806)); +static_assert(fps::power(1) > si::power(0.042140110093804) && + fps::power(1) < si::power(0.042140110093806)); +static_assert(fps::pressure(1) > si::pressure(1.4881639435) && + fps::pressure(1) < si::pressure(1.4881639437)); namespace si_literals { @@ -69,16 +69,17 @@ static_assert(fps::length(1) == 0.3048q_m); static_assert(fps::mass(1) == 0.45359237q_kg); static_assert(fps::time(1) == 1q_s); static_assert(fps::speed(1) == 0.3048q_m_per_s); -static_assert(fps::area(1) == 0.09290304q_m2); +static_assert(fps::area(1) > 0.09290303q_m2 && + fps::area(1) < 0.09290305q_m2); static_assert(fps::acceleration(1) == 0.3048q_m_per_s2); -static_assert(fps::force(1) >= 0.138254q_N && - fps::force(1) <= 0.138256q_N); -static_assert(fps::energy(1) >= 0.042140110093804q_J && - fps::energy(1) <= 0.042140110093806q_J); -static_assert(fps::power(1) >= 0.042140110093804q_W && - fps::power(1) <= 0.042140110093806q_W); -static_assert(fps::pressure(1) >= 1.4881639435q_Pa && - fps::pressure(1) <= 1.4881639437q_Pa); +static_assert(fps::force(1) > 0.138254q_N && + fps::force(1) < 0.138256q_N); +static_assert(fps::energy(1) > 0.042140110093804q_J && + fps::energy(1) < 0.042140110093806q_J); +static_assert(fps::power(1) > 0.042140110093804q_W && + fps::power(1) < 0.042140110093806q_W); +static_assert(fps::pressure(1) > 1.4881639435q_Pa && + fps::pressure(1) < 1.4881639437q_Pa); } namespace fps_literals { @@ -91,14 +92,14 @@ static_assert(1q_s == si::time(1)); static_assert(1q_ft_per_s == si::speed(0.3048)); static_assert(1q_ft2 == si::area(0.09290304)); static_assert(1q_ft_per_s2 == si::acceleration(0.3048)); -static_assert(1q_pdl >= si::force(0.138254) && - 1q_pdl <= si::force(0.138256)); -static_assert(1q_ft_pdl >= si::energy(0.042140110093804) && - 1q_ft_pdl <= si::energy(0.042140110093806)); -static_assert(1q_ft_pdl_per_s >= si::power(0.042140110093804) && - 1q_ft_pdl_per_s <= si::power(0.042140110093806)); -static_assert(1q_pdl_per_ft2>= si::pressure(1.4881639435) && - 1q_pdl_per_ft2 <= si::pressure(1.4881639437)); +static_assert(1q_pdl > si::force(0.138254) && + 1q_pdl < si::force(0.138256)); +static_assert(1q_ft_pdl > si::energy(0.042140110093804) && + 1q_ft_pdl < si::energy(0.042140110093806)); +static_assert(1q_ft_pdl_per_s > si::power(0.042140110093804) && + 1q_ft_pdl_per_s < si::power(0.042140110093806)); +static_assert(1q_pdl_per_ft2> si::pressure(1.4881639435) && + 1q_pdl_per_ft2 < si::pressure(1.4881639437)); } 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_s == 1q_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_pdl >= 0.138254q_N && - 1q_pdl <= 0.138256q_N); -static_assert(1q_ft_pdl >= 0.042140110093804q_J && - 1q_ft_pdl <= 0.042140110093806q_J); -static_assert(1q_ft_pdl_per_s >= 0.042140110093804q_W && - 1q_ft_pdl_per_s <= 0.042140110093806q_W); -static_assert(1q_pdl_per_ft2>= 1.4881639435q_Pa && - 1q_pdl_per_ft2 <=1.4881639437q_Pa); +static_assert(1q_pdl > 0.138254q_N && + 1q_pdl < 0.138256q_N); +static_assert(1q_ft_pdl > 0.042140110093804q_J && + 1q_ft_pdl < 0.042140110093806q_J); +static_assert(1q_ft_pdl_per_s > 0.042140110093804q_W && + 1q_ft_pdl_per_s < 0.042140110093806q_W); +static_assert(1q_pdl_per_ft2 > 1.4881639435q_Pa && + 1q_pdl_per_ft2 < 1.4881639437q_Pa); } diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index a1b339f0..5a3cea9e 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -106,6 +106,7 @@ static_assert(10q_Hz * 1q_min == 600); static_assert(2 / 1q_Hz == 2q_s); // force + 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_kg == 100q_m_per_s2); @@ -237,8 +238,6 @@ static_assert(kilogray::symbol == "kGy"); // speed -static_assert(is_same_v, std::int64_t>>); - static_assert(10q_m / 5q_s == 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); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index a48a1e86..9412d636 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "units/unit.h" +#include "units/bits/equivalent.h" #include "units/physical/si/prefixes.h" namespace { @@ -28,6 +29,9 @@ namespace { using namespace units; using namespace units::physical; +template +inline constexpr bool compare = DOWNCAST_MODE != 0 ? std::is_same_v : (std::is_same_v || units::equivalent); + struct metre : named_unit {}; struct centimetre : prefixed_unit {}; struct kilometre : prefixed_unit {}; @@ -46,11 +50,11 @@ struct metre_per_second : unit {}; struct dim_speed : derived_dimension, units::exp> {}; struct kilometre_per_hour : deduced_unit {}; -static_assert(is_same_v>, metre>); -static_assert(is_same_v>, centimetre>); -static_assert(is_same_v>, yard>); -static_assert(is_same_v>, foot>); -static_assert(is_same_v>, kilometre_per_hour>); +static_assert(compare>, metre>); +static_assert(compare>, centimetre>); +static_assert(compare>, yard>); +static_assert(compare>, foot>); +static_assert(compare>, kilometre_per_hour>); static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km");