diff --git a/conanfile.py b/conanfile.py index c8c578b8..8d1fe106 100644 --- a/conanfile.py +++ b/conanfile.py @@ -48,6 +48,12 @@ class UnitsConan(ConanFile): "fmt/7.0.3", "ms-gsl/3.1.0" ) + options = { + "downcast": ["off", "on", "auto"] + } + default_options = { + "downcast": "auto" + } exports = ["LICENSE.md"] exports_sources = ["docs/*", "src/*", "test/*", "cmake/*", "example/*","CMakeLists.txt"] # scm = { @@ -77,6 +83,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 a777898c..91c8dacc 100644 --- a/docs/design/quantity.rst +++ b/docs/design/quantity.rst @@ -23,12 +23,7 @@ a few additional member types and functions:: }; template - requires detail::basic_arithmetic && equivalent_dim> - [[nodiscard]] constexpr ScalableNumber 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 +33,12 @@ a few additional member types and functions:: const quantity& q); template - requires detail::basic_arithmetic && equivalent_dim - [[nodiscard]] constexpr ScalableNumber 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 cfd9cb31..6c5a46b3 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 fd73b03a..c73a2c21 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 62a6ee39..461314cf 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(mp-units $ $ ) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_link_libraries(mp-units INTERFACE @@ -80,6 +84,17 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") ) endif() +if(DOWNCAST_MODE STREQUAL "AUTO") + message(STATUS "Configuring DOWNCAST_MODE=AUTOMATIC") + target_compile_definitions(mp-units INTERFACE DOWNCAST_MODE=2) +elseif(DOWNCAST_MODE) + message(STATUS "Configuring DOWNCAST_MODE=ON") + target_compile_definitions(mp-units INTERFACE DOWNCAST_MODE=1) +else() + message(STATUS "Configuring DOWNCAST_MODE=OFF") + target_compile_definitions(mp-units INTERFACE DOWNCAST_MODE=0) +endif() + add_library(mp-units::mp-units ALIAS mp-units) # installation info diff --git a/src/include/units/bits/common_quantity.h b/src/include/units/bits/common_quantity.h index 803dfc7c..570d26d3 100644 --- a/src/include/units/bits/common_quantity.h +++ b/src/include/units/bits/common_quantity.h @@ -23,6 +23,8 @@ #pragma once #include +#include +#include namespace units { @@ -55,9 +57,12 @@ struct common_quantity_impl, quantity, Rep> template struct common_quantity_impl, quantity, Rep> { + using dimension = conditional, D2, D1>; static constexpr ratio r1 = D1::base_units_ratio * U1::ratio; static constexpr ratio r2 = D2::base_units_ratio * U2::ratio; - using type = quantity, Rep>; + static constexpr ratio cr = common_ratio(r1, r2); + using unit = downcast_unit; + using type = quantity; }; template @@ -66,7 +71,7 @@ quantity_point common_quantity_point_impl(quantity); } // namespace detail template> - requires equivalent_dim + requires equivalent using common_quantity = TYPENAME detail::common_quantity_impl::type; template @@ -79,7 +84,7 @@ using common_quantity_point = decltype( namespace std { 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 541d9300..608d00f1 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, exponent> : 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 * diff --git a/src/include/units/bits/equivalent.h b/src/include/units/bits/equivalent.h new file mode 100644 index 00000000..2164da9f --- /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, exponent> : 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 6e269dea..a5cb1fa4 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 7f690b13..3b432ce9 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -78,7 +78,7 @@ using make_dimension = TYPENAME to_derived_dimension_base -struct derived_dimension : downcast_child> { +struct derived_dimension : downcast_dispatch> { using recipe = exponent_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 129be0b3..36740e08 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -71,7 +71,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 437299f6..0d3c53c2 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -76,7 +76,7 @@ public: constexpr explicit(!(std::is_same_v && std::is_same_v)) quantity(const Value& v) : value_{static_cast(v)} {} template - requires equivalent_dim && + requires equivalent && detail::safe_convertible && detail::safe_divisible constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} {} @@ -314,7 +314,7 @@ public: } template - requires equivalent_dim && + requires equivalent && std::three_way_comparable_with [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) { @@ -323,7 +323,7 @@ public: } template - requires equivalent_dim && + requires equivalent && std::equality_comparable_with [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) { diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index 3f66b91c..6f05649b 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -59,7 +59,7 @@ constexpr auto quantity_ratio(const quantity&) // QuantityOf template -concept QuantityOf = Quantity && Dimension && equivalent_dim; +concept QuantityOf = Quantity && Dimension && equivalent; // quantity_cast namespace detail { @@ -337,7 +337,7 @@ template * @tparam ToD a dimension type to use for a target quantity */ template - requires equivalent_dim + requires equivalent [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { return quantity_cast, Rep>>(q); diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 14aa5649..d4d5fe3c 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -74,7 +74,7 @@ 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; }; @@ -92,7 +92,7 @@ struct unit : downcast_child> { * @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; @@ -114,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; @@ -133,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; @@ -155,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; @@ -179,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; @@ -203,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; diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 85348405..1215bb83 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((10_q_cm / 5_q_s).count() == 2); +static_assert((2_q_cm_per_s).count() == 2); static_assert(10_q_cm / 5_q_s == 2_q_cm_per_s); static_assert(10_q_cm / 2_q_cm_per_s == 5_q_s); static_assert(10_q_cm == 2_q_cm_per_s * 5_q_s); @@ -59,7 +61,11 @@ static_assert(detail::unit_text() == "cm/s"); // area static_assert(centimetre::ratio / dimension_unit::ratio == ratio(1)); +static_assert((1_q_cm * 1_q_cm).count() == 1); +static_assert((1_q_cm2).count() == 1); static_assert(1_q_cm * 1_q_cm == 1_q_cm2); +static_assert(100_q_cm * 100_q_cm == area(1)); +static_assert(100_q_cm * 100_q_cm == length(1) * length(1)); static_assert(100_q_cm2 / 10_q_cm == 10_q_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 ef902c17..2dbe60df 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::exponent> {}; struct kilogram_per_hour : deduced_unit {}; constexpr auto a = 1_q_kg / 1_q_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 64322d94..6efd92d4 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(2_q_m)), std::int64_t>); - static_assert(is_same_v(2_q_m)), decltype(2_q_m)>); - static_assert(is_same_v(2_q_m)), decltype(4_q_m2)>); - static_assert(is_same_v(2_q_km)), decltype(4_q_km2)>); - static_assert(is_same_v(2_q_ft)), decltype(4_q_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(2_q_m)), std::int64_t>); +static_assert(compare(2_q_m)), decltype(2_q_m)>); +static_assert(compare(2_q_m)), decltype(4_q_m2)>); +static_assert(compare(2_q_km)), decltype(4_q_km2)>); +static_assert(compare(2_q_ft)), decltype(4_q_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 e3d501ed..c7b73f1e 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(2_q_m) -= 1_q_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((1_q_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>); @@ -208,8 +211,7 @@ static_assert(std::equality_comparable_with>(quantity_point(2_q_km)))::unit, metre>); +static_assert(compare>(quantity_point(2_q_km)))::unit, metre>); static_assert(quantity_point_cast>(quantity_point(2_q_km)).relative().count() == 2000); diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 882cc91e..14e3557e 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 template @@ -147,37 +150,35 @@ static_assert(invalid_compound_assignments); // 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::exponent>, 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()), dimensionless>); -static_assert(is_same_v() / length()), dimensionless, double>>); +static_assert(compare() / 1.0), length>); +static_assert(compare() / length()), dimensionless>); +static_assert(compare() / length()), dimensionless, double>>); static_assert( - is_same_v() / physical::si::time()), speed>); + compare() / physical::si::time()), speed>); static_assert( - is_same_v() / physical::si::time()), speed>>); -static_assert(is_same_v() / length()), + compare() / physical::si::time()), speed>>); +static_assert(compare() / length()), quantity, units::exponent>, scaled_unit>>); -static_assert(is_same_v() % short(1)), length>); -static_assert(is_same_v() % length(1)), length>); +static_assert(compare() % short(1)), length>); +static_assert(compare() % length(1)), length>); static_assert((1_q_m + km).count() == 1001); static_assert((1_q_m + 1_q_km).count() == 1001); @@ -251,11 +252,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 @@ -272,7 +272,7 @@ static_assert(!std::equality_comparable_with, double>); // quantity_cast -static_assert(is_same_v>(2_q_km))::unit, metre>); +static_assert(compare>(2_q_km))::unit, metre>); static_assert(quantity_cast>(2_q_km).count() == 2000); static_assert(quantity_cast>(2000_q_m).count() == 2); @@ -339,6 +339,20 @@ static_assert(1_q_km / 1_q_s == 1000_q_m_per_s); static_assert(2_q_km_per_h * 2_q_h == 4_q_km); static_assert(2_q_km / 2_q_km_per_h == 1_q_h); -static_assert(is_same_v(2_q_m)), decltype(4_q_m2)>); +static_assert(compare(2_q_m)), decltype(4_q_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 120d0c99..f236eff9 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_test.cpp b/test/unit_test/static/si_test.cpp index be55ece9..0d0fb1c5 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -241,8 +241,6 @@ static_assert(kilogray::symbol == "kGy"); // speed -static_assert(is_same_v, std::int64_t>>); - static_assert(10_q_m / 5_q_s == 2_q_m_per_s); static_assert(10 / 5_q_s * 1_q_m == 2_q_m_per_s); static_assert(1_q_km / 1_q_s == 1000_q_m_per_s); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 43d1d473..66317a74 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 {}; @@ -49,11 +53,11 @@ struct metre_per_second : unit {}; struct dim_speed : derived_dimension, units::exponent> {}; 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>); #if COMP_GCC >= 10 static_assert([]() { return !requires { typename scaled_unit; }; }.template operator()()); // negative unit ratio