From daf97a3a8608207cf41a3b24a7815f5bbef7180f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 8 Sep 2022 19:11:45 +0200 Subject: [PATCH] feat: initial very dirty version of V2 framework --- CMakeLists.txt | 9 +- example/CMakeLists.txt | 26 +- example/hello_units.cpp | 416 +++++++++++++++-- example/v2_framework.cpp | 418 ++++++++++++++++++ src/core/CMakeLists.txt | 5 +- src/core/include/units/base_dimension.h | 66 --- .../units/bits/derived_dimension_base.h | 60 --- .../include/units/bits/derived_scaled_unit.h | 48 -- src/core/include/units/bits/dim_consolidate.h | 67 --- src/core/include/units/bits/dim_unpack.h | 59 --- src/core/include/units/bits/dimension_op.h | 208 --------- .../include/units/bits/expression_template.h | 390 ++++++++++++++++ .../include/units/bits/external/type_list.h | 50 +++ .../include/units/bits/external/type_name.h | 27 ++ src/core/include/units/derived_dimension.h | 92 ---- src/core/include/units/dimension.h | 260 +++++++++++ src/core/include/units/exponent.h | 85 ---- src/core/include/units/magnitude.h | 2 +- src/core/include/units/prefix.h | 62 --- src/core/include/units/reference.h | 139 +++--- src/core/include/units/unit.h | 272 +++++++----- .../si/include/units/isq/si/prefixes.h | 64 ++- 22 files changed, 1829 insertions(+), 996 deletions(-) create mode 100644 example/v2_framework.cpp delete mode 100644 src/core/include/units/base_dimension.h delete mode 100644 src/core/include/units/bits/derived_dimension_base.h delete mode 100644 src/core/include/units/bits/derived_scaled_unit.h delete mode 100644 src/core/include/units/bits/dim_consolidate.h delete mode 100644 src/core/include/units/bits/dim_unpack.h delete mode 100644 src/core/include/units/bits/dimension_op.h create mode 100644 src/core/include/units/bits/expression_template.h create mode 100644 src/core/include/units/bits/external/type_name.h delete mode 100644 src/core/include/units/derived_dimension.h create mode 100644 src/core/include/units/dimension.h delete mode 100644 src/core/include/units/exponent.h delete mode 100644 src/core/include/units/prefix.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f0ca1045..29e11b68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ add_compile_definitions($<$:gsl_CONFIG_CONTRACT_CHECKING_AUDIT>) # enable include-what-you-use option(${projectPrefix}IWYU "Enables include-what-you-use" OFF) + if(${projectPrefix}IWYU) include(include-what-you-use) enable_iwyu( @@ -52,12 +53,13 @@ if(${projectPrefix}IWYU) MAX_LINE_LENGTH 120 NO_COMMENTS ) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(${projectPrefix}AS_SYSTEM_HEADERS ON) endif() endif() -#enable_clang_tidy() +# enable_clang_tidy() # add project code add_subdirectory(src) @@ -66,11 +68,12 @@ add_subdirectory(src) add_subdirectory(example) # generate project documentation -add_subdirectory(docs) +# add_subdirectory(docs) # add unit tests enable_testing() -add_subdirectory(test) + +# add_subdirectory(test) # tests for standalone headers include(TestPublicHeaders) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 6733348b..686b57ac 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -33,17 +33,19 @@ function(add_example target) target_link_libraries(${target} PRIVATE ${ARGN}) endfunction() -add_example(conversion_factor mp-units::core-fmt mp-units::core-io mp-units::si) -add_example(custom_systems mp-units::core-io mp-units::si) -add_example(hello_units mp-units::core-fmt mp-units::core-io mp-units::si mp-units::si-international) -add_example(measurement mp-units::core-io mp-units::si) -add_example(si_constants mp-units::core-fmt mp-units::si) +add_example(v2_framework mp-units::core-fmt mp-units::core-io mp-units::si) -if(NOT ${projectPrefix}LIBCXX) - add_subdirectory(glide_computer) -endif() +# add_example(conversion_factor mp-units::core-fmt mp-units::core-io mp-units::si) +# add_example(custom_systems mp-units::core-io mp-units::si) +# add_example(hello_units mp-units::core-fmt mp-units::core-io mp-units::si mp-units::si-international) +# add_example(measurement mp-units::core-io mp-units::si) +# add_example(si_constants mp-units::core-fmt mp-units::si) -add_subdirectory(aliases) -add_subdirectory(kalman_filter) -add_subdirectory(literals) -add_subdirectory(references) +# if(NOT ${projectPrefix}LIBCXX) +# add_subdirectory(glide_computer) +# endif() + +# add_subdirectory(aliases) +# add_subdirectory(kalman_filter) +# add_subdirectory(literals) +# add_subdirectory(references) diff --git a/example/hello_units.cpp b/example/hello_units.cpp index 5bfbc860..d9620410 100644 --- a/example/hello_units.cpp +++ b/example/hello_units.cpp @@ -20,43 +20,391 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include // IWYU pragma: keep -#include -#include // IWYU pragma: keep -#include -#include -#include +// #include +#include -using namespace units::isq; +namespace units::isq { -constexpr Speed auto avg_speed(Length auto d, Time auto t) { return d / t; } +inline constexpr struct dim_length : base_dimension<"L"> { +} dim_length; + +// template +// concept Length = QuantityOf; + +inline constexpr struct dim_time : base_dimension<"T"> { +} dim_time; +inline constexpr struct dim_frequency : decltype(1 / dim_time) { +} dim_frequency; +inline constexpr struct dim_area : decltype(dim_length * dim_length) { +} dim_area; +inline constexpr struct dim_volume : decltype(dim_area * dim_length) { +} dim_volume; +inline constexpr struct dim_speed : decltype(dim_length / dim_time) { +} dim_speed; +inline constexpr struct dim_acceleration : decltype(dim_speed / dim_time) { +} dim_acceleration; + +// inline constexpr auto speed = length / time; + +} // namespace units::isq + +#include +#include + +namespace units { + +namespace isq::si { + +// length units +inline constexpr struct metre : named_unit<"m"> { +} metre; +inline constexpr struct kilometre : kilo { +} kilometre; + +inline constexpr struct astronomical_unit : named_scaled_unit<"au", mag<149'597'870'700>(), metre> { +} astronomical_unit; + +// area units +inline constexpr struct square_metre : derived_unit { +} square_metre; + +// volume units +inline constexpr struct cubic_metre : derived_unit { +} cubic_metre; + +// time units +inline constexpr struct second : named_unit<"s"> { +} second; +inline constexpr struct minute : named_scaled_unit<"min", mag<60>(), second> { +} minute; +inline constexpr struct hour : named_scaled_unit<"h", mag<60>(), minute> { +} hour; +inline constexpr struct day : named_scaled_unit<"d", mag<24>(), hour> { +} day; + +// not a time unit! +inline constexpr struct second_squared : derived_unit { +} second_squared; + +// mass units +inline constexpr struct gram : named_unit<"g"> { +} gram; +inline constexpr struct kilogram : kilo { +} kilogram; +inline constexpr struct tonne : named_scaled_unit<"t", mag<1000>(), gram> { +} tonne; + +// other units +inline constexpr struct hertz : named_unit<"Hz", 1 / second> { +} hertz; +inline constexpr struct newton : named_unit<"N", kilogram * metre / second_squared> { +} newton; +inline constexpr struct pascal : named_unit<"Pa", kilogram / (metre * second_squared)> { +} pascal; +inline constexpr struct joule : named_unit<"J", newton * metre> { +} joule; +inline constexpr struct watt : named_unit<"W", joule / second> { +} watt; + +namespace short_units { + +// TODO collide with reference names +// inline namespace length { + +inline constexpr auto m = metre; +inline constexpr auto km = kilometre; +inline constexpr auto au = astronomical_unit; + +// } // namespace length + +// inline namespace area { + +inline constexpr auto m2 = square_metre; + +// } + +// inline namespace volume { + +inline constexpr auto m3 = cubic_metre; + +// } + +// inline namespace time { + +inline constexpr auto s = second; +inline constexpr auto min = minute; +inline constexpr auto h = hour; +inline constexpr auto d = day; + +inline constexpr auto s2 = second_squared; + +// } // namespace time + +// inline namespace mass { + +inline constexpr auto g = gram; +inline constexpr auto kg = kilogram; +inline constexpr auto t = tonne; + +// } // namespace mass + +// inline namespace frequency { + +inline constexpr auto Hz = hertz; + +// } + +// inline namespace force { + +inline constexpr auto N = newton; + +// } + +// inline namespace pressure { + +inline constexpr auto Pa = pascal; + +// } + +// inline namespace energy { + +inline constexpr auto J = joule; + +// } + +// inline namespace power { + +inline constexpr auto W = watt; + +// } + +} // namespace short_units + +} // namespace isq::si +} // namespace units + +#include + +namespace units::isq::si { + +inline constexpr struct length : system_reference { +} length; +inline constexpr struct time : system_reference { +} time; +inline constexpr struct frequency : system_reference { +} frequency; +inline constexpr struct area : system_reference { +} area; +inline constexpr struct volume : system_reference { +} volume; +inline constexpr struct speed : system_reference { +} speed; +inline constexpr struct acceleration : system_reference { +} acceleration; + +} // namespace units::isq::si + + +template +inline constexpr bool is_of_type = std::is_same_v, T>; + +namespace units::isq { + +// derived dimension expression template syntax verification +static_assert(is_of_type<1 / dim_time, derived_dimension>>); +static_assert(is_of_type<1 / (1 / dim_time), struct dim_time>); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(is_of_type>>); +static_assert(is_of_type<1 / dim_time * dim_one, derived_dimension>>); + +static_assert(is_of_type>); +static_assert(is_of_type>>); + +static_assert( + is_of_type, struct dim_time>>); +static_assert( + is_of_type, struct dim_time>>); + +static_assert( + is_of_type, struct dim_time>>); +static_assert( + is_of_type, struct dim_time>>); + +static_assert(is_of_type<1 / dim_time * dim_length, derived_dimension>>); +static_assert(is_of_type<1 / dim_time * dim_time, struct dim_one>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / dim_time / dim_one, derived_dimension>>); + +static_assert(is_of_type); +static_assert( + is_of_type<1 / dim_time * (1 / dim_time), derived_dimension>>>); +static_assert(is_of_type<1 / (dim_time * dim_time), derived_dimension>>>); +static_assert(is_of_type<1 / (1 / (dim_time * dim_time)), derived_dimension>>); + +static_assert(is_of_type>>>); +static_assert(is_of_type, per>>>); +static_assert(is_of_type); + +static_assert( + is_of_type>>); +static_assert( + is_of_type>>); +static_assert(is_of_type, per>>); +static_assert(is_of_type<1 / (dim_speed * dim_speed) * dim_length, + derived_dimension>>>); + +// comparisons of equivalent dimensions +static_assert(dim_length / dim_length == dim_one); + +static_assert(1 / dim_time == dim_frequency); +static_assert(1 / dim_frequency == dim_time); +static_assert(dim_frequency * dim_time == dim_one); + +static_assert(dim_length * dim_length == dim_area); +static_assert(dim_length * dim_length != dim_volume); +static_assert(dim_area / dim_length == dim_length); + +static_assert(dim_length * dim_length * dim_length == dim_volume); +static_assert(dim_area * dim_length == dim_volume); +static_assert(dim_volume / dim_length == dim_area); +static_assert(dim_volume / dim_length / dim_length == dim_length); +static_assert(dim_area * dim_area / dim_length == dim_volume); +static_assert(dim_area * (dim_area / dim_length) == dim_volume); +static_assert(dim_volume / (dim_length * dim_length) == dim_length); + +static_assert(dim_length / dim_time == dim_speed); +static_assert(dim_length * dim_time != dim_speed); +static_assert(dim_length / dim_time / dim_time != dim_speed); +static_assert(dim_length / dim_speed == dim_time); +static_assert(dim_speed * dim_time == dim_length); + +static_assert(dim_length / dim_time / dim_time == dim_acceleration); +static_assert(dim_length / (dim_time * dim_time) == dim_acceleration); +static_assert(dim_speed / dim_time == dim_acceleration); +static_assert(dim_speed / dim_acceleration == dim_time); +static_assert(dim_acceleration * dim_time == dim_speed); +static_assert(dim_acceleration * (dim_time * dim_time) == dim_length); +static_assert(dim_acceleration / dim_speed == dim_frequency); + +} // namespace units::isq + + +namespace units::isq::si { + +// derived unit expression template syntax verification +static_assert(is_of_type<1 / second, derived_unit>>); +static_assert(is_of_type<1 / (1 / second), struct second>); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(is_of_type>>); +static_assert(is_of_type<1 / second * one, derived_unit>>); + +static_assert(is_of_type>); +static_assert(is_of_type>>); + +static_assert(is_of_type, struct second>>); +static_assert(is_of_type, struct second>>); + +static_assert(is_of_type, struct second>>); +static_assert(is_of_type, struct second>>); + +static_assert(is_of_type<1 / second * metre, derived_unit>>); +static_assert(is_of_type<1 / second * second, struct one>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / second / one, derived_unit>>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / second * (1 / second), derived_unit>>>); +static_assert(is_of_type<1 / (second * second), derived_unit>>>); +static_assert(is_of_type<1 / (1 / (second * second)), derived_unit>>); + +static_assert(is_of_type>>>); +static_assert( + is_of_type, per>>>); +static_assert(is_of_type); + +static_assert(is_of_type>>); +static_assert(is_of_type>>); + +// comparisons of equivalent dimensions +// static_assert(metre / metre == one); + +// static_assert(1 / second == dim_frequency); +// static_assert(1 / dim_frequency == second); +// static_assert(dim_frequency * second == one); + +// static_assert(metre * metre == dim_area); +// static_assert(metre * metre != dim_volume); +// static_assert(dim_area / metre == metre); + +// static_assert(metre * metre * metre == dim_volume); +// static_assert(dim_area * metre == dim_volume); +// static_assert(dim_volume / metre == dim_area); +// static_assert(dim_volume / metre / metre == metre); +// static_assert(dim_area * dim_area / metre == dim_volume); +// static_assert(dim_area * (dim_area / metre) == dim_volume); +// static_assert(dim_volume / (metre * metre) == metre); + +// static_assert(metre / second == dim_speed); +// static_assert(metre * second != dim_speed); +// static_assert(metre / second / second != dim_speed); +// static_assert(metre / dim_speed == second); +// static_assert(dim_speed * second == metre); + +// static_assert(metre / second / second == dim_acceleration); +// static_assert(metre / (second * second) == dim_acceleration); +// static_assert(dim_speed / second == dim_acceleration); +// static_assert(dim_speed / dim_acceleration == second); +// static_assert(dim_acceleration * second == dim_speed); +// static_assert(dim_acceleration * (second * second) == metre); +// static_assert(dim_acceleration / dim_speed == dim_frequency); + + +// Bq + Hz should not compile + +// Bq + Hz + 1/s should compile? + + +} // namespace units::isq::si + + +using namespace units; +using namespace units::isq::si; +using namespace units::isq::si::short_units; + +/* Frequency */ auto freq1 = 20 * frequency[Hz]; +// /* Frequency */ auto freq2 = 20 / (1 * time[s]); +quantity freq3(20); +// quantity freq4(20); +// quantity<1 / time[s]> freq5(20); + + +/* Speed */ auto speed1 = 20 * speed[m / s]; +/* Speed */ auto speed2 = 20 * (length[m] / isq::si::time[s]); +// quantity speed3(20); +// quantity speed4(20); + + +// quantity / second> speed1(123); +// quantity speed2(123); +// auto speed3 = 123 * (kilo / second); +// auto speed4 = 123 * (km / s); + +template +void print(); + +// constexpr auto avg_speed(quantity d, quantity t) { return d / t; } int main() { - using namespace units::isq::si::literals; - using namespace units::isq::si::references; - using namespace units::aliases::isq::si::international; - - constexpr Speed auto v1 = 110 * (km / h); - constexpr Speed auto v2 = mi_per_h<>(70.); - constexpr Speed auto v3 = avg_speed(220_q_km, 2_q_h); - constexpr Speed auto v4 = avg_speed(si::length(140), si::time(2)); -#if UNITS_DOWNCAST_MODE == 0 - constexpr Speed auto v5 = quantity_cast>(v3); - constexpr Speed auto v6 = quantity_cast(v4); -#else - constexpr Speed auto v5 = quantity_cast>(v3); - constexpr Speed auto v6 = quantity_cast(v4); -#endif - constexpr Speed auto v7 = quantity_cast(v6); - - std::cout << v1 << '\n'; // 110 km/h - std::cout << v2 << '\n'; // 70 mi/h - std::cout << STD_FMT::format("{}", v3) << '\n'; // 110 km/h - std::cout << STD_FMT::format("{:*^14}", v4) << '\n'; // ***70 mi/h**** - std::cout << STD_FMT::format("{:%Q in %q}", v5) << '\n'; // 30.5556 in m/s - std::cout << STD_FMT::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s - std::cout << STD_FMT::format("{:%Q}", v7) << '\n'; // 31 + print(); + print(); } diff --git a/example/v2_framework.cpp b/example/v2_framework.cpp new file mode 100644 index 00000000..9fbf1f4c --- /dev/null +++ b/example/v2_framework.cpp @@ -0,0 +1,418 @@ +// 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. + +// #include +#include + +namespace units::isq { + +inline constexpr struct dim_length : base_dimension<"L"> { +} dim_length; + +// template +// concept Length = QuantityOf; + +inline constexpr struct dim_time : base_dimension<"T"> { +} dim_time; +inline constexpr struct dim_frequency : decltype(1 / dim_time) { +} dim_frequency; +inline constexpr struct dim_area : decltype(dim_length * dim_length) { +} dim_area; +inline constexpr struct dim_volume : decltype(dim_area * dim_length) { +} dim_volume; +inline constexpr struct dim_speed : decltype(dim_length / dim_time) { +} dim_speed; +inline constexpr struct dim_acceleration : decltype(dim_speed / dim_time) { +} dim_acceleration; + +// inline constexpr auto speed = length / time; + +} // namespace units::isq + +#include +#include + +namespace units { + +namespace isq::si { + +// length units +inline constexpr struct metre : named_unit<"m"> { +} metre; +inline constexpr struct kilometre : kilo { +} kilometre; + +inline constexpr struct astronomical_unit : named_scaled_unit<"au", mag<149'597'870'700>(), metre> { +} astronomical_unit; + +// area units +inline constexpr struct square_metre : derived_unit { +} square_metre; + +// volume units +inline constexpr struct cubic_metre : derived_unit { +} cubic_metre; + +// time units +inline constexpr struct second : named_unit<"s"> { +} second; +inline constexpr struct minute : named_scaled_unit<"min", mag<60>(), second> { +} minute; +inline constexpr struct hour : named_scaled_unit<"h", mag<60>(), minute> { +} hour; +inline constexpr struct day : named_scaled_unit<"d", mag<24>(), hour> { +} day; + +// not a time unit! +inline constexpr struct second_squared : derived_unit { +} second_squared; + +// mass units +inline constexpr struct gram : named_unit<"g"> { +} gram; +inline constexpr struct kilogram : kilo { +} kilogram; +inline constexpr struct tonne : named_scaled_unit<"t", mag<1000>(), gram> { +} tonne; + +// other units +inline constexpr struct hertz : named_unit<"Hz", 1 / second> { +} hertz; +inline constexpr struct newton : named_unit<"N", kilogram * metre / second_squared> { +} newton; +inline constexpr struct pascal : named_unit<"Pa", kilogram / (metre * second_squared)> { +} pascal; +inline constexpr struct joule : named_unit<"J", newton * metre> { +} joule; +inline constexpr struct watt : named_unit<"W", joule / second> { +} watt; + +namespace short_units { + +// TODO collide with reference names +// inline namespace length { + +inline constexpr auto m = metre; +inline constexpr auto km = kilometre; +inline constexpr auto au = astronomical_unit; + +// } // namespace length + +// inline namespace area { + +inline constexpr auto m2 = square_metre; + +// } + +// inline namespace volume { + +inline constexpr auto m3 = cubic_metre; + +// } + +// inline namespace time { + +inline constexpr auto s = second; +inline constexpr auto min = minute; +inline constexpr auto h = hour; +inline constexpr auto d = day; + +inline constexpr auto s2 = second_squared; + +// } // namespace time + +// inline namespace mass { + +inline constexpr auto g = gram; +inline constexpr auto kg = kilogram; +inline constexpr auto t = tonne; + +// } // namespace mass + +// inline namespace frequency { + +inline constexpr auto Hz = hertz; + +// } + +// inline namespace force { + +inline constexpr auto N = newton; + +// } + +// inline namespace pressure { + +inline constexpr auto Pa = pascal; + +// } + +// inline namespace energy { + +inline constexpr auto J = joule; + +// } + +// inline namespace power { + +inline constexpr auto W = watt; + +// } + +} // namespace short_units + +} // namespace isq::si +} // namespace units + +#include + +namespace units { + +inline constexpr struct dimensionless : system_reference { +} dimensionless; + +} // namespace units + +namespace units::isq::si { + +inline constexpr struct length : system_reference { +} length; +inline constexpr struct time : system_reference { +} time; +inline constexpr struct frequency : system_reference { +} frequency; +inline constexpr struct area : system_reference { +} area; +inline constexpr struct volume : system_reference { +} volume; +inline constexpr struct speed : system_reference { +} speed; +inline constexpr struct acceleration : system_reference { +} acceleration; + +} // namespace units::isq::si + + +template +inline constexpr bool is_of_type = std::is_same_v, T>; + +namespace units::isq { + +// derived dimension expression template syntax verification +static_assert(is_of_type<1 / dim_time, derived_dimension>>); +static_assert(is_of_type<1 / (1 / dim_time), struct dim_time>); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(is_of_type>>); +static_assert(is_of_type<1 / dim_time * dim_one, derived_dimension>>); + +static_assert(is_of_type>); +static_assert(is_of_type>>); + +static_assert( + is_of_type, struct dim_time>>); +static_assert( + is_of_type, struct dim_time>>); + +static_assert( + is_of_type, struct dim_time>>); +static_assert( + is_of_type, struct dim_time>>); + +static_assert(is_of_type<1 / dim_time * dim_length, derived_dimension>>); +static_assert(is_of_type<1 / dim_time * dim_time, struct dim_one>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / dim_time / dim_one, derived_dimension>>); + +static_assert(is_of_type); +static_assert( + is_of_type<1 / dim_time * (1 / dim_time), derived_dimension>>>); +static_assert(is_of_type<1 / (dim_time * dim_time), derived_dimension>>>); +static_assert(is_of_type<1 / (1 / (dim_time * dim_time)), derived_dimension>>); + +static_assert(is_of_type>>>); +static_assert(is_of_type, per>>>); +static_assert(is_of_type); + +static_assert( + is_of_type>>); +static_assert( + is_of_type>>); +static_assert(is_of_type, per>>); +static_assert(is_of_type<1 / (dim_speed * dim_speed) * dim_length, + derived_dimension>>>); + +// comparisons of equivalent dimensions +static_assert(dim_length / dim_length == dim_one); + +static_assert(1 / dim_time == dim_frequency); +static_assert(1 / dim_frequency == dim_time); +static_assert(dim_frequency * dim_time == dim_one); + +static_assert(dim_length * dim_length == dim_area); +static_assert(dim_length * dim_length != dim_volume); +static_assert(dim_area / dim_length == dim_length); + +static_assert(dim_length * dim_length * dim_length == dim_volume); +static_assert(dim_area * dim_length == dim_volume); +static_assert(dim_volume / dim_length == dim_area); +static_assert(dim_volume / dim_length / dim_length == dim_length); +static_assert(dim_area * dim_area / dim_length == dim_volume); +static_assert(dim_area * (dim_area / dim_length) == dim_volume); +static_assert(dim_volume / (dim_length * dim_length) == dim_length); + +static_assert(dim_length / dim_time == dim_speed); +static_assert(dim_length * dim_time != dim_speed); +static_assert(dim_length / dim_time / dim_time != dim_speed); +static_assert(dim_length / dim_speed == dim_time); +static_assert(dim_speed * dim_time == dim_length); + +static_assert(dim_length / dim_time / dim_time == dim_acceleration); +static_assert(dim_length / (dim_time * dim_time) == dim_acceleration); +static_assert(dim_speed / dim_time == dim_acceleration); +static_assert(dim_speed / dim_acceleration == dim_time); +static_assert(dim_acceleration * dim_time == dim_speed); +static_assert(dim_acceleration * (dim_time * dim_time) == dim_length); +static_assert(dim_acceleration / dim_speed == dim_frequency); + +} // namespace units::isq + + +namespace units::isq::si { + +// derived unit expression template syntax verification +static_assert(is_of_type<1 / second, derived_unit>>); +static_assert(is_of_type<1 / (1 / second), struct second>); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(is_of_type>>); +static_assert(is_of_type<1 / second * one, derived_unit>>); + +static_assert(is_of_type>); +static_assert(is_of_type>>); + +static_assert(is_of_type, struct second>>); +static_assert(is_of_type, struct second>>); + +static_assert(is_of_type, struct second>>); +static_assert(is_of_type, struct second>>); + +static_assert(is_of_type<1 / second * metre, derived_unit>>); +static_assert(is_of_type<1 / second * second, struct one>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / second / one, derived_unit>>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / second * (1 / second), derived_unit>>>); +static_assert(is_of_type<1 / (second * second), derived_unit>>>); +static_assert(is_of_type<1 / (1 / (second * second)), derived_unit>>); + +static_assert(is_of_type>>>); +static_assert( + is_of_type, per>>>); +static_assert(is_of_type); + +static_assert(is_of_type>>); +static_assert(is_of_type>>); + +// comparisons of equivalent dimensions +// static_assert(metre / metre == one); + +// static_assert(1 / second == dim_frequency); +// static_assert(1 / dim_frequency == second); +// static_assert(dim_frequency * second == one); + +// static_assert(metre * metre == dim_area); +// static_assert(metre * metre != dim_volume); +// static_assert(dim_area / metre == metre); + +// static_assert(metre * metre * metre == dim_volume); +// static_assert(dim_area * metre == dim_volume); +// static_assert(dim_volume / metre == dim_area); +// static_assert(dim_volume / metre / metre == metre); +// static_assert(dim_area * dim_area / metre == dim_volume); +// static_assert(dim_area * (dim_area / metre) == dim_volume); +// static_assert(dim_volume / (metre * metre) == metre); + +// static_assert(metre / second == dim_speed); +// static_assert(metre * second != dim_speed); +// static_assert(metre / second / second != dim_speed); +// static_assert(metre / dim_speed == second); +// static_assert(dim_speed * second == metre); + +// static_assert(metre / second / second == dim_acceleration); +// static_assert(metre / (second * second) == dim_acceleration); +// static_assert(dim_speed / second == dim_acceleration); +// static_assert(dim_speed / dim_acceleration == second); +// static_assert(dim_acceleration * second == dim_speed); +// static_assert(dim_acceleration * (second * second) == metre); +// static_assert(dim_acceleration / dim_speed == dim_frequency); + + +// Bq + Hz should not compile + +// Bq + Hz + 1/s should compile? + + +} // namespace units::isq::si + + +using namespace units; +using namespace units::isq::si; +using namespace units::isq::si::short_units; + +/* Frequency */ auto freq1 = 20 * frequency[Hz]; +// /* Frequency */ auto freq2 = 20 / (1 * isq::si::time[s]); +quantity freq3(20); +quantity freq4(20); +quantity freq5(20); + +/* Speed */ auto speed1 = 20 * speed[m / s]; +/* Speed */ auto speed2 = 20 * (length[m] / isq::si::time[s]); +quantity speed3(20); +quantity speed4(20); + +template +void print(); + +// constexpr auto avg_speed(quantity d, quantity t) { return d / t; } + +int main() +{ + print(); + // print(); + print(); + print(); + print(); + + print(); + print(); + print(); + print(); +} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 56f462af..e39c7bd9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -37,12 +37,10 @@ check_libcxx_in_use(${projectPrefix}LIBCXX) add_library( mp-units-core INTERFACE - include/units/base_dimension.h include/units/chrono.h include/units/concepts.h include/units/customization_points.h - include/units/derived_dimension.h - include/units/exponent.h + include/units/dimension.h include/units/generic/angle.h include/units/generic/dimensionless.h include/units/generic/solid_angle.h @@ -50,7 +48,6 @@ add_library( include/units/magnitude.h include/units/math.h include/units/point_origin.h - include/units/prefix.h include/units/quantity.h include/units/quantity_cast.h include/units/quantity_kind.h diff --git a/src/core/include/units/base_dimension.h b/src/core/include/units/base_dimension.h deleted file mode 100644 index 0af40adb..00000000 --- a/src/core/include/units/base_dimension.h +++ /dev/null @@ -1,66 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -// IWYU pragma: begin_exports -#include -#include -// IWYU pragma: end_exports - -#include - -namespace units { - -/** - * @brief A dimension of a base quantity - * - * Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity - * in the subset can be expressed in terms of the other quantities within that subset. They are referred to as - * being mutually independent since a base quantity cannot be expressed as a product of powers of the other base - * quantities. - * - * Base unit is a measurement unit that is adopted by convention for a base quantity in a specific system of units. - * - * Pair of Symbol and Unit template parameters form an unique identifier of the base dimension. The same identifiers can - * be multiplied and divided which will result with an adjustment of its factor in an Exponent of a DerivedDimension - * (in case of zero the dimension will be simplified and removed from further analysis of current expresion). In case - * the Symbol is the same but the Unit differs (i.e. mixing SI and CGS length), there is no automatic simplification but - * is possible to force it with a quantity_cast. - * - * @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support - * @tparam U a base unit to be used for this base dimension - */ -template -struct base_dimension { - static constexpr auto symbol = Symbol; ///< Unique base dimension identifier - using base_unit = U; ///< Base unit adopted for this dimension - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = magnitude{}; -}; - -// base_dimension_less -template -struct base_dimension_less : - std::bool_constant<(D1::symbol < D2::symbol) || - (D1::symbol == D2::symbol && D1::base_unit::symbol < D1::base_unit::symbol)> {}; - -} // namespace units diff --git a/src/core/include/units/bits/derived_dimension_base.h b/src/core/include/units/bits/derived_dimension_base.h deleted file mode 100644 index 9284eb0e..00000000 --- a/src/core/include/units/bits/derived_dimension_base.h +++ /dev/null @@ -1,60 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include - -namespace units::detail { - -/** - * @brief A dimension of a derived quantity - * - * Expression of the dependence of a quantity on the base quantities (and their base dimensions) of a system of - * quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factors. - * A power of a factor is the factor raised to an exponent. - * - * A derived dimension can be formed from multiple exponents (i.e. speed is represented as "exponent, exponent"). It is also possible to form a derived dimension with only one exponent (i.e. frequency is represented as just - * "exponent"). - * - * @note This class template is used by the library engine and should not be directly instantiated by the user. - * - * @tparam Es zero or more exponents of a derived dimension - */ -template - requires(BaseDimension && ...) -struct derived_dimension_base : downcast_base> { - using exponents = exponent_list; -}; - -template -struct to_derived_dimension_base; - -template -struct to_derived_dimension_base> { - using type = derived_dimension_base; -}; - -} // namespace units::detail diff --git a/src/core/include/units/bits/derived_scaled_unit.h b/src/core/include/units/bits/derived_scaled_unit.h deleted file mode 100644 index d62b6f45..00000000 --- a/src/core/include/units/bits/derived_scaled_unit.h +++ /dev/null @@ -1,48 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include - -namespace units::detail { - -// compatible_units -template -inline constexpr bool compatible_units = false; - -template -inline constexpr bool compatible_units, Us...> = (UnitOf && ...); - -// derived_scaled_unit - -template -constexpr Magnitude auto derived_mag(exponent_list) -{ - return (magnitude<>{} * ... * pow(Us::mag / dimension_unit::mag)); -} - -template -using derived_scaled_unit = scaled_unit(typename D::recipe()), typename D::coherent_unit::reference>; - -} // namespace units::detail diff --git a/src/core/include/units/bits/dim_consolidate.h b/src/core/include/units/bits/dim_consolidate.h deleted file mode 100644 index 2590b460..00000000 --- a/src/core/include/units/bits/dim_consolidate.h +++ /dev/null @@ -1,67 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include - -namespace units::detail { - -/** - * @brief Consolidates contiguous ranges of exponents of the same dimension - * - * If there is more than one exponent with the same dimension they are aggregated into one exponent by adding - * their exponents. If this accumulation will result with 0, such a dimension is removed from the list. - * - * @tparam D derived dimension to consolidate - */ -template -struct dim_consolidate; - -template<> -struct dim_consolidate> { - using type = exponent_list<>; -}; - -template -struct dim_consolidate> { - using type = exponent_list; -}; - -template -struct dim_consolidate> { - using type = type_list_push_front>::type, E1>; -}; - -template -struct dim_consolidate, exponent, ERest...>> { - using r1 = std::ratio; - using r2 = std::ratio; - using r = std::ratio_add; - using type = conditional>::type, - typename dim_consolidate, ERest...>>::type>; -}; - -} // namespace units::detail diff --git a/src/core/include/units/bits/dim_unpack.h b/src/core/include/units/bits/dim_unpack.h deleted file mode 100644 index ae018f2a..00000000 --- a/src/core/include/units/bits/dim_unpack.h +++ /dev/null @@ -1,59 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include - -namespace units::detail { - -/** - * @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions - * - * @tparam Es Exponents of potentially derived dimensions - */ -template -struct dim_unpack; - -template<> -struct dim_unpack<> { - using type = exponent_list<>; -}; - -template -struct dim_unpack, ERest...> { - using type = type_list_push_front::type, exponent>; -}; - -template -struct dim_unpack, ERest...> { - using type = TYPENAME dim_unpack, Num, Den>, ERest...>::type; -}; - -template -struct dim_unpack, Num, Den>, ERest...> { - using type = type_list_push_front::type, exponent_multiply...>; -}; - -} // namespace units::detail diff --git a/src/core/include/units/bits/dimension_op.h b/src/core/include/units/bits/dimension_op.h deleted file mode 100644 index 820fbc82..00000000 --- a/src/core/include/units/bits/dimension_op.h +++ /dev/null @@ -1,208 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace units { - -/** - * @brief Unknown dimension - * - * Sometimes a temporary partial result of a complex calculation may not result in a predefined - * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit` - * with a magnitude being the absolute one of all the exponents of such a dimension. - * - * @tparam Es the list of exponents of ingredient dimensions - */ -template -struct unknown_dimension : derived_dimension, unknown_coherent_unit, Es...> {}; - -namespace detail { - -template -struct check_unknown { - using type = D; -}; - -// downcast did not find a user predefined type -template -struct check_unknown> { - using type = unknown_dimension; -}; - -template -struct downcast_dimension_impl; - -template -struct downcast_dimension_impl { - using type = D; -}; - -template -struct downcast_dimension_impl { - using type = TYPENAME check_unknown>::type; -}; - -} // namespace detail - -template -using downcast_dimension = TYPENAME detail::downcast_dimension_impl::type; - -// dim_invert -namespace detail { - -template -struct dim_invert_impl; - -template -struct dim_invert_impl { - using type = downcast_dimension>>; -}; - -template -struct dim_invert_impl>> { - using type = D; -}; - -template -struct dim_invert_impl> { - using type = downcast_dimension...>>; -}; - -template -struct dim_invert_impl : dim_invert_impl> {}; - -} // namespace detail - -template -using dim_invert = TYPENAME detail::dim_invert_impl::type; - -// dimension_multiply -namespace detail { - -template -struct to_dimension; - -template -struct to_dimension> { - using type = derived_dimension_base; -}; - -template -struct to_dimension>> { - using type = D; -}; - -/** - * @brief Merges 2 sorted derived dimensions into one units::derived_dimension_base - * - * A result of a dimensional calculation may result with many exponents of the same base dimension orginated - * from different parts of the equation. As the exponents lists of both operands it is enough to merge them - * into one list and consolidate duplicates. Also it is possible that final exponents list will contain only - * one element being a base dimension with exponent 1. In such a case the final dimension should be the base - * dimension itself. - */ -template -using merge_dimension = TYPENAME to_dimension>::type>::type; - -template -struct dimension_multiply_impl; - -template -struct dimension_multiply_impl { - using type = downcast_dimension< - merge_dimension>, derived_dimension_base>>>; -}; - -template -struct dimension_multiply_impl { - using type = - downcast_dimension>, typename D2::downcast_base_type>>; -}; - -template -struct dimension_multiply_impl { - using type = TYPENAME dimension_multiply_impl::type; -}; - -template -struct dimension_multiply_impl { - using type = downcast_dimension>; -}; - -} // namespace detail - -template -using dimension_multiply = TYPENAME detail::dimension_multiply_impl::type; - -template -using dimension_divide = TYPENAME detail::dimension_multiply_impl>::type; - -// dimension_pow -namespace detail { - -template -struct dimension_pow_impl; - -template -struct dimension_pow_impl { - using type = downcast_dimension>>; -}; - -template -struct dimension_pow_impl { - using type = D; -}; - -template -struct dimension_pow_impl>, Num, Den> { - using type = D; -}; - -template -struct dimension_pow_impl { - using type = TYPENAME dimension_pow_impl, Num, Den>::type; -}; - -template -struct dimension_pow_impl, Num, Den> { - using type = downcast_dimension...>>; -}; - -} // namespace detail - -template -using dimension_pow = TYPENAME detail::dimension_pow_impl::type; - -template -using dimension_sqrt = TYPENAME detail::dimension_pow_impl::type; - -} // namespace units diff --git a/src/core/include/units/bits/expression_template.h b/src/core/include/units/bits/expression_template.h new file mode 100644 index 00000000..638fc8c6 --- /dev/null +++ b/src/core/include/units/bits/expression_template.h @@ -0,0 +1,390 @@ +// 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 +#include + +namespace units { + +template +struct type_list {}; + +template +struct per {}; + +template +struct power; + +// TODO make_power() that will simplify cases like ? + +namespace detail { + +template +inline constexpr bool is_specialization_of_power = false; + +template +inline constexpr bool is_specialization_of_power> = true; + +} // namespace detail + +template +struct power { + using factor = F; + static constexpr int num = Num; + static constexpr int den = 1; +}; + +template +struct power { + using factor = F; + static constexpr int num = Num; + static constexpr int den = Den; +}; + +namespace detail { + +template +constexpr auto power_or_T_impl() +{ + constexpr ratio r{Num, Den}; + if constexpr (r.den == 1) { + if constexpr (r.num == 1) + return T{}; + else + return power{}; + } else { + return power{}; + } +}; + +template +using power_or_T = decltype(power_or_T_impl()); + +// type_power +template +struct type_power { + using type = conditional, power>; +}; + +template +struct type_power, Num, 1> { + using type = power; +}; + +template +struct type_power, Num, Den> { + static constexpr ratio r = ratio(power::num, power::den) * ratio(Num, Den); + using type = power_or_T; +}; + +// expr_power +template +struct expr_power; + +template +struct expr_power, Num, Den> { + using type = type_list::type...>; +}; + +/** + * @brief Consolidates contiguous ranges of exponents of the same dimension + * + * If there is more than one exponent with the same dimension they are aggregated into one exponent by adding + * their exponents. If this accumulation will result with 0, such a dimension is removed from the list. + * + * @tparam D derived dimension to consolidate + */ +template +struct expr_consolidate_impl; + +template<> +struct expr_consolidate_impl> { + using type = type_list<>; +}; + +template +struct expr_consolidate_impl> { + using type = type_list; +}; + +template +struct expr_consolidate_impl> { + using type = type_list_push_front>::type, T>; +}; + +template +struct expr_consolidate_impl> { + using type = expr_consolidate_impl, Rest...>>::type; +}; + +template +struct expr_consolidate_impl, Rest...>> { + using type = expr_consolidate_impl< + type_list::num + power::den, power::den>, Rest...>>::type; +}; + +template +struct expr_consolidate_impl, power, Rest...>> { + static constexpr ratio r = + ratio(power::num, power::den) + ratio(power::num, power::den); + using type = conditional>::type, + typename expr_consolidate_impl, Rest...>>::type>; +}; + +template +using expr_consolidate = typename expr_consolidate_impl::type; + + +// expr_simplify + +template +struct expr_simplify_power { + static constexpr ratio r = ratio(powerNum::num, powerNum::den) - ratio(powerDen::num, powerDen::den); + using type = power_or_T; + using num = conditional<(r > 0), type_list, type_list<>>; + using den = conditional<(r < 0), type_list, type_list<>>; +}; + +template typename Pred> +struct expr_simplify; + +template typename Pred> + requires(type_list_size == 0) || (type_list_size == 0) +struct expr_simplify { + using num = NumList; + using den = DenList; +}; + +template typename Pred> +struct expr_simplify, type_list, Pred> { + using impl = conditional::value, expr_simplify, type_list, Pred>, + expr_simplify, type_list, Pred>>; + using num = conditional::value, type_list_push_front, typename impl::num>; + using den = conditional::value, typename impl::den, type_list_push_front>; +}; + +template typename Pred> +struct expr_simplify, type_list, Pred> : + expr_simplify, type_list, Pred> {}; + +template typename Pred> +struct expr_simplify, NRest...>, type_list, Pred> { + using impl = expr_simplify, type_list, Pred>; + using type = expr_simplify_power, power>; + using num = type_list_join; + using den = type_list_join; +}; + +template typename Pred> +struct expr_simplify, type_list, DRest...>, Pred> { + using impl = expr_simplify, type_list, Pred>; + using type = expr_simplify_power, power>; + using num = type_list_join; + using den = type_list_join; +}; + +template typename Pred> + requires(!std::same_as, power>) +struct expr_simplify, NRest...>, type_list, DRest...>, Pred> { + using impl = expr_simplify, type_list, Pred>; + using type = expr_simplify_power, power>; + using num = type_list_join; + using den = type_list_join; +}; + + +// expr_less +template typename Pred> +struct expr_less_impl : Pred {}; + +template typename Pred> +struct expr_less_impl, power, Pred> : Pred {}; + +template typename Pred> +struct expr_less_impl, T2, Pred> : Pred {}; + +template typename Pred> +struct expr_less_impl, Pred> : Pred {}; + +template typename Pred> +struct expr_less_impl, Pred> : std::true_type {}; + +template typename Pred> +using expr_less = expr_less_impl; + + +// expr_fractions +template +struct expr_fractions_impl; + +template +struct expr_fractions_impl { + using den = type_list<>; +}; + +template +struct expr_fractions_impl { + using den = type_list_push_front::den, T>; +}; + +template T, typename... Rest> +struct expr_fractions_impl : + TYPENAME expr_fractions_impl::den {}; + +template +struct expr_fractions_impl { + using num = type_list<>; + using den = type_list<>; +}; + +template +struct expr_fractions_impl> { + using num = type_list<>; + using den = TYPENAME expr_fractions_impl::den; +}; + +template T, typename... Rest> +struct expr_fractions_impl : expr_fractions_impl {}; + +template +struct expr_fractions_impl { + using impl = expr_fractions_impl; + using num = type_list_push_front; + using den = TYPENAME impl::den; +}; + +template +struct expr_fractions { +private: + using impl = expr_fractions_impl; +public: + using num = TYPENAME impl::num; + using den = TYPENAME impl::den; +}; + +template typename To> +constexpr auto expr_expression_impl() +{ + constexpr std::size_t num = type_list_size; + constexpr std::size_t den = type_list_size; + + if constexpr (num == 0 && den == 0) { + return OneType{}; + } else if constexpr (num > 0 && den > 0) { + return type_list_map>, To>{}; + } else if constexpr (den > 0) { + return To>{}; + } else { + if constexpr (num == 1 && !is_specialization_of_power>) + // temporary derived type not needed -> just return the original one + return type_list_front{}; + else + return type_list_map{}; + } +} + +template typename To> +using expr_expression = decltype(expr_expression_impl()); + +template typename Pred, + template typename To> +constexpr auto get_optimized_expression() +{ + using num_list = expr_consolidate; + using den_list = expr_consolidate; + using simple = expr_simplify; + using expr = expr_expression; + return expr{}; +} + +/** + * @brief Merges 2 sorted derived dimensions into one units::normalized_dimension + * + * A result of a dimensional calculation may result with many exponents of the same base dimension originated + * from different parts of the equation. As the exponents lists of both operands it is enough to merge them + * into one list and consolidate duplicates. Also it is possible that final exponents list will contain only + * one element being a base dimension with exponent 1. In such a case the final dimension should be the base + * dimension itself. + */ +template typename Pred, + template typename To> +constexpr auto expr_multiply() +{ + if constexpr (is_same_v) { + return T2{}; + } else if constexpr (is_same_v) { + return T1{}; + } else if constexpr (is_specialization_of && is_specialization_of) { + return get_optimized_expression, + type_list_merge_sorted, OneType, Pred, + To>(); + } else if constexpr (is_specialization_of) { + return get_optimized_expression, Pred>, typename T1::den, + OneType, Pred, To>(); + } else if constexpr (is_specialization_of) { + return get_optimized_expression, Pred>, typename T2::den, + OneType, Pred, To>(); + } else { + return get_optimized_expression, type_list, Pred>, type_list<>, OneType, + Pred, To>(); + } +} + +template typename Pred, + template typename To> +constexpr auto expr_divide() +{ + if constexpr (is_same_v) { + return OneType{}; + } else if constexpr (is_same_v) { + return T1{}; + } else if constexpr (is_specialization_of && is_specialization_of) { + return get_optimized_expression, + type_list_merge_sorted, OneType, Pred, + To>(); + } else if constexpr (is_specialization_of) { + return get_optimized_expression, Pred>, + OneType, Pred, To>(); + } else if constexpr (is_specialization_of) { + return get_optimized_expression, Pred>, typename T2::num, + OneType, Pred, To>(); + } else { + return To>{}; + } +} + +template typename To> +constexpr auto expr_invert() +{ + if constexpr (is_specialization_of) + return expr_expression{}; + else + return To>{}; +} + +} // namespace detail + +} // namespace units diff --git a/src/core/include/units/bits/external/type_list.h b/src/core/include/units/bits/external/type_list.h index b6b6e7ec..28007478 100644 --- a/src/core/include/units/bits/external/type_list.h +++ b/src/core/include/units/bits/external/type_list.h @@ -22,6 +22,7 @@ #pragma once +#include // IWYU pragma: keep #include UNITS_DIAGNOSTIC_PUSH @@ -42,6 +43,38 @@ inline constexpr bool is_type_list> = true; template concept TypeList = detail::is_type_list; +// size + +namespace detail { + +template typename List, typename... Ts> +constexpr std::size_t type_list_size_impl(List) +{ + return sizeof...(Ts); +} + +} // namespace detail + +template +inline constexpr std::size_t type_list_size = detail::type_list_size_impl(List{}); + +// front + +namespace detail { + +template +struct type_list_front_impl; + +template typename List, typename T, typename... Ts> +struct type_list_front_impl> { + using type = T; +}; + +} // namespace detail + +template +using type_list_front = TYPENAME detail::type_list_front_impl::type; + // push_front namespace detail { @@ -218,6 +251,23 @@ struct type_list_sort_impl, Pred> { template typename Pred> using type_list_sort = TYPENAME detail::type_list_sort_impl::type; +// map + +namespace detail { + +template typename To> +struct type_list_map_impl; + +template typename From, template typename To, typename... Args> +struct type_list_map_impl, To> { + using type = To; +}; + +} // namespace detail + +template typename To> +using type_list_map = TYPENAME detail::type_list_map_impl::type; + } // namespace units UNITS_DIAGNOSTIC_POP diff --git a/src/core/include/units/bits/external/type_name.h b/src/core/include/units/bits/external/type_name.h new file mode 100644 index 00000000..e65ec9d1 --- /dev/null +++ b/src/core/include/units/bits/external/type_name.h @@ -0,0 +1,27 @@ +// https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/56766138#56766138 + +#pragma once + +#include + +template +constexpr auto type_name() +{ + std::string_view name, prefix, suffix; +#ifdef __clang__ + name = __PRETTY_FUNCTION__; + prefix = "auto type_name() [T = "; + suffix = "]"; +#elif defined(__GNUC__) + name = __PRETTY_FUNCTION__; + prefix = "constexpr auto type_name() [with T = "; + suffix = "]"; +#elif defined(_MSC_VER) + name = __FUNCSIG__; + prefix = "auto __cdecl type_name<"; + suffix = ">(void)"; +#endif + name.remove_prefix(prefix.size()); + name.remove_suffix(suffix.size()); + return name; +} diff --git a/src/core/include/units/derived_dimension.h b/src/core/include/units/derived_dimension.h deleted file mode 100644 index 0af2e096..00000000 --- a/src/core/include/units/derived_dimension.h +++ /dev/null @@ -1,92 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -// IWYU pragma: begin_exports -#include -// IWYU pragma: end_exports - -namespace units { - -namespace detail { - -/** - * @brief Converts user provided derived dimension specification into a valid units::derived_dimension_base definition - * - * User provided definition of a derived dimension may contain the same base dimension repeated more than once on the - * list possibly hidden in other derived units provided by the user. The process here should: - * 1. Extract derived dimensions into exponents of base dimensions. - * 2. Sort the exponents so the same dimensions are placed next to each other. - * 3. Consolidate contiguous range of exponents of the same base dimensions to a one (or possibly zero) exponent for - * this base dimension. - */ -template -using make_dimension = TYPENAME to_derived_dimension_base< - typename dim_consolidate::type, exponent_less>>::type>::type; - -} // namespace detail - -/** - * @brief The list of exponents of dimensions (both base and derived) provided by the user - * - * This is the user's interface to create derived dimensions. Exponents list can contain powers of factors of both - * base and derived dimensions. This is called a "recipe" of the dimension and among others is used to print - * unnamed coherent units of this dimension. - * - * Coherent unit is a unit that, for a given system of quantities and for a chosen set of base units, is a product - * of powers of base units with no other proportionality factor than one. - * - * The implementation is responsible for unpacking all of the dimensions into a list containing only base dimensions - * and their factors and putting them to derived_dimension_base class template. - * - * Sometimes units of equivalent quantities in different systems of units do not share the same reference so they - * cannot be easily converted to each other. An example can be a pressure for which a coherent unit in SI is pascal - * and in CGS barye. Those two units are not directly related with each other with some ratio. As they both are - * coherent units of their dimensions, the ratio between them is directly determined by the ratios of base units - * defined in base dimensions end their exponents in the derived dimension recipe. To provide interoperability of - * such quantities of different systems mag is being used. The result of the division of two - * mag of two quantities of equivalent dimensions in two different systems gives a ratio between their - * coherent units. Alternatively, the user would always have to directly define a barye in terms of pascal or vice - * versa. - * - * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) - * @tparam U a coherent unit of a derived dimension - * @tparam Es the list of exponents of ingredient dimensions - */ -template -struct derived_dimension : downcast_dispatch> { - using recipe = exponent_list; - using coherent_unit = U; - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = - detail::absolute_magnitude(typename derived_dimension::exponents()) / U::mag; -}; - -} // namespace units diff --git a/src/core/include/units/dimension.h b/src/core/include/units/dimension.h new file mode 100644 index 00000000..08015f25 --- /dev/null +++ b/src/core/include/units/dimension.h @@ -0,0 +1,260 @@ +// 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 +#include +#include + +namespace units { + +/** + * @brief A dimension of a base quantity + * + * Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity + * in the subset can be expressed in terms of the other quantities within that subset. They are referred to as + * being mutually independent since a base quantity cannot be expressed as a product of powers of the other base + * quantities. + * + * Symbol template parameters is an unique identifier of the base dimension. The same identifiers can be multiplied + * and divided which will result with an adjustment of its factor in an Exponent of a DerivedDimension + * (in case of zero the dimension will be simplified and removed from further analysis of current expresion). + * + * @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support + */ +template +struct base_dimension { + static constexpr auto symbol = Symbol; ///< Unique base dimension identifier +}; + +namespace detail { + +template +void to_base_base_dimension(const volatile base_dimension*); + +} // namespace detail + +/** + * @brief A concept matching all base dimensions in the library. + * + * Satisfied by all dimension types derived from an specialization of `base_dimension`. + */ +template +concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); }; + +template +struct base_dimension_less : std::bool_constant<(D1::symbol < D2::symbol)> {}; + +// TODO Can we provide a smarter implementation? +std::false_type is_derived_dimension(...); + +/** + * @brief A concept matching all derived dimensions in the library. + * + * Satisfied by all dimension types derived from an specialization of `derived_dimension`. + */ +template +concept DerivedDimension = decltype(is_derived_dimension(std::declval()))::value; + +/** + * @brief A concept matching all dimensions in the library. + * + * Satisfied by all dimension types for which either `BaseDimension` or `DerivedDimension` is `true`. + */ +template +concept Dimension = BaseDimension || DerivedDimension; + +namespace detail { + +/** + * @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions + * + * @tparam Es Exponents of potentially derived dimensions + */ +template +struct dim_extract; + +template<> +struct dim_extract, type_list<>> { + using num = type_list<>; + using den = type_list<>; +}; + +template + requires BaseDimension || BaseDimension +struct dim_extract, type_list> { + using impl = dim_extract, type_list>; + using num = type_list_push_front; + using den = TYPENAME impl::den; +}; + +template + requires BaseDimension || BaseDimension +struct dim_extract, type_list> { + using impl = dim_extract, type_list>; + using num = TYPENAME impl::num; + using den = type_list_push_front; +}; + +template +struct dim_extract, type_list> : + dim_extract, + type_list_push_back> {}; + +template +struct dim_extract, NRest...>, type_list> : + dim_extract::num, power::den>::type, + NRest...>, + type_list_push_back< + typename expr_power::num, power::den>::type, + Dens...>> {}; + + +template +struct dim_extract, type_list> : + dim_extract> {}; + +template +struct dim_extract, type_list, DRest...>> : + dim_extract::num, power::den>::type, + type_list_push_back< + typename expr_power::num, power::den>::type, + DRest...>> {}; + +template +using type_list_of_base_dimension_less = expr_less; + +/** + * @brief Converts user provided derived dimension specification into a valid units::normalized_dimension definition + * + * User provided definition of a derived dimension may contain the same base dimension repeated more than once on the + * list possibly hidden in other derived units provided by the user. The process here should: + * 1. Extract derived dimensions into exponents of base dimensions. + * 2. Sort the exponents so the same dimensions are placed next to each other. + * 3. Consolidate contiguous range of exponents of the same base dimensions to a one (or possibly zero) exponent for + * this base dimension. + */ +template +struct normalized_dimension : detail::expr_fractions { +private: + using base = detail::expr_fractions; + using extracted = dim_extract; + using num_list = expr_consolidate>; + using den_list = expr_consolidate>; + using simple = expr_simplify; +public: + using normalized_num = TYPENAME simple::num; + using normalized_den = TYPENAME simple::den; +}; + +} // namespace detail + +// TODO add checking for `per` and power elements as well +template +concept DimensionSpec = Dimension || is_specialization_of || detail::is_specialization_of_power; + +// User should not instantiate this type!!! +template +struct derived_dimension : detail::normalized_dimension, Ds...> {}; + +// TODO move type_list_fractions out of + +template +std::true_type is_derived_dimension(const volatile derived_dimension*); + +/** + * @brief Dimension one + * + * Dimension for which all the exponents of the factors corresponding to the base + * dimensions are zero. Also commonly named as "dimensionless". + */ +inline constexpr struct dim_one : derived_dimension<> { +} dim_one; + +namespace detail { + +template +struct dimension_less : std::bool_constant() < type_name()> {}; + +template +using type_list_of_dimension_less = expr_less; + +} // namespace detail + +template +constexpr Dimension auto operator*(D1, D2) +{ + return detail::expr_multiply(); +} + +template +constexpr Dimension auto operator/(D1, D2) +{ + return detail::expr_divide(); +} + +template +constexpr Dimension auto operator/(int value, D) +{ + gsl_Assert(value == 1); + return detail::expr_invert(); +} + +template +constexpr bool operator==(D1, D2) +{ + return false; +} + +template +constexpr bool operator==(D1, D2) +{ + return D1::symbol == D2::symbol; +} + +template + requires(type_list_size == 0) && (type_list_size == 1) && + BaseDimension> +constexpr bool operator==(D1, D2) +{ + return D1::symbol == type_list_front::symbol; +} + +template + requires(type_list_size == 0) && (type_list_size == 1) && + BaseDimension> +constexpr bool operator==(D1, D2) +{ + return type_list_front::symbol == D2::symbol; +} + +template +constexpr bool operator==(D1, D2) +{ + return is_same_v && + is_same_v; +} + +} // namespace units diff --git a/src/core/include/units/exponent.h b/src/core/include/units/exponent.h deleted file mode 100644 index 2f713af2..00000000 --- a/src/core/include/units/exponent.h +++ /dev/null @@ -1,85 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include - -namespace units { - -/** - * @brief A power of factor corresponding to the dimension of a quantity - * - * @tparam Dim component dimension of a derived quantity - * @tparam Num numinator of the factor - * @tparam Den denominator of the factor - */ -template -struct exponent { - using dimension = Dim; - static constexpr std::intmax_t num = Num; - static constexpr std::intmax_t den = Den; -}; - -// is_exponent -namespace detail { - -template -inline constexpr bool is_exponent> = true; - -} // namespace detail - -// exponent_less -template - requires BaseDimension && BaseDimension -struct exponent_less : base_dimension_less {}; - -// exponent_invert -namespace detail { - -template -constexpr exponent exponent_invert_impl(exponent); - -} // namespace detail - -template -using exponent_invert = decltype(detail::exponent_invert_impl(E())); - -// exponent_multiply -namespace detail { - -template -struct exponent_multiply_impl { - static constexpr ratio r = ratio(E::num, E::den) * ratio(Num, Den); - using type = exponent; -}; - -} // namespace detail - -template -using exponent_multiply = TYPENAME detail::exponent_multiply_impl::type; - -template -struct exponent_list {}; - -} // namespace units diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 0195cf8e..d0bd6501 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -595,7 +595,7 @@ constexpr auto common_magnitude(magnitude, magnitude) } else { // When the bases are equal, pick whichever has the lower power. constexpr auto common_tail = common_magnitude(magnitude{}, magnitude{}); - if constexpr (H1.power < H2.power) { + if constexpr ((H1.power) < (H2.power)) { return magnitude

{} * common_tail; } else { return magnitude

{} * common_tail; diff --git a/src/core/include/units/prefix.h b/src/core/include/units/prefix.h deleted file mode 100644 index 803cf341..00000000 --- a/src/core/include/units/prefix.h +++ /dev/null @@ -1,62 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -// IWYU pragma: begin_exports -#include -#include -#include -// IWYU pragma: end_exports - -namespace units { - -namespace detail { - -template -struct prefix_base : downcast_base> { - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M; -}; - -} // namespace detail - -/** - * @brief A prefix used to scale units - * - * Data from a prefix class is used in two cases: - * - when defining a prefixed_unit its ratio is used to scale the reference unit and its - * symbol is used to prepend to the symbol of referenced unit - * - when printing the symbol of a scaled unit that was not predefined by the user but its - * factor matches ratio of a prefix, its symbol will be prepended to the symbol of the unit - * - * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) - * @tparam Symbol a text representation of the prefix - * @tparam R factor to be used to scale a unit - */ -template -struct prefix : downcast_dispatch, downcast_mode::on> { - static constexpr auto symbol = Symbol; -}; - -} // namespace units diff --git a/src/core/include/units/reference.h b/src/core/include/units/reference.h index c6a68fb1..6fa3c6cc 100644 --- a/src/core/include/units/reference.h +++ b/src/core/include/units/reference.h @@ -22,35 +22,37 @@ #pragma once -#include -#include +#include +#include namespace units { -template U, Representation Rep> -class quantity; +// TODO Concept for system reference +template +class quantity { +public: + using reference = decltype(R); + static constexpr auto dimension = reference::dimension; + static constexpr auto unit = reference::unit; -template U> -struct reference; + quantity(Rep) {} +}; + +template +struct system_reference; namespace detail { -template -using reference_multiply_impl = reference>; +template +void to_base_specialization_of_system_reference(const volatile system_reference*); -template -using reference_divide_impl = reference>; +template +// inline constexpr bool // TODO: Replace with concept when it works with MSVC +concept is_derived_from_specialization_of_system_reference = + requires(T* t) { detail::to_base_specialization_of_system_reference(t); }; } // namespace detail -template -using reference_multiply = - detail::reference_multiply_impl, R1, R2>; - -template -using reference_divide = - detail::reference_divide_impl, R1, R2>; - /** * @brief The type for quantity references * @@ -90,50 +92,71 @@ using reference_divide = * The following syntaxes are not allowed: * `2 / s`, `km * 3`, `s / 4`, `70 * km / h`. */ -template U> -struct reference { - using dimension = D; - using unit = U; - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag; +template +struct reference; - // Hidden Friends - // Below friend functions are to be found via argument-dependent lookup only - - template - [[nodiscard]] friend constexpr reference_multiply operator*(reference, R2) - { - return {}; - } - - template - [[nodiscard]] friend constexpr reference_divide operator/(reference, R2) - { - return {}; - } - - template - [[nodiscard]] friend constexpr Quantity auto operator*(const Rep& lhs, reference) - { - return quantity(lhs); - } - - friend void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, reference) = delete; - - template - [[nodiscard]] friend constexpr bool operator==(reference, R2) - { - return false; - } - - [[nodiscard]] friend constexpr bool operator==(reference, reference) { return true; } +template + requires detail::is_derived_from_specialization_of_system_reference +struct reference { + using system_reference = R; + static constexpr auto dimension = R::dimension; + static constexpr U unit{}; + // static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag; }; -// type traits -namespace detail { +template +struct reference { + static constexpr D dimension{}; + static constexpr U unit{}; + // static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag; +}; -template -inline constexpr bool is_reference> = true; +// Reference +/** + * @brief A concept matching all references in the library. + * + * Satisfied by all specializations of @c reference. + */ +template +concept Reference = is_specialization_of; -} // namespace detail +template +[[nodiscard]] constexpr reference operator*(R1, + R2) +{ + return {}; +} + +template +[[nodiscard]] constexpr reference operator/(R1, + R2) +{ + return {}; +} + +// TODO Update when quantity is done +// template +// [[nodiscard]] friend constexpr Quantity auto operator*(const Rep& lhs, reference) +template +[[nodiscard]] constexpr quantity operator*(const Rep& lhs, R) +{ + return quantity(lhs); +} + +// friend void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, reference) = delete; + +// TODO will use deducing this +template +struct system_reference { + static constexpr auto dimension = Dim; + static constexpr auto coherent_unit = CoU; + + template + // requires same_unit_reference + [[nodiscard]] constexpr reference operator[](U) const + { + return {}; + } +}; } // namespace units diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index c388369c..24e1b0eb 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -22,27 +22,28 @@ #pragma once -#include -#include +#include +#include +#include #include +#include +#include + +// #include // IWYU pragma: begin_exports -#include -#include -#include -#include -#include -#include +// #include +// #include // IWYU pragma: end_exports namespace units { -namespace detail { +// namespace detail { -template -inline constexpr bool can_be_prefixed = false; +// template +// inline constexpr bool can_be_prefixed = false; -} // namespace detail +// } // namespace detail /** * @brief A common point for a hierarchy of units @@ -63,16 +64,56 @@ inline constexpr bool can_be_prefixed = false; * it gets the incomplete child's type with the CRTP idiom. */ template -struct scaled_unit : downcast_base> { +struct scaled_unit { static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M; using reference = U; }; -template -using downcast_unit = downcast::reference>>; +// TODO: Remove when P1985 accepted +namespace detail { -template -struct same_unit_reference : is_same {}; +template +void to_base_scaled_unit(const volatile scaled_unit*); + +} // namespace detail + +/** + * @brief A concept matching all unit types in the library + * + * Satisfied by all unit types derived from an specialization of :class:`scaled_unit`. + */ +template +concept Unit = requires(T* t) { detail::to_base_scaled_unit(t); }; + +namespace detail { + +template +inline constexpr bool is_named = false; + +} + +template +concept NamedUnit = Unit && detail::is_named; + +template +struct same_unit_reference : is_same {}; + +// TODO add checking for `per` and power elements as well +template +concept UnitSpec = Unit || is_specialization_of || detail::is_specialization_of_power; + +template +struct derived_unit : detail::expr_fractions, Us...>, scaled_unit(), derived_unit> {}; + +// : detail::normalized_dimension, Ds...> {}; + +/** + * @brief Unit one + * + * Unit of a dimensionless quantity. + */ +inline constexpr struct one : derived_unit<> { +} one; /** * @brief A named unit @@ -80,27 +121,35 @@ struct same_unit_reference : is_same -struct named_unit : downcast_dispatch(), Child>> { +template +struct named_unit; + +template +struct named_unit : scaled_unit(), named_unit> { static constexpr auto symbol = Symbol; }; +template + requires is_specialization_of, derived_unit> +struct named_unit : decltype(U) { + static constexpr auto symbol = Symbol; +}; + + /** * @brief A named scaled unit * * Defines a new named unit that is a scaled version of another unit. * A named unit may be composed with a prefix to create a prefixed_unit. * - * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam Symbol a short text representation of the unit * @tparam M the Magnitude by which to scale U * @tparam U a reference unit to scale */ -template -struct named_scaled_unit : downcast_dispatch> { +template +struct named_scaled_unit : scaled_unit { static constexpr auto symbol = Symbol; }; @@ -115,11 +164,15 @@ struct named_scaled_unit : downcast_dispatch - requires detail::can_be_prefixed -struct prefixed_unit : downcast_dispatch> { - static constexpr auto symbol = P::symbol + U::symbol; -}; +// template +// requires detail::can_be_prefixed +// struct prefixed_unit : downcast_dispatch> { +// static constexpr auto symbol = P::symbol + U::symbol; +// }; + + +template +struct prefixed_unit : scaled_unit {}; /** * @brief A coherent unit of a derived quantity @@ -129,105 +182,96 @@ struct prefixed_unit : downcast_dispatch -struct derived_unit : downcast_dispatch(), Child>> {}; - -/** - * @brief A unit with a deduced ratio and symbol - * - * Defines a new unit with a deduced ratio and symbol based on the recipe from the provided - * derived dimension. The number and order of provided units should match the recipe of the - * derived dimension. All of the units provided should also be a named ones so it is possible - * to create a deduced symbol text. - * - * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) - * @tparam Dim a derived dimension recipe to use for deduction - * @tparam U the unit of the first composite dimension from provided derived dimension's recipe - * @tparam URest the units for the rest of dimensions from the recipe - */ -template - requires detail::compatible_units -struct derived_scaled_unit : downcast_dispatch> { - static constexpr auto symbol = detail::derived_symbol_text(); -}; - -/** - * @brief An aliased named unit - * - * Defines a named alias for another unit. It is useful to assign alternative names and symbols - * to the already predefined units (i.e. "tonne" for "megagram"). - * A alias unit may be composed with a prefix to create a prefixed_alias_unit. - * - * @tparam U Unit for which an alias is defined - * @tparam Symbol a short text representation of the unit - */ -template -struct alias_unit : U { - static constexpr auto symbol = Symbol; -}; - -/** - * @brief A prefixed alias unit - * - * Defines a new unit that is an alias for a scaled version of another unit by the provided - * prefix. It is only possible to create such a unit if the given prefix type matches the one - * defined in a reference unit. - * - * @tparam U Unit for which an alias is defined - * @tparam P prefix to be appied to the reference unit - * @tparam AU reference alias unit - */ -template - requires(!AliasUnit) -struct prefixed_alias_unit : U { - static constexpr auto symbol = P::symbol + AU::symbol; -}; - -/** - * @brief Unknown coherent unit - * - * Used as a coherent unit of an unknown dimension. - */ -template -struct unknown_coherent_unit : - downcast_dispatch, - scaled_unit()), unknown_coherent_unit>> {}; +// template +// struct derived_unit : downcast_dispatch(), Child>> {}; namespace detail { -template -void is_named_impl(const volatile named_unit*); +template +void is_named_impl(const volatile named_unit*); -template -void is_named_impl(const volatile named_scaled_unit*); - -template -void is_named_impl(const volatile prefixed_unit*); - -template -void is_named_impl(const volatile alias_unit*); - -template -void is_named_impl(const volatile prefixed_alias_unit*); +template +void is_named_impl(const volatile named_scaled_unit*); template inline constexpr bool is_named = requires(U * u) { is_named_impl(u); }; -template -void can_be_prefixed_impl(const volatile named_unit*); +// template +// void can_be_prefixed_impl(const volatile named_unit*); -template -void can_be_prefixed_impl(const volatile named_scaled_unit*); +// template +// void can_be_prefixed_impl(const volatile named_scaled_unit*); -template -void can_be_prefixed_impl(const volatile alias_unit*); +// template +// void can_be_prefixed_impl(const volatile alias_unit*); -template -inline constexpr bool can_be_prefixed = requires(U * u) { can_be_prefixed_impl(u); }; +// template +// inline constexpr bool can_be_prefixed = requires(U * u) { can_be_prefixed_impl(u); }; -template -inline constexpr bool can_be_prefixed> = can_be_prefixed; +// template +// inline constexpr bool can_be_prefixed> = can_be_prefixed; + + +template +struct unit_less : std::bool_constant() < type_name()> {}; + +template +using type_list_of_unit_less = expr_less; } // namespace detail +template +constexpr Unit auto operator*(U1, U2) +{ + return detail::expr_multiply(); +} + +template +constexpr Unit auto operator/(U1, U2) +{ + return detail::expr_divide(); +} + +template +constexpr Unit auto operator/(int value, U) +{ + gsl_Assert(value == 1); + return detail::expr_invert(); +} + +template +constexpr bool operator==(U1, U2) +{ + return false; +} + +// template +// constexpr bool operator==(D1, D2) +// { +// return D1::symbol == D2::symbol; +// } + +// template +// requires(type_list_size == 0) && (type_list_size == 1) && +// BaseDimension> +// constexpr bool operator==(D1, D2) +// { +// return D1::symbol == type_list_front::symbol; +// } + +// template +// requires(type_list_size == 0) && (type_list_size == 1) && +// BaseDimension> +// constexpr bool operator==(D1, D2) +// { +// return type_list_front::symbol == D2::symbol; +// } + +// template +// constexpr bool operator==(D1, D2) +// { +// return std::is_same_v && +// std::is_same_v; +// } + } // namespace units diff --git a/src/systems/si/include/units/isq/si/prefixes.h b/src/systems/si/include/units/isq/si/prefixes.h index 562370b6..e150426e 100644 --- a/src/systems/si/include/units/isq/si/prefixes.h +++ b/src/systems/si/include/units/isq/si/prefixes.h @@ -22,31 +22,49 @@ #pragma once -#include +#include namespace units::isq::si { -// clang-format off -struct yocto : prefix(mag<10>())> {}; -struct zepto : prefix(mag<10>())> {}; -struct atto : prefix(mag<10>())> {}; -struct femto : prefix(mag<10>())> {}; -struct pico : prefix(mag<10>())> {}; -struct nano : prefix(mag<10>())> {}; -struct micro : prefix(mag<10>())> {}; -struct milli : prefix(mag<10>())> {}; -struct centi : prefix(mag<10>())> {}; -struct deci : prefix(mag<10>())> {}; -struct deca : prefix(mag<10>())> {}; -struct hecto : prefix(mag<10>())> {}; -struct kilo : prefix(mag<10>())> {}; -struct mega : prefix(mag<10>())> {}; -struct giga : prefix(mag<10>())> {}; -struct tera : prefix(mag<10>())> {}; -struct peta : prefix(mag<10>())> {}; -struct exa : prefix(mag<10>())> {}; -struct zetta : prefix(mag<10>())> {}; -struct yotta : prefix(mag<10>())> {}; -// clang-format on +template +struct yocto : prefixed_unit<"y", pow<-24>(mag<10>()), U> {}; +template +struct zepto : prefixed_unit<"z", pow<-21>(mag<10>()), U> {}; +template +struct atto : prefixed_unit<"a", pow<-18>(mag<10>()), U> {}; +template +struct femto : prefixed_unit<"f", pow<-15>(mag<10>()), U> {}; +template +struct pico : prefixed_unit<"p", pow<-12>(mag<10>()), U> {}; +template +struct nano : prefixed_unit<"n", pow<-9>(mag<10>()), U> {}; +template +struct micro : prefixed_unit(mag<10>()), U> {}; +template +struct milli : prefixed_unit<"m", pow<-3>(mag<10>()), U> {}; +template +struct centi : prefixed_unit<"c", pow<-2>(mag<10>()), U> {}; +template +struct deci : prefixed_unit<"d", pow<-1>(mag<10>()), U> {}; +template +struct deca : prefixed_unit<"da", pow<1>(mag<10>()), U> {}; +template +struct hecto : prefixed_unit<"h", pow<2>(mag<10>()), U> {}; +template +struct kilo : prefixed_unit<"k", pow<3>(mag<10>()), U> {}; +template +struct mega : prefixed_unit<"M", pow<6>(mag<10>()), U> {}; +template +struct giga : prefixed_unit<"G", pow<9>(mag<10>()), U> {}; +template +struct tera : prefixed_unit<"T", pow<12>(mag<10>()), U> {}; +template +struct peta : prefixed_unit<"P", pow<15>(mag<10>()), U> {}; +template +struct exa : prefixed_unit<"E", pow<18>(mag<10>()), U> {}; +template +struct zetta : prefixed_unit<"Z", pow<21>(mag<10>()), U> {}; +template +struct yotta : prefixed_unit<"Y", pow<24>(mag<10>()), U> {}; } // namespace units::isq::si