From d4734f630d13cfa5f405c815eca124ac4ad09151 Mon Sep 17 00:00:00 2001 From: Yves Delley Date: Sat, 11 May 2024 11:10:30 +0200 Subject: [PATCH 01/61] add missing unit symbols for ohm --- .../mp-units/systems/si/unit_symbols.h | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/systems/include/mp-units/systems/si/unit_symbols.h b/src/systems/include/mp-units/systems/si/unit_symbols.h index 8477bbb2..eaa2e7d2 100644 --- a/src/systems/include/mp-units/systems/si/unit_symbols.h +++ b/src/systems/include/mp-units/systems/si/unit_symbols.h @@ -474,6 +474,32 @@ inline constexpr auto YF = yotta; inline constexpr auto RF = ronna; inline constexpr auto QF = quetta; +inline constexpr auto qohm = quecto; +inline constexpr auto rohm = ronto; +inline constexpr auto yohm = yocto; +inline constexpr auto zohm = zepto; +inline constexpr auto aohm = atto; +inline constexpr auto fohm = femto; +inline constexpr auto pohm = pico; +inline constexpr auto nohm = nano; +inline constexpr auto uohm = micro; +inline constexpr auto mohm = milli; +inline constexpr auto cohm = centi; +inline constexpr auto dohm = deci; +inline constexpr auto ohm = si::ohm; +inline constexpr auto daohm = deca; +inline constexpr auto hohm = hecto; +inline constexpr auto kohm = kilo; +inline constexpr auto Mohm = mega; +inline constexpr auto Gohm = giga; +inline constexpr auto Tohm = tera; +inline constexpr auto Pohm = peta; +inline constexpr auto Eohm = exa; +inline constexpr auto Zohm = zetta; +inline constexpr auto Yohm = yotta; +inline constexpr auto Rohm = ronna; +inline constexpr auto Qohm = quetta; + inline constexpr auto qS = quecto; inline constexpr auto rS = ronto; inline constexpr auto yS = yocto; From 6c85ba3f17a360d48865efbae6bbd4c90aafd758 Mon Sep 17 00:00:00 2001 From: Yves Delley Date: Tue, 14 May 2024 15:50:04 +0200 Subject: [PATCH 02/61] Update src/systems/include/mp-units/systems/si/unit_symbols.h Co-authored-by: Mateusz Pusz --- src/systems/include/mp-units/systems/si/unit_symbols.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/include/mp-units/systems/si/unit_symbols.h b/src/systems/include/mp-units/systems/si/unit_symbols.h index eaa2e7d2..060bfb93 100644 --- a/src/systems/include/mp-units/systems/si/unit_symbols.h +++ b/src/systems/include/mp-units/systems/si/unit_symbols.h @@ -486,7 +486,7 @@ inline constexpr auto uohm = micro; inline constexpr auto mohm = milli; inline constexpr auto cohm = centi; inline constexpr auto dohm = deci; -inline constexpr auto ohm = si::ohm; +using si::ohm; inline constexpr auto daohm = deca; inline constexpr auto hohm = hecto; inline constexpr auto kohm = kilo; From a479246ea714a12ddfd4d6fba4c8f4807219e5e9 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 12:30:45 +0200 Subject: [PATCH 03/61] feat: `basic_fixed_string` implementation and testing improved --- src/core/include/mp-units/bits/hacks.h | 14 ++ src/core/include/mp-units/ext/algorithm.h | 39 --- src/core/include/mp-units/ext/fixed_string.h | 227 +++++++++++++----- .../include/mp-units/framework/dimension.h | 3 +- .../include/mp-units/framework/symbol_text.h | 9 +- src/core/include/mp-units/framework/unit.h | 3 +- test/runtime/CMakeLists.txt | 4 +- test/runtime/fixed_string_test.cpp | 67 ++++++ test/runtime/fmt_test.cpp | 13 - test/static/fixed_string_test.cpp | 148 ++++++++++-- 10 files changed, 392 insertions(+), 135 deletions(-) create mode 100644 test/runtime/fixed_string_test.cpp diff --git a/src/core/include/mp-units/bits/hacks.h b/src/core/include/mp-units/bits/hacks.h index f1d9b837..f8522059 100644 --- a/src/core/include/mp-units/bits/hacks.h +++ b/src/core/include/mp-units/bits/hacks.h @@ -90,6 +90,20 @@ #endif + +#if !defined __cpp_lib_ranges_to_container + +namespace std { + +struct from_range_t { + explicit from_range_t() = default; +}; +inline constexpr from_range_t from_range{}; + +} // namespace std + +#endif + #if defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 17 #define MP_UNITS_CONSTEVAL constexpr diff --git a/src/core/include/mp-units/ext/algorithm.h b/src/core/include/mp-units/ext/algorithm.h index 1d5a7d67..3aa460b8 100644 --- a/src/core/include/mp-units/ext/algorithm.h +++ b/src/core/include/mp-units/ext/algorithm.h @@ -72,45 +72,6 @@ constexpr bool all_of(InputIt first, InputIt last, UnaryPred p) return find_if_not(first, last, p) == last; } -template -constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) -{ - for (; first1 != last1; ++first1, ++first2) { - if (!(*first1 == *first2)) { - return false; - } - } - return true; -} - -template -constexpr auto lexicographical_compare_three_way(I1 first1, I1 last1, I2 first2, I2 last2, Cmp comp) - -> decltype(comp(*first1, *first2)) -{ - using ret_t = decltype(comp(*first1, *first2)); - static_assert(std::disjunction_v, std::is_same, - std::is_same>, - "The return type must be a comparison category type."); - - bool exhaust1 = (first1 == last1); - bool exhaust2 = (first2 == last2); - MP_UNITS_DIAGNOSTIC_PUSH - MP_UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT - for (; !exhaust1 && !exhaust2; exhaust1 = (++first1 == last1), exhaust2 = (++first2 == last2)) - if (auto c = comp(*first1, *first2); c != 0) return c; - MP_UNITS_DIAGNOSTIC_POP - - if (!exhaust1) return std::strong_ordering::greater; - if (!exhaust2) return std::strong_ordering::less; - return std::strong_ordering::equal; -} - -template -constexpr auto lexicographical_compare_three_way(I1 first1, I1 last1, I2 first2, I2 last2) -{ - return ::mp_units::detail::lexicographical_compare_three_way(first1, last1, first2, last2, std::compare_three_way()); -} - template constexpr ForwardIt max_element(ForwardIt first, ForwardIt last) { diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 8dac2822..80165c3f 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -26,10 +26,10 @@ // NOLINTBEGIN(*-avoid-c-arrays) #pragma once -// TODO use when moved to C++20 modules (parsing takes too long for each translation unit) +#include // IWYU pragma: keep #include #include // IWYU pragma: keep -#include +#include #ifndef MP_UNITS_IN_MODULE_INTERFACE #include @@ -49,22 +49,34 @@ namespace mp_units { * @tparam CharT Character type to be used by the string * @tparam N The size of the string */ -template -struct basic_fixed_string { - CharT data_[N + 1] = {}; +template> +class basic_fixed_string { +public: + CharT data_[N + 1] = {}; // exposition only + // types + using traits_type = Traits; using value_type = CharT; - using pointer = CharT*; - using const_pointer = const CharT*; - using reference = CharT&; - using const_reference = const CharT&; - using const_iterator = const CharT*; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using const_iterator = const value_type*; using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator; + using reverse_iterator = const_reverse_iterator; using size_type = std::size_t; using difference_type = std::ptrdiff_t; + // construction and assignment + template... Chars> + requires(sizeof...(Chars) == N) && (... && !std::is_pointer_v) + constexpr explicit basic_fixed_string(Chars... chars) noexcept : data_{chars..., CharT{}} + { + } + // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - constexpr explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept + consteval explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) { gsl_Expects(txt[N] == CharT{}); for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i]; @@ -72,98 +84,195 @@ struct basic_fixed_string { template S> requires std::convertible_to, CharT> - constexpr explicit basic_fixed_string(It first, S last) noexcept + constexpr explicit basic_fixed_string(It begin, S end) { - gsl_Expects(std::distance(first, last) == N); - for (auto it = data_; first != last; ++first, ++it) *it = *first; + gsl_Expects(std::distance(begin, end) == N); + for (auto it = data_; begin != end; ++begin, ++it) *it = *begin; } - template... Rest> - requires(1 + sizeof...(Rest) == N) - constexpr explicit basic_fixed_string(CharT first, Rest... rest) noexcept : data_{first, rest..., CharT{}} + template + requires std::convertible_to, CharT> + constexpr explicit basic_fixed_string(std::from_range_t, R&& r) { + gsl_Expects(std::ranges::size(r) == N); + for (auto it = data_; auto&& v : std::forward(r)) *it++ = std::forward(v); } - [[nodiscard]] constexpr bool empty() const noexcept { return N == 0; } - [[nodiscard]] constexpr size_type size() const noexcept { return N; } - [[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast(data_); } - [[nodiscard]] constexpr const CharT* c_str() const noexcept { return data(); } - [[nodiscard]] constexpr value_type operator[](size_type index) const noexcept - { - gsl_Expects(index < N); - return data()[index]; - } + constexpr basic_fixed_string(const basic_fixed_string&) noexcept = default; + constexpr basic_fixed_string& operator=(const basic_fixed_string&) noexcept = default; + // iterator support [[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); } - [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return data(); } [[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); } - [[nodiscard]] constexpr const_iterator cend() const noexcept { return data() + size(); } + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } + [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } - // NOLINTNEXTLINE(*-explicit-conversions, google-explicit-constructor) - [[nodiscard]] constexpr explicit(false) operator std::basic_string_view() const noexcept + // capacity + [[nodiscard]] constexpr size_type size() const noexcept { return N; } + [[nodiscard]] constexpr size_type length() const noexcept { return size(); } + [[nodiscard]] constexpr size_type max_size() const noexcept { return size(); } + [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } + + // element access + [[nodiscard]] constexpr const_reference operator[](size_type pos) const { - return std::basic_string_view(cbegin(), cend()); + gsl_Expects(pos < N); + return data()[pos]; + } + + [[nodiscard]] constexpr const_reference at(size_type pos) const + { + if (pos >= size()) throw std::out_of_range("basic_fixed_string::at"); + return (*this)[pos]; + } + [[nodiscard]] constexpr const_reference front() const + { + gsl_Expects(!empty()); + return (*this)[0]; + } + [[nodiscard]] constexpr const_reference back() const + { + gsl_Expects(!empty()); + return (*this)[N - 1]; + } + [[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast(data_); } + + // modifiers + constexpr void swap(basic_fixed_string& s) noexcept { std::swap_ranges(begin(), end(), s.begin()); } + + // string operations + [[nodiscard]] constexpr const_pointer c_str() const noexcept { return data(); } + [[nodiscard]] constexpr std::basic_string_view view() const noexcept + { + return std::basic_string_view(cbegin(), cend()); + } + // NOLINTNEXTLINE(*-explicit-conversions, google-explicit-constructor) + [[nodiscard]] constexpr explicit(false) operator std::basic_string_view() const noexcept + { + return view(); } template - [[nodiscard]] constexpr friend basic_fixed_string operator+( - const basic_fixed_string& lhs, const basic_fixed_string& rhs) noexcept + [[nodiscard]] constexpr friend basic_fixed_string operator+( + const basic_fixed_string& lhs, const basic_fixed_string& rhs) noexcept { - CharT txt[N + N2 + 1] = {}; - + CharT txt[N + N2] = {}; for (size_t i = 0; i != N; ++i) txt[i] = lhs[i]; for (size_t i = 0; i != N2; ++i) txt[N + i] = rhs[i]; - - return basic_fixed_string(txt); + return basic_fixed_string(std::begin(txt), std::end(txt)); } - [[nodiscard]] constexpr bool operator==(const basic_fixed_string&) const = default; - - template - [[nodiscard]] friend constexpr bool operator==(const basic_fixed_string&, const basic_fixed_string&) + [[nodiscard]] constexpr friend basic_fixed_string operator+(const basic_fixed_string& lhs, + CharT rhs) noexcept { - return false; + CharT txt[N + 1] = {}; + for (size_t i = 0; i != N; ++i) txt[i] = lhs[i]; + txt[N] = rhs; + return basic_fixed_string(std::begin(txt), std::end(txt)); + } + + [[nodiscard]] constexpr friend basic_fixed_string operator+( + const CharT lhs, const basic_fixed_string& rhs) noexcept + { + CharT txt[1 + N] = {lhs}; + for (size_t i = 0; i != N; ++i) txt[1 + i] = rhs[i]; + return basic_fixed_string(std::begin(txt), std::end(txt)); } template + [[nodiscard]] consteval friend basic_fixed_string operator+( + const basic_fixed_string& lhs, const CharT (&rhs)[N2]) noexcept + { + CharT txt[N + N2 - 1] = {}; + for (size_t i = 0; i != N; ++i) txt[i] = lhs[i]; + for (size_t i = 0; i != N2 - 1; ++i) txt[N + i] = rhs[i]; + return basic_fixed_string(std::begin(txt), std::end(txt)); + } + + template + [[nodiscard]] consteval friend basic_fixed_string operator+( + const CharT (&lhs)[N1], const basic_fixed_string& rhs) noexcept + { + CharT txt[N1 + N - 1] = {}; + for (size_t i = 0; i != N1 - 1; ++i) txt[i] = lhs[i]; + for (size_t i = 0; i != N; ++i) txt[N1 - 1 + i] = rhs[i]; + return basic_fixed_string(std::begin(txt), std::end(txt)); + } + + // non-member comparison functions + template + [[nodiscard]] friend constexpr bool operator==(const basic_fixed_string& lhs, + const basic_fixed_string& rhs) + { + return lhs.view() == rhs.view(); + } + template + [[nodiscard]] friend consteval bool operator==(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) + { + return lhs.view() == std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); + } + + template [[nodiscard]] friend constexpr auto operator<=>(const basic_fixed_string& lhs, - const basic_fixed_string& rhs) + const basic_fixed_string& rhs) { - // TODO std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - return detail::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + return lhs.view() <=> rhs.view(); + } + template + [[nodiscard]] friend consteval auto operator<=>(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) + { + return lhs.view() <=> std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); } - template + // inserters and extractors friend std::basic_ostream& operator<<(std::basic_ostream& os, - const basic_fixed_string& str) + const basic_fixed_string& str) { return os << str.c_str(); } }; +// deduction guides +template CharT, std::convertible_to... Rest> +basic_fixed_string(CharT, Rest...) -> basic_fixed_string; + template basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string; -template... Rest> -basic_fixed_string(CharT, Rest...) -> basic_fixed_string; +template CharT, std::size_t N> +basic_fixed_string(std::from_range_t, std::array) -> basic_fixed_string; +// typedef-names template using fixed_string = basic_fixed_string; - +template +using fixed_u8string = basic_fixed_string; +template +using fixed_u16string = basic_fixed_string; +template +using fixed_u32string = basic_fixed_string; template using fixed_wstring = basic_fixed_string; -template -using fixed_u8string = basic_fixed_string; - -template -using fixed_u16string = basic_fixed_string; - -template -using fixed_u32string = basic_fixed_string; - } // namespace mp_units +// hash support +template +struct std::hash> : std::hash {}; +template +struct std::hash> : std::hash {}; +template +struct std::hash> : std::hash {}; +template +struct std::hash> : std::hash {}; +template +struct std::hash> : std::hash {}; + +// formatting support template struct MP_UNITS_STD_FMT::formatter> : formatter> { template diff --git a/src/core/include/mp-units/framework/dimension.h b/src/core/include/mp-units/framework/dimension.h index 770194b9..a708afc2 100644 --- a/src/core/include/mp-units/framework/dimension.h +++ b/src/core/include/mp-units/framework/dimension.h @@ -328,8 +328,7 @@ MP_UNITS_EXPORT template(D{}); - return basic_fixed_string(buffer.begin(), buffer.end()); + return basic_fixed_string(std::from_range, detail::get_symbol_buffer(D{})); #endif } diff --git a/src/core/include/mp-units/framework/symbol_text.h b/src/core/include/mp-units/framework/symbol_text.h index d36aa082..f7c3efaa 100644 --- a/src/core/include/mp-units/framework/symbol_text.h +++ b/src/core/include/mp-units/framework/symbol_text.h @@ -24,8 +24,10 @@ #pragma once // IWYU pragma: private, include +// TODO use when moved to C++20 modules (parsing takes too long for each translation unit) #include #include +#include #include #ifndef MP_UNITS_IN_MODULE_INTERFACE @@ -85,7 +87,8 @@ constexpr fixed_u8string to_u8string(fixed_string txt) * @tparam M The size of the ASCII-only symbol */ MP_UNITS_EXPORT template -struct symbol_text { +class symbol_text { +public: fixed_u8string unicode_; fixed_string ascii_; @@ -96,7 +99,7 @@ struct symbol_text { } // NOLINTNEXTLINE(*-avoid-c-arrays, google-explicit-constructor, hicpp-explicit-conversions) - constexpr explicit(false) symbol_text(const char (&txt)[N + 1]) : + consteval explicit(false) symbol_text(const char (&txt)[N + 1]) : unicode_(detail::to_u8string(basic_fixed_string{txt})), ascii_(txt) { gsl_Expects(txt[N] == char{}); @@ -110,7 +113,7 @@ struct symbol_text { } // NOLINTNEXTLINE(*-avoid-c-arrays) - constexpr symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a) + consteval symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a) { gsl_Expects(u[N] == char8_t{}); gsl_Expects(a[M] == char{}); diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index ce9484a2..8e8ad5df 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -854,8 +854,7 @@ MP_UNITS_EXPORT template(U{}); - return basic_fixed_string(buffer.begin(), buffer.end()); + return basic_fixed_string(std::from_range, detail::get_symbol_buffer(U{})); #endif } diff --git a/test/runtime/CMakeLists.txt b/test/runtime/CMakeLists.txt index f78c0ad4..fcd2cca8 100644 --- a/test/runtime/CMakeLists.txt +++ b/test/runtime/CMakeLists.txt @@ -24,7 +24,9 @@ cmake_minimum_required(VERSION 3.5) find_package(Catch2 3 REQUIRED) -add_executable(unit_tests_runtime distribution_test.cpp fmt_test.cpp math_test.cpp atomic_test.cpp) +add_executable( + unit_tests_runtime distribution_test.cpp fixed_string_test.cpp fmt_test.cpp math_test.cpp atomic_test.cpp +) if(${projectPrefix}BUILD_CXX_MODULES) target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES) endif() diff --git a/test/runtime/fixed_string_test.cpp b/test/runtime/fixed_string_test.cpp new file mode 100644 index 00000000..706e8b17 --- /dev/null +++ b/test/runtime/fixed_string_test.cpp @@ -0,0 +1,67 @@ +// 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 +#include +#include +#ifdef MP_UNITS_MODULES +import mp_units; +#else +#include +#endif + +using namespace mp_units; + +TEST_CASE("fixed_string::at", "[fixed_string]") +{ + basic_fixed_string txt = "abc"; + SECTION("in range") + { + CHECK(txt.at(0) == 'a'); + CHECK(txt.at(1) == 'b'); + CHECK(txt.at(2) == 'c'); + } + SECTION("out of range") + { + REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at")); + REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at")); + } +} + +TEST_CASE("fixed_string text output", "[fixed_string][ostream][fmt]") +{ + basic_fixed_string txt = "units"; + SECTION("iostream") + { + std::ostringstream os; + os << txt; + CHECK(os.str() == "units"); + } + SECTION("fmt") { CHECK(MP_UNITS_STD_FMT::format("{}", txt) == "units"); } +} + +TEST_CASE("fixed_string hash", "[fixed_string][hash]") +{ + basic_fixed_string txt = "units"; + CHECK(std::hash>{}(txt) == std::hash{}("units")); +} diff --git a/test/runtime/fmt_test.cpp b/test/runtime/fmt_test.cpp index e8437438..8c26ee28 100644 --- a/test/runtime/fmt_test.cpp +++ b/test/runtime/fmt_test.cpp @@ -34,7 +34,6 @@ #ifdef MP_UNITS_MODULES import mp_units; #else -#include #include #include // IWYU pragma: keep #include @@ -51,18 +50,6 @@ inline constexpr bool mp_units::is_vector = true; using namespace mp_units; using namespace mp_units::si::unit_symbols; -TEST_CASE("fixed_string", "[text][ostream][fmt]") -{ - basic_fixed_string txt = "units"; - SECTION("iostream") - { - std::ostringstream os; - os << txt; - CHECK(os.str() == "units"); - } - SECTION("fmt") { CHECK(MP_UNITS_STD_FMT::format("{}", txt) == "units"); } -} - TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { std::ostringstream os; diff --git a/test/static/fixed_string_test.cpp b/test/static/fixed_string_test.cpp index 93890a98..87ca7660 100644 --- a/test/static/fixed_string_test.cpp +++ b/test/static/fixed_string_test.cpp @@ -27,36 +27,152 @@ using namespace mp_units; namespace { +constexpr std::array array = {'a', 'b', 'c'}; + +auto from_string = [] { + std::string txt = "abc"; + return fixed_string<3>(std::from_range, txt); +}; + +auto from_string_iter = [] { + std::string txt = "abc"; + return fixed_string<3>(txt.begin(), txt.end()); +}; + +constexpr fixed_string<0> txt0; constexpr basic_fixed_string txt1('a'); +constexpr basic_fixed_string txt2('a', 'b', 'c'); +constexpr basic_fixed_string txt3 = "abc"; +constexpr fixed_string<3> txt4(array.begin(), array.end()); +constexpr basic_fixed_string txt5(std::from_range, array); +constexpr basic_fixed_string txt6(from_string()); +constexpr basic_fixed_string txt7(from_string_iter()); + +constexpr fixed_string<3> txt8(txt2.begin(), txt2.end()); +constexpr fixed_string<3> txt9(txt2.rbegin(), txt2.rend()); + +static_assert(txt0.size() == 0); static_assert(txt1.size() == 1); +static_assert(txt2.size() == 3); +static_assert(txt3.size() == 3); +static_assert(txt4.size() == 3); +static_assert(txt5.size() == 3); +static_assert(txt6.size() == 3); +static_assert(txt7.size() == 3); +static_assert(txt8.size() == 3); +static_assert(txt9.size() == 3); + +static_assert(txt0.length() == 0); +static_assert(txt1.length() == 1); +static_assert(txt2.length() == 3); + +static_assert(txt0.max_size() == 0); +static_assert(txt1.max_size() == 1); +static_assert(txt2.max_size() == 3); + +static_assert(txt0.empty() == true); +static_assert(txt1.empty() == false); +static_assert(txt2.empty() == false); +static_assert(txt3.empty() == false); +static_assert(txt4.empty() == false); +static_assert(txt5.empty() == false); +static_assert(txt6.empty() == false); +static_assert(txt7.empty() == false); +static_assert(txt8.empty() == false); +static_assert(txt9.empty() == false); + static_assert(txt1[0] == 'a'); +static_assert(txt2[0] == 'a'); +static_assert(txt2[1] == 'b'); +static_assert(txt2[2] == 'c'); +static_assert(txt9[0] == 'c'); +static_assert(txt9[1] == 'b'); +static_assert(txt9[2] == 'a'); + +static_assert(txt1.at(0) == 'a'); +static_assert(txt2.at(0) == 'a'); +static_assert(txt2.at(1) == 'b'); +static_assert(txt2.at(2) == 'c'); +static_assert(txt9.at(0) == 'c'); +static_assert(txt9.at(1) == 'b'); +static_assert(txt9.at(2) == 'a'); + +static_assert(txt1.front() == 'a'); +static_assert(txt1.back() == 'a'); +static_assert(txt2.front() == 'a'); +static_assert(txt2.back() == 'c'); +static_assert(txt5.front() == 'a'); +static_assert(txt5.back() == 'c'); +static_assert(txt6.front() == 'a'); +static_assert(txt6.back() == 'c'); +static_assert(txt7.front() == 'a'); +static_assert(txt7.back() == 'c'); +static_assert(txt8.front() == 'a'); +static_assert(txt8.back() == 'c'); +static_assert(txt9.front() == 'c'); +static_assert(txt9.back() == 'a'); + +static_assert(std::string_view(txt0.data()) == ""); +static_assert(std::string_view(txt0.c_str()) == ""); +static_assert(std::string_view(txt1.data()) == "a"); +static_assert(std::string_view(txt1.c_str()) == "a"); +static_assert(std::string_view(txt2.data()) == "abc"); +static_assert(std::string_view(txt2.c_str()) == "abc"); + +static_assert(txt0 == ""); +static_assert("a" == txt1); +static_assert(txt2 == "abc"); +static_assert(txt3 == "abc"); +static_assert(txt4 == "abc"); +static_assert(txt5 == "abc"); +static_assert(txt6 == "abc"); +static_assert(txt7 == "abc"); +static_assert(txt8 == "abc"); +static_assert(txt9 == "cba"); + static_assert(txt1 == basic_fixed_string("a")); static_assert(txt1 != basic_fixed_string("b")); static_assert(txt1 != basic_fixed_string("aa")); static_assert(txt1 < basic_fixed_string("b")); static_assert(txt1 < basic_fixed_string("aa")); -static_assert(txt1 + basic_fixed_string('b') == basic_fixed_string("ab")); -static_assert(basic_fixed_string('b') + txt1 == basic_fixed_string("ba")); -static_assert(txt1 + basic_fixed_string("bc") == basic_fixed_string("abc")); -static_assert(basic_fixed_string("bc") + txt1 == basic_fixed_string("bca")); +static_assert(txt1 == "a"); +static_assert(txt1 != "b"); +static_assert(txt1 != "aa"); +static_assert(txt1 < "b"); +static_assert(txt1 < "aa"); + +static_assert(txt1 + basic_fixed_string('b') == "ab"); +static_assert(basic_fixed_string('b') + txt1 == "ba"); +static_assert(txt1 + basic_fixed_string("bc") == "abc"); +static_assert(basic_fixed_string("bc") + txt1 == "bca"); +static_assert(txt1 + 'b' == "ab"); +static_assert('b' + txt1 == "ba"); +static_assert(txt1 + "bc" == "abc"); +static_assert("bc" + txt1 == "bca"); -constexpr basic_fixed_string txt2("abc"); -static_assert(txt2.size() == 3); -static_assert(txt2[0] == 'a'); -static_assert(txt2[1] == 'b'); -static_assert(txt2[2] == 'c'); static_assert(txt2 == basic_fixed_string("abc")); static_assert(txt2 != basic_fixed_string("cba")); static_assert(txt2 != basic_fixed_string("abcd")); static_assert(txt2 < basic_fixed_string("b")); static_assert(txt2 > basic_fixed_string("aa")); -static_assert(txt2 + basic_fixed_string('d') == basic_fixed_string("abcd")); -static_assert(basic_fixed_string('d') + txt2 == basic_fixed_string("dabc")); -static_assert(txt2 + basic_fixed_string("def") == basic_fixed_string("abcdef")); -static_assert(basic_fixed_string("def") + txt2 == basic_fixed_string("defabc")); +static_assert(txt2 == "abc"); +static_assert(txt2 != "cba"); +static_assert(txt2 != "abcd"); +static_assert(txt2 < "b"); +static_assert(txt2 > "aa"); -#ifndef MP_UNITS_COMP_GCC -static_assert(std::string_view(basic_fixed_string("abcd")).find('c') == 2); -#endif +static_assert(txt2 + basic_fixed_string('d') == "abcd"); +static_assert(basic_fixed_string('d') + txt2 == "dabc"); +static_assert(txt2 + basic_fixed_string("def") == "abcdef"); +static_assert(basic_fixed_string("def") + txt2 == "defabc"); +static_assert(txt2 + 'd' == "abcd"); +static_assert('d' + txt2 == "dabc"); +static_assert(txt2 + "def" == "abcdef"); +static_assert("def" + txt2 == "defabc"); + +static_assert(std::string_view(txt2) == "abc"); +static_assert(txt2.view() == "abc"); +static_assert(std::string_view(txt2).find('b') == 1); +static_assert(txt2.view().find('b') == 1); } // namespace From ecb39d79c07a2f0fc035da5cbfa35dba7b12f96f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 12:31:30 +0200 Subject: [PATCH 04/61] fix: missing headers added to `quantity_spec.h` --- src/core/include/mp-units/framework/quantity_spec.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 1ff3dc1d..78d039c1 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -38,8 +38,10 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include #include #include +#include #endif namespace mp_units { From 5dc21fd29b71bf0afaa209a0f0f9d27f0cc8380b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 12:33:28 +0200 Subject: [PATCH 05/61] refactor: `zeroth_degree_Fahrenheit` definition simplified --- docs/users_guide/framework_basics/the_affine_space.md | 2 +- src/systems/include/mp-units/systems/usc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index 62c00efb..c8a48541 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -403,7 +403,7 @@ inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_de namespace usc { inline constexpr struct zeroth_degree_Fahrenheit : - relative_point_origin * si::degree_Celsius)> {} zeroth_degree_Fahrenheit; + relative_point_origin * si::degree_Celsius)}> {} zeroth_degree_Fahrenheit; } ``` diff --git a/src/systems/include/mp-units/systems/usc.h b/src/systems/include/mp-units/systems/usc.h index 9a13c702..cfa2eb76 100644 --- a/src/systems/include/mp-units/systems/usc.h +++ b/src/systems/include/mp-units/systems/usc.h @@ -118,7 +118,7 @@ inline constexpr struct troy_pound : named_unit<"lb t", mag<12> * troy_once> {} inline constexpr struct inch_of_mercury : named_unit<"inHg", mag_ratio<3'386'389, 1'000> * si::pascal> {} inch_of_mercury; // https://en.wikipedia.org/wiki/United_States_customary_units#Temperature -inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin * si::degree_Celsius)> {} zeroth_degree_Fahrenheit; +inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin * si::degree_Celsius)}> {} zeroth_degree_Fahrenheit; inline constexpr struct degree_Fahrenheit : named_unit * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_Fahrenheit; // clang-format on From f4e44651a93950bfb89d0af74c4ee749c131fddb Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 12:35:49 +0200 Subject: [PATCH 06/61] docs: fmtlib dependency removed from the Compiler Explorer code --- README.md | 2 +- docs/getting_started/look_and_feel.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 313f6065..dd6ea350 100644 --- a/README.md +++ b/README.md @@ -130,4 +130,4 @@ int main() } ``` -_Try it on the [Compiler Explorer](https://godbolt.org/z/fsdovTcYh)._ +_Try it on the [Compiler Explorer](https://godbolt.org/z/nhqhT8Mzb)._ diff --git a/docs/getting_started/look_and_feel.md b/docs/getting_started/look_and_feel.md index b87d5470..a8bfb824 100644 --- a/docs/getting_started/look_and_feel.md +++ b/docs/getting_started/look_and_feel.md @@ -150,7 +150,7 @@ performed without sacrificing accuracy. Please see the below example for a quick } ``` -!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/fsdovTcYh)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/nhqhT8Mzb)" !!! note From baf77c25812eca68cdfba4f53850c26185ba981c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 13:04:44 +0200 Subject: [PATCH 07/61] ci: Ubuntu machines upgraded to 24.04 --- .github/workflows/ci-conan.yml | 12 ++++++------ .github/workflows/ci-formatting.yml | 2 +- .github/workflows/ci-test-package-cmake.yml | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index b6802f35..ed674691 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -57,7 +57,7 @@ jobs: # } - { name: "GCC-12", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: GCC, @@ -71,7 +71,7 @@ jobs: } - { name: "GCC-13", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: GCC, @@ -85,7 +85,7 @@ jobs: } - { name: "Clang-16", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: CLANG, @@ -100,7 +100,7 @@ jobs: } - { name: "Clang-17", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: CLANG, @@ -115,7 +115,7 @@ jobs: } - { name: "Clang-18", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: CLANG, @@ -254,7 +254,7 @@ jobs: if: github.ref == 'refs/heads/master' || (github.ref_type == 'tag' && startsWith(github.ref_name, 'v')) needs: build name: Promote Conan package - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Set up Python diff --git a/.github/workflows/ci-formatting.yml b/.github/workflows/ci-formatting.yml index ad053fb7..06114560 100644 --- a/.github/workflows/ci-formatting.yml +++ b/.github/workflows/ci-formatting.yml @@ -26,7 +26,7 @@ on: [push, pull_request] jobs: check: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Set up Python 3.8 diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index e822d030..619ffcf2 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -56,7 +56,7 @@ jobs: # } - { name: "GCC-12", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: GCC, @@ -69,7 +69,7 @@ jobs: } - { name: "GCC-13", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: GCC, @@ -82,7 +82,7 @@ jobs: } - { name: "Clang-16", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: CLANG, @@ -96,7 +96,7 @@ jobs: } - { name: "Clang-17", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: CLANG, @@ -110,7 +110,7 @@ jobs: } - { name: "Clang-18", - os: ubuntu-22.04, + os: ubuntu-24.04, compiler: { type: CLANG, From 2a5d5c4ebf3a1d88ef09b9f3b6b0a392030653cf Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 13:06:34 +0200 Subject: [PATCH 08/61] fix: missing `sstream` header added to fixed_string_test --- test/runtime/fixed_string_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/runtime/fixed_string_test.cpp b/test/runtime/fixed_string_test.cpp index 706e8b17..7efae3df 100644 --- a/test/runtime/fixed_string_test.cpp +++ b/test/runtime/fixed_string_test.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #ifdef MP_UNITS_MODULES import mp_units; From a7cfa60f5638635a8528ac20d83505dadc14aeb6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 13:15:05 +0200 Subject: [PATCH 09/61] ci: python version changed to 3.x --- .github/workflows/ci-conan.yml | 4 ++-- .github/workflows/ci-formatting.yml | 4 ++-- .github/workflows/ci-test-package-cmake.yml | 2 +- .github/workflows/codeql.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index ed674691..1c295b80 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -202,7 +202,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: 3.x - name: Install Ninja shell: bash run: | @@ -260,7 +260,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: 3.x - name: Install Conan shell: bash run: | diff --git a/.github/workflows/ci-formatting.yml b/.github/workflows/ci-formatting.yml index 06114560..086fe625 100644 --- a/.github/workflows/ci-formatting.yml +++ b/.github/workflows/ci-formatting.yml @@ -29,10 +29,10 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: 3.x - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 619ffcf2..10f3c180 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -195,7 +195,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: 3.x - name: Install Ninja shell: bash run: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d9fdc3c7..eb45fb53 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -97,7 +97,7 @@ jobs: if: matrix.language == 'cpp' uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: 3.x - name: Conan build if: matrix.language == 'cpp' run: | From af5f62f6ced1cd3e061d10f48886f2ee5985eba2 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 13:24:12 +0200 Subject: [PATCH 10/61] ci: clang-16 reverted to ubuntu-22.04 --- .github/workflows/ci-conan.yml | 2 +- .github/workflows/ci-test-package-cmake.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 1c295b80..df0882e0 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -85,7 +85,7 @@ jobs: } - { name: "Clang-16", - os: ubuntu-24.04, + os: ubuntu-22.04, compiler: { type: CLANG, diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 10f3c180..81d81e42 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -82,7 +82,7 @@ jobs: } - { name: "Clang-16", - os: ubuntu-24.04, + os: ubuntu-22.04, compiler: { type: CLANG, From 96f9b5b714bba74280012fafdc892c5fcf0436d5 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 13:26:47 +0200 Subject: [PATCH 11/61] ci: gcc-14 added --- .github/workflows/ci-conan.yml | 14 ++++++++++++++ .github/workflows/ci-test-package-cmake.yml | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index df0882e0..6df32736 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -83,6 +83,20 @@ jobs: std_format_support: "True", conan-config: "", } + - { + name: "GCC-14", + os: ubuntu-24.04, + compiler: + { + type: GCC, + version: 14, + cc: "gcc-14", + cxx: "g++-14", + }, + cxx_modules: "True", + std_format_support: "True", + conan-config: "", + } - { name: "Clang-16", os: ubuntu-22.04, diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 81d81e42..ff460377 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -80,6 +80,20 @@ jobs: cxx_modules: "False", std_format_support: "True", } + - { + name: "GCC-14", + os: ubuntu-24.04, + compiler: + { + type: GCC, + version: 14, + cc: "gcc-14", + cxx: "g++-14", + }, + cxx_modules: "True", + std_format_support: "True", + conan-config: "", + } - { name: "Clang-16", os: ubuntu-22.04, From 72b6fa0fcf965ae2277fa38c43d28f8e826ed057 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 13:46:43 +0200 Subject: [PATCH 12/61] ci: C++ modules disabled for gcc-14 --- .github/workflows/ci-conan.yml | 2 +- .github/workflows/ci-test-package-cmake.yml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 6df32736..03768356 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -93,7 +93,7 @@ jobs: cc: "gcc-14", cxx: "g++-14", }, - cxx_modules: "True", + cxx_modules: "False", std_format_support: "True", conan-config: "", } diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index ff460377..731f9c55 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -90,9 +90,8 @@ jobs: cc: "gcc-14", cxx: "g++-14", }, - cxx_modules: "True", - std_format_support: "True", - conan-config: "", + cxx_modules: "False", + std_format_support: "True" } - { name: "Clang-16", From 430b37e108bd2151a06c8dda786399ff66f6644f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 14:06:33 +0200 Subject: [PATCH 13/61] build: `-Wno-subobject-linkage` added for GCC compilation of static unit tests --- test/static/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/static/CMakeLists.txt b/test/static/CMakeLists.txt index 21fd9711..4e5a709e 100644 --- a/test/static/CMakeLists.txt +++ b/test/static/CMakeLists.txt @@ -64,6 +64,6 @@ add_library( unit_symbol_test.cpp usc_test.cpp ) - +target_compile_options(unit_tests_static PRIVATE $<$:-Wno-subobject-linkage>) target_link_libraries(unit_tests_static PRIVATE mp-units::mp-units) target_link_libraries(unit_tests_static PRIVATE unit_tests_static_truncating) From b9614ba8a106922afe6b5382e5aa05a0dd4f154c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 22:25:30 +0200 Subject: [PATCH 14/61] docs: 2.1.1 release added to CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index efe2a403..ce89ea18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,11 @@ - build(conan): `generate()` now set `cache_variables` - build(conan): `can_run` check added before running tests +### 2.1.1 May 16, 2024 { id="2.1.1" } + +- fix: unit tests compilation on gcc-14 fixed +- fix: explicit `this` parameter support fixed + ### 2.1.0 December 9, 2023 { id="2.1.0" } - (!) feat: `inverse()` support added for dimensions, quantity_spec, units, and references From c9382ecbdaa1d34ef2f847aee716bcc36041c3c4 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 16 May 2024 22:26:35 +0200 Subject: [PATCH 15/61] ci: macos upgraded to version 14 --- .github/workflows/ci-conan.yml | 2 +- .github/workflows/ci-test-package-cmake.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 03768356..abf0bd27 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -144,7 +144,7 @@ jobs: } - { name: "Apple Clang 15", - os: macos-13, + os: macos-14, compiler: { type: APPLE_CLANG, diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 731f9c55..4dc8e9ff 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -137,7 +137,7 @@ jobs: } - { name: "Apple Clang 15", - os: macos-13, + os: macos-14, compiler: { type: APPLE_CLANG, From 20b08480317487ad60b356a5fad5fcf51cb82ce9 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 21 May 2024 12:11:00 +0200 Subject: [PATCH 16/61] refactor: 2-parameters `fixed_string` constructors are not explicit anymore --- src/core/include/mp-units/ext/fixed_string.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 80165c3f..1301a868 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -84,7 +84,7 @@ public: template S> requires std::convertible_to, CharT> - constexpr explicit basic_fixed_string(It begin, S end) + constexpr basic_fixed_string(It begin, S end) { gsl_Expects(std::distance(begin, end) == N); for (auto it = data_; begin != end; ++begin, ++it) *it = *begin; @@ -92,7 +92,7 @@ public: template requires std::convertible_to, CharT> - constexpr explicit basic_fixed_string(std::from_range_t, R&& r) + constexpr basic_fixed_string(std::from_range_t, R&& r) { gsl_Expects(std::ranges::size(r) == N); for (auto it = data_; auto&& v : std::forward(r)) *it++ = std::forward(v); From 7322d9a10fe9fdf45552b6e59150b9196dbabb49 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 21 May 2024 12:11:43 +0200 Subject: [PATCH 17/61] refactor: `std::simd`-like `integral_constant` usage added to member functions --- src/core/include/mp-units/ext/fixed_string.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 1301a868..2b16d51b 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -112,10 +112,10 @@ public: [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } // capacity - [[nodiscard]] constexpr size_type size() const noexcept { return N; } - [[nodiscard]] constexpr size_type length() const noexcept { return size(); } - [[nodiscard]] constexpr size_type max_size() const noexcept { return size(); } - [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } + [[nodiscard]] static constexpr std::integral_constant size() noexcept { return {}; } + [[nodiscard]] static constexpr std::integral_constant length() noexcept { return {}; } + [[nodiscard]] static constexpr std::integral_constant max_size() noexcept { return {}; } + [[nodiscard]] static constexpr std::bool_constant empty() noexcept { return {}; } // element access [[nodiscard]] constexpr const_reference operator[](size_type pos) const From cb7cbf4cb3e3a12554a3c9dc1d291daab6bcf50b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 22 May 2024 12:18:48 +0200 Subject: [PATCH 18/61] refactor: `fixed_string` refactoring to match the R2 specs --- src/core/include/mp-units/ext/fixed_string.h | 53 ++++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 2b16d51b..05c12f4e 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -76,7 +76,7 @@ public: } // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - consteval explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) + consteval explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept { gsl_Expects(txt[N] == CharT{}); for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i]; @@ -139,13 +139,13 @@ public: gsl_Expects(!empty()); return (*this)[N - 1]; } - [[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast(data_); } // modifiers constexpr void swap(basic_fixed_string& s) noexcept { std::swap_ranges(begin(), end(), s.begin()); } // string operations [[nodiscard]] constexpr const_pointer c_str() const noexcept { return data(); } + [[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast(data_); } [[nodiscard]] constexpr std::basic_string_view view() const noexcept { return std::basic_string_view(cbegin(), cend()); @@ -160,47 +160,56 @@ public: [[nodiscard]] constexpr friend basic_fixed_string operator+( const basic_fixed_string& lhs, const basic_fixed_string& rhs) noexcept { - CharT txt[N + N2] = {}; - for (size_t i = 0; i != N; ++i) txt[i] = lhs[i]; - for (size_t i = 0; i != N2; ++i) txt[N + i] = rhs[i]; - return basic_fixed_string(std::begin(txt), std::end(txt)); + CharT txt[N + N2]; + CharT* it = txt; + for (CharT c : lhs) *it++ = c; + for (CharT c : rhs) *it++ = c; + return basic_fixed_string(txt, it); } [[nodiscard]] constexpr friend basic_fixed_string operator+(const basic_fixed_string& lhs, CharT rhs) noexcept { - CharT txt[N + 1] = {}; - for (size_t i = 0; i != N; ++i) txt[i] = lhs[i]; - txt[N] = rhs; - return basic_fixed_string(std::begin(txt), std::end(txt)); + CharT txt[N + 1]; + CharT* it = txt; + for (CharT c : lhs) *it++ = c; + *it++ = rhs; + return basic_fixed_string(txt, it); } [[nodiscard]] constexpr friend basic_fixed_string operator+( const CharT lhs, const basic_fixed_string& rhs) noexcept { - CharT txt[1 + N] = {lhs}; - for (size_t i = 0; i != N; ++i) txt[1 + i] = rhs[i]; - return basic_fixed_string(std::begin(txt), std::end(txt)); + CharT txt[1 + N]; + CharT* it = txt; + *it++ = lhs; + for (CharT c : rhs) *it++ = c; + return basic_fixed_string(txt, it); } template [[nodiscard]] consteval friend basic_fixed_string operator+( const basic_fixed_string& lhs, const CharT (&rhs)[N2]) noexcept { - CharT txt[N + N2 - 1] = {}; - for (size_t i = 0; i != N; ++i) txt[i] = lhs[i]; - for (size_t i = 0; i != N2 - 1; ++i) txt[N + i] = rhs[i]; - return basic_fixed_string(std::begin(txt), std::end(txt)); + gsl_Expects(rhs[N2 - 1] == CharT{}); + CharT txt[N + N2]; + CharT* it = txt; + for (CharT c : lhs) *it++ = c; + for (CharT c : rhs) *it++ = c; + return txt; } template [[nodiscard]] consteval friend basic_fixed_string operator+( const CharT (&lhs)[N1], const basic_fixed_string& rhs) noexcept { - CharT txt[N1 + N - 1] = {}; - for (size_t i = 0; i != N1 - 1; ++i) txt[i] = lhs[i]; - for (size_t i = 0; i != N; ++i) txt[N1 - 1 + i] = rhs[i]; - return basic_fixed_string(std::begin(txt), std::end(txt)); + gsl_Expects(lhs[N1 - 1] == CharT{}); + CharT txt[N1 + N]; + CharT* it = txt; + for (size_t i = 0; i != N1 - 1; ++i) *it++ = lhs[i]; + for (CharT c : rhs) *it++ = c; + *it++ = CharT(); + return txt; } // non-member comparison functions @@ -213,6 +222,7 @@ public: template [[nodiscard]] friend consteval bool operator==(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) { + gsl_Expects(rhs[N2 - 1] == CharT{}); return lhs.view() == std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); } @@ -225,6 +235,7 @@ public: template [[nodiscard]] friend consteval auto operator<=>(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) { + gsl_Expects(rhs[N2 - 1] == CharT{}); return lhs.view() <=> std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); } From f227cdd227da1ca1cb8d126f1af93ae73d85dd9a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 24 May 2024 09:36:29 +0200 Subject: [PATCH 19/61] build: `tc.absolute_paths = True` set to enable CMake CI --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index dc57a1d0..b4ca324d 100644 --- a/conanfile.py +++ b/conanfile.py @@ -221,6 +221,7 @@ class MPUnitsConan(ConanFile): def generate(self): tc = CMakeToolchain(self) if self._build_all: + tc.absolute_paths = True # only needed for CMake CI tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = True tc.cache_variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = True tc.cache_variables["MP_UNITS_DEV_BUILD_LA"] = not self._skip_la From 30a23b4c21e664ea815ad717342338bb8a82c691 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 24 May 2024 09:39:15 +0200 Subject: [PATCH 20/61] build: `tc.absolute_paths` moved out from `build_all` --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index b4ca324d..9f51a130 100644 --- a/conanfile.py +++ b/conanfile.py @@ -220,8 +220,8 @@ class MPUnitsConan(ConanFile): def generate(self): tc = CMakeToolchain(self) + tc.absolute_paths = True # only needed for CMake CI if self._build_all: - tc.absolute_paths = True # only needed for CMake CI tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = True tc.cache_variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = True tc.cache_variables["MP_UNITS_DEV_BUILD_LA"] = not self._skip_la From 5ef36dcf4e7bff3ac6916993c44727664be5b9b2 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 24 May 2024 10:03:38 +0200 Subject: [PATCH 21/61] ci: `ASzc/change-string-case-action` updated to v6 --- .github/workflows/ci-test-package-cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 4dc8e9ff..98abe43c 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -160,7 +160,7 @@ jobs: steps: - name: Downcase 'build_type' id: build_type - uses: ASzc/change-string-case-action@v5 + uses: ASzc/change-string-case-action@v6 with: string: ${{ matrix.build_type }} - uses: actions/checkout@v4 From 602a60999547ed13134232b54f041bfac6ccc35b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 24 May 2024 10:05:27 +0200 Subject: [PATCH 22/61] ci: check if floating-point issue exists on macos-13 as well --- .github/workflows/ci-conan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index abf0bd27..03768356 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -144,7 +144,7 @@ jobs: } - { name: "Apple Clang 15", - os: macos-14, + os: macos-13, compiler: { type: APPLE_CLANG, From 411d7d5c50f18bb2db21d13c1328a0c45bbcedaa Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 28 May 2024 09:41:25 +0200 Subject: [PATCH 23/61] feat: custom implementation of `swap_ranges` added --- src/core/include/mp-units/ext/algorithm.h | 14 ++++++++++++++ src/core/include/mp-units/ext/fixed_string.h | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/include/mp-units/ext/algorithm.h b/src/core/include/mp-units/ext/algorithm.h index 3aa460b8..4e9f77c0 100644 --- a/src/core/include/mp-units/ext/algorithm.h +++ b/src/core/include/mp-units/ext/algorithm.h @@ -131,4 +131,18 @@ constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first) return d_first; } +template +constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) +{ + using std::swap; + swap(*a, *b); +} + +template +constexpr ForwardIt2 swap_ranges(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2) +{ + for (; first1 != last1; ++first1, ++first2) iter_swap(first1, first2); + return first2; +} + } // namespace mp_units::detail diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 05c12f4e..1b1d13cf 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -141,7 +141,7 @@ public: } // modifiers - constexpr void swap(basic_fixed_string& s) noexcept { std::swap_ranges(begin(), end(), s.begin()); } + constexpr void swap(basic_fixed_string& s) noexcept { swap_ranges(begin(), end(), s.begin()); } // string operations [[nodiscard]] constexpr const_pointer c_str() const noexcept { return data(); } From da0ab13b0d997f289d8763abd1ee24554dcceee3 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 29 May 2024 19:54:53 +0200 Subject: [PATCH 24/61] feat: `fma` for quantity points added --- src/core/include/mp-units/math.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index e6957201..c93883c6 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -237,6 +237,29 @@ template common_reference(R * S, T)}; } +/** + * @brief Computes the fma of 2 quantities and a quantity point + * + * @param a: Multiplicand + * @param x: Multiplicand + * @param b: Addend + * @return QuantityPoint: The nearest floating point representable to ax+b + */ +template + requires requires { common_quantity_spec(get_quantity_spec(R) * get_quantity_spec(S), get_quantity_spec(T)); } && + (get_unit(R) * get_unit(S) == get_unit(T)) && requires(Rep1 v1, Rep2 v2, Rep3 v3) { + requires requires { fma(v1, v2, v3); } || requires { std::fma(v1, v2, v3); }; + } +[[nodiscard]] constexpr QuantityPointOf< + common_quantity_spec(get_quantity_spec(R) * get_quantity_spec(S), + get_quantity_spec(T))> auto fma(const quantity& a, const quantity& x, + const quantity_point& b) noexcept +{ + using std::fma; + return Origin + quantity{fma(a.numerical_value_ref_in(a.unit), x.numerical_value_ref_in(x.unit), + b.quantity_ref_from(b.point_origin).numerical_value_ref_in(b.unit)), + common_reference(R * S, T)}; +} /** * @brief Computes the floating-point remainder of the division operation x / y. From 1cb86a227110cddd7816afc1210f9f8f3de09f08 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 29 May 2024 20:25:21 +0200 Subject: [PATCH 25/61] feat(example): kalman filter examples enabled after text formatting refactoring --- example/CMakeLists.txt | 2 +- example/kalman_filter/kalman.h | 354 +++++++++++------- .../kalman_filter/kalman_filter-example_1.cpp | 33 +- .../kalman_filter/kalman_filter-example_2.cpp | 28 +- .../kalman_filter/kalman_filter-example_3.cpp | 30 +- .../kalman_filter/kalman_filter-example_4.cpp | 31 +- .../kalman_filter/kalman_filter-example_5.cpp | 50 +-- .../kalman_filter/kalman_filter-example_6.cpp | 61 ++- .../kalman_filter/kalman_filter-example_7.cpp | 61 ++- .../kalman_filter/kalman_filter-example_8.cpp | 61 ++- src/core/include/mp-units/bits/fmt.h | 39 +- src/core/include/mp-units/format.h | 22 +- 12 files changed, 429 insertions(+), 343 deletions(-) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 9e08103d..36364f12 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -72,4 +72,4 @@ add_example(total_energy) add_example(unmanned_aerial_vehicle example_utils) add_subdirectory(glide_computer_lib) -# add_subdirectory(kalman_filter) +add_subdirectory(kalman_filter) diff --git a/example/kalman_filter/kalman.h b/example/kalman_filter/kalman.h index 9ae5b462..bcf19646 100644 --- a/example/kalman_filter/kalman.h +++ b/example/kalman_filter/kalman.h @@ -23,21 +23,22 @@ #pragma once #include +#include #include #ifdef MP_UNITS_MODULES import mp_units; #else +#include #include +#include +#include #include -#include -#include -#include +#include #endif namespace kalman { -template -concept QuantityOrQuantityPoint = mp_units::Quantity || mp_units::QuantityPoint; +namespace detail { template inline constexpr bool are_time_derivatives = false; @@ -49,192 +50,261 @@ template = (D1 / D2 == mp_units::isq::dim_time) && are_time_derivatives; -// state -template - requires(sizeof...(QQPs) > 0) && (sizeof...(QQPs) <= 3) && are_time_derivatives -struct state { - std::tuple variables_; - constexpr state(QQPs... qqps) : variables_(std::move(qqps)...) {} +} // namespace detail + +// system state +template + requires(sizeof...(QPs) > 0) && (sizeof...(QPs) <= 3) && detail::are_time_derivatives +class system_state { + std::tuple variables_; +public: + constexpr explicit system_state(QPs... qps) : variables_(std::move(qps)...) {} + + template + [[nodiscard]] friend constexpr auto& get(system_state& s) + { + return get(s.variables_); + } + + template + [[nodiscard]] friend constexpr const auto& get(const system_state& s) + { + return get(s.variables_); + } }; template -concept State = mp_units::is_specialization_of; +concept SystemState = mp_units::is_specialization_of; -template -constexpr auto& get(state& s) -{ - return get(s.variables_); -} -template -constexpr const auto& get(const state& s) -{ - return get(s.variables_); -} - -// estimation -template -struct estimation { -private: - static constexpr auto uncertainty_ref = QQP::reference * QQP::reference; - using uncertainty_type = mp_units::quantity; +// system state estimation +template + requires requires { typename system_state; } +class system_state_estimate { public: - kalman::state state; // TODO extend kalman functions to work with this variadic parameter list - uncertainty_type uncertainty; + using state_type = system_state; + using standard_deviation_type = QP::quantity_type; + using variance_type = + mp_units::quantity(standard_deviation_type::reference), typename standard_deviation_type::rep>; +private: + state_type state_; + variance_type variance_; +public: + constexpr system_state_estimate(state_type state, standard_deviation_type standard_deviation) : + state_(state), variance_(pow<2>(standard_deviation)) + { + } + constexpr system_state_estimate(state_type state, variance_type variance) : state_(state), variance_(variance) {} + [[nodiscard]] constexpr const state_type& state() const { return state_; } + [[nodiscard]] constexpr const variance_type& variance() const { return variance_; } + [[nodiscard]] constexpr standard_deviation_type standard_deviation() const { return sqrt(variance_); } }; -template -estimation(state, U) -> estimation; - // kalman gain -template -constexpr mp_units::quantity kalman_gain(Q estimate_uncertainty, - Q measurement_uncertainty) +template + requires requires { mp_units::common_reference(Q1::reference, Q2::reference); } +[[nodiscard]] constexpr mp_units::quantity kalman_gain( + Q1 variance_in_estimate, Q2 variance_in_measurement) { - return estimate_uncertainty / (estimate_uncertainty + measurement_uncertainty); + return variance_in_estimate / (variance_in_estimate + variance_in_measurement); } // state update -template K> - requires(implicitly_convertible(QM::quantity_spec, Q::quantity_spec)) -constexpr state state_update(const state& predicted, QM measured, K gain) +template K> + requires(implicitly_convertible(QM::quantity_spec, QP::quantity_spec)) +[[nodiscard]] constexpr system_state state_update(const system_state& predicted, QM measured, K gain) { - return {get<0>(predicted) + gain * (measured - get<0>(predicted))}; + return system_state{get<0>(predicted) + gain * (measured - get<0>(predicted))}; } -template K, +template K, mp_units::QuantityOf T> - requires(implicitly_convertible(QM::quantity_spec, Q1::quantity_spec)) -constexpr state state_update(const state& predicted, QM measured, std::array gain, T interval) + requires(implicitly_convertible(QM::quantity_spec, QP1::quantity_spec)) +[[nodiscard]] constexpr system_state state_update(const system_state& predicted, QM measured, + std::array gain, T interval) { - const auto q1 = get<0>(predicted) + get<0>(gain) * (measured - get<0>(predicted)); - const auto q2 = get<1>(predicted) + get<1>(gain) * (measured - get<0>(predicted)) / interval; - return {q1, q2}; + const auto qp1 = fma(get<0>(gain), measured - get<0>(predicted), get<0>(predicted)); + const auto qp2 = fma(get<1>(gain), (measured - get<0>(predicted)) / interval, get<1>(predicted)); + return system_state{qp1, qp2}; } -template K, mp_units::QuantityOf T> - requires(implicitly_convertible(QM::quantity_spec, Q1::quantity_spec)) -constexpr state state_update(const state& predicted, QM measured, std::array gain, - T interval) + requires(implicitly_convertible(QM::quantity_spec, QP1::quantity_spec)) +[[nodiscard]] constexpr system_state state_update(const system_state& predicted, + QM measured, std::array gain, T interval) { - const auto q1 = get<0>(predicted) + get<0>(gain) * (measured - get<0>(predicted)); - const auto q2 = get<1>(predicted) + get<1>(gain) * (measured - get<0>(predicted)) / interval; - const auto q3 = get<2>(predicted) + get<2>(gain) * (measured - get<0>(predicted)) / (interval * interval / 2); - return {q1, q2, q3}; + const auto qp1 = fma(get<0>(gain), measured - get<0>(predicted), get<0>(predicted)); + const auto qp2 = fma(get<1>(gain), (measured - get<0>(predicted)) / interval, get<1>(predicted)); + const auto qp3 = fma(get<2>(gain), (measured - get<0>(predicted)) / (interval * interval / 2), get<2>(predicted)); + return system_state{qp1, qp2, qp3}; } // covariance update template K> -constexpr Q covariance_update(Q uncertainty, K gain) +[[nodiscard]] constexpr Q covariance_update(Q uncertainty, K gain) { return (1 * mp_units::one - gain) * uncertainty; } -// state extrapolation -template T> -constexpr state state_extrapolation(const state& estimated, T interval) +template K> +[[nodiscard]] constexpr system_state_estimate state_estimate_update( + const system_state_estimate& previous, QP measurement, K gain) { - const auto q1 = get<0>(estimated) + get<1>(estimated) * interval; - const auto q2 = get<1>(estimated); - return {q1, q2}; + return {state_update(previous.state(), measurement, gain), covariance_update(previous.variance(), gain)}; +}; + + +// state extrapolation +template T> +[[nodiscard]] constexpr system_state state_extrapolation(const system_state& estimated, T interval) +{ + auto to_quantity = [](const auto& qp) { return qp.quantity_ref_from(qp.point_origin); }; + const auto qp1 = fma(to_quantity(get<1>(estimated)), interval, get<0>(estimated)); + const auto qp2 = get<1>(estimated); + return system_state{qp1, qp2}; } -template T> -constexpr state state_extrapolation(const state& estimated, T interval) +template T> +[[nodiscard]] constexpr system_state state_extrapolation(const system_state& estimated, + T interval) { - const auto q1 = get<0>(estimated) + get<1>(estimated) * interval + get<2>(estimated) * pow<2>(interval) / 2; - const auto q2 = get<1>(estimated) + get<2>(estimated) * interval; - const auto q3 = get<2>(estimated); - return {q1, q2, q3}; + auto to_quantity = [](const auto& qp) { return qp.quantity_ref_from(qp.point_origin); }; + const auto qp1 = to_quantity(get<2>(estimated)) * pow<2>(interval) / 2 + + fma(to_quantity(get<1>(estimated)), interval, get<0>(estimated)); + const auto qp2 = fma(to_quantity(get<2>(estimated)), interval, get<1>(estimated)); + const auto qp3 = get<2>(estimated); + return system_state{qp1, qp2, qp3}; } // covariance extrapolation -template -constexpr Q covariance_extrapolation(Q uncertainty, Q process_noise_variance) +template + requires requires { mp_units::common_reference(Q1::reference, Q2::reference); } +[[nodiscard]] constexpr mp_units::Quantity auto covariance_extrapolation(Q1 uncertainty, Q2 process_noise_variance) { return uncertainty + process_noise_variance; } } // namespace kalman -template -struct MP_UNITS_STD_FMT::formatter> { - constexpr auto parse(format_parse_context& ctx) +template +struct MP_UNITS_STD_FMT::formatter, Char> : + MP_UNITS_STD_FMT::formatter::quantity_type> { + template + auto format(const mp_units::quantity_point& qp, FormatContext& ctx) const -> decltype(ctx.out()) { - mp_units::detail::dynamic_specs_handler handler(specs, ctx); - return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler); + return MP_UNITS_STD_FMT::formatter::quantity_type>::format( + qp.quantity_ref_from(qp.point_origin), ctx); + } +}; + +template +class MP_UNITS_STD_FMT::formatter, Char> { + using format_specs = mp_units::detail::fill_align_width_format_specs; + format_specs specs_{}; + std::array, sizeof...(QPs)> format_str_; + std::tuple...> formatters_{}; + + template + constexpr const Char* parse_default_spec(const Char* begin, const Char* end, Formatter& f, std::string& format_str) + { + if (begin == end || *begin++ != '[') + throw MP_UNITS_STD_FMT::format_error("`default-spec` should contain a `[` character"); + auto it = begin; + for (int nested_brackets = 0; it != end && !(*it == ']' && nested_brackets == 0); it++) { + if (*it == '[') ++nested_brackets; + if (*it == ']') { + if (nested_brackets == 0) throw MP_UNITS_STD_FMT::format_error("unmatched ']' in format string"); + --nested_brackets; + } + } + format_str = "{:" + std::string(begin, it) + '}'; + if (it == end) throw MP_UNITS_STD_FMT::format_error("unmatched '[' in format string"); + MP_UNITS_STD_FMT::basic_format_parse_context ctx(std::string_view(begin, it)); + auto ptr = f.parse(ctx); + if (ptr != it) throw MP_UNITS_STD_FMT::format_error("invalid subentity format '" + std::string(begin, it) + "'"); + return ++it; // skip `]` + } + + template + [[nodiscard]] constexpr const Char* parse_default_spec(const Char* begin, const Char* end, size_t idx, + std::index_sequence) + { + auto parse = [&](bool flag, auto& f, std::basic_string& str) { + return flag ? parse_default_spec(begin, end, f, str) : begin; + }; + return std::max({parse(idx == Is, std::get(formatters_), format_str_[Is])...}); + } + + [[nodiscard]] constexpr const Char* parse_defaults_specs(const Char* begin, const Char* end) + { + if (begin == end || *begin == '}') return begin; + if (*begin++ != ':') throw MP_UNITS_STD_FMT::format_error("`defaults-specs` should start with a `:`"); + do { + auto c = *begin++; + if (c < '0' || c >= static_cast('0' + sizeof...(QPs))) + throw MP_UNITS_STD_FMT::format_error(std::string("unknown `subentity-id` token '") + c + "'"); + const size_t idx = static_cast(c - '0'); + begin = parse_default_spec(begin, end, idx, std::index_sequence_for{}); + } while (begin != end && *begin != '}'); + return begin; + } + + template + OutputIt format_system_state(OutputIt out, const kalman::system_state& s, FormatContext& ctx, + std::index_sequence) const + { + const std::locale locale = MP_UNITS_FMT_LOCALE(ctx.locale()); + auto f = [&](const std::basic_string& str, const mp_units::QuantityPoint auto& qp) { + return MP_UNITS_STD_FMT::vformat_to(out, locale, str, MP_UNITS_STD_FMT::make_format_args(qp)); + }; + return f(get(format_str_), get(s)); + } + + template + OutputIt format_system_state(OutputIt out, const kalman::system_state& s, FormatContext& ctx, + std::index_sequence) const + { + const std::locale locale = MP_UNITS_FMT_LOCALE(ctx.locale()); + auto f = [&](const std::basic_string& str, const mp_units::QuantityPoint auto& qp) { + return MP_UNITS_STD_FMT::vformat_to(out, locale, str, MP_UNITS_STD_FMT::make_format_args(qp)); + }; + return f(get(format_str_), get(s)), ((*out++ = ' ', f(get(format_str_), get(s))), ...); + } + +public: + constexpr formatter() + { + for (auto& str : format_str_) str = "{}"; + } + + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) + { + auto begin = ctx.begin(), end = ctx.end(); + + begin = parse_fill_align_width(ctx, begin, end, specs_, mp_units::detail::fmt_align::right); + if (begin == end) return begin; + + return parse_defaults_specs(begin, end); } template - auto format(const kalman::state& s, FormatContext& ctx) + auto format(const kalman::system_state& s, FormatContext& ctx) const -> decltype(ctx.out()) { - std::string value_buffer; - auto to_value_buffer = std::back_inserter(value_buffer); - if (specs.precision != -1) { - if constexpr (sizeof...(Qs) == 1) - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{1:%.{0}Q %q}", specs.precision, kalman::get<0>(s)); - else if constexpr (sizeof...(Qs) == 2) - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q} }}", specs.precision, - kalman::get<0>(s), kalman::get<1>(s)); - else - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q}, {3:%.{0}Q %q} }}", - specs.precision, kalman::get<0>(s), kalman::get<1>(s), kalman::get<2>(s)); - } else { - if constexpr (sizeof...(Qs) == 1) - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{}", kalman::get<0>(s)); - else if constexpr (sizeof...(Qs) == 2) - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {} }}", kalman::get<0>(s), kalman::get<1>(s)); - else - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {}, {} }}", kalman::get<0>(s), kalman::get<1>(s), - kalman::get<2>(s)); + auto specs = specs_; + mp_units::detail::handle_dynamic_spec(specs.width, specs.width_ref, ctx); + + if (specs.width == 0) { + // Avoid extra copying if width is not specified + format_system_state(ctx.out(), s, ctx, std::index_sequence_for{}); + return ctx.out(); } + std::basic_string quantity_buffer; + format_system_state(std::back_inserter(quantity_buffer), s, ctx, std::index_sequence_for{}); - std::string global_format_buffer; - mp_units::detail::quantity_global_format_specs global_specs = {specs.fill, specs.align, specs.width}; - mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); - - return MP_UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, - MP_UNITS_STD_FMT::make_format_args(value_buffer)); + std::basic_string fill_align_width_format_str; + mp_units::detail::format_global_buffer(std::back_inserter(fill_align_width_format_str), specs); + return MP_UNITS_STD_FMT::vformat_to(ctx.out(), fill_align_width_format_str, + MP_UNITS_STD_FMT::make_format_args(quantity_buffer)); } -private: - mp_units::detail::dynamic_format_specs specs; -}; - -template -struct MP_UNITS_STD_FMT::formatter> { - constexpr auto parse(format_parse_context& ctx) - { - mp_units::detail::dynamic_specs_handler handler(specs, ctx); - return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler); - } - - template - auto format(kalman::estimation e, FormatContext& ctx) - { - mp_units::Quantity auto q = [](const Q& t) { - if constexpr (mp_units::Quantity) - return t; - else - return t.quantity_ref_from(t.point_origin); - }(kalman::get<0>(e.state)); - - std::string value_buffer; - auto to_value_buffer = std::back_inserter(value_buffer); - if (specs.precision != -1) { - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{0:%.{2}Q} ± {1:%.{2}Q} {0:%q}", q, sqrt(e.uncertainty), - specs.precision); - } else { - MP_UNITS_STD_FMT::format_to(to_value_buffer, "{0:%Q} ± {1:%Q} {0:%q}", q, sqrt(e.uncertainty)); - } - - std::string global_format_buffer; - mp_units::detail::quantity_global_format_specs global_specs = {specs.fill, specs.align, specs.width}; - mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); - - return MP_UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, - MP_UNITS_STD_FMT::make_format_args(value_buffer)); - } -private: - mp_units::detail::dynamic_format_specs specs; }; diff --git a/example/kalman_filter/kalman_filter-example_1.cpp b/example/kalman_filter/kalman_filter-example_1.cpp index c038f253..26746ace 100644 --- a/example/kalman_filter/kalman_filter-example_1.cpp +++ b/example/kalman_filter/kalman_filter-example_1.cpp @@ -35,36 +35,37 @@ import mp_units; using namespace mp_units; -void print_header(const kalman::State auto& initial) +void print_header(const kalman::SystemState auto& initial) { std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>9} | {:>8} | {:>14} | {:>14}\n", "N", "Gain", "Measured", "Curr. Estimate", "Next Estimate"); } -void print(auto iteration, QuantityOf auto gain, Quantity auto measured, - const kalman::State auto& current, const kalman::State auto& next) +void print(auto iteration, QuantityOf auto gain, QuantityPoint auto measured, + const kalman::SystemState auto& current, const kalman::SystemState auto& next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current, - next); + std::cout << MP_UNITS_STD_FMT::format("{:2} | {:9:N[.2f]} | {:8} | {:14:0[:N[.2f]]} | {:14:0[:N[.2f]]}\n", iteration, + gain, measured, current, next); } int main() { using namespace mp_units::si::unit_symbols; - using state = kalman::state>; + using state = kalman::system_state>; + using qp = quantity_point; - const state initial = {1 * kg}; - const std::array measurements = {1'030 * g, 989 * g, 1'017 * g, 1'009 * g, 1'013 * g, - 979 * g, 1'008 * g, 1'042 * g, 1'012 * g, 1'011 * g}; + const state initial_guess{qp{1 * kg}}; + const std::array measurements = {qp{996 * g}, qp{994 * g}, qp{1021 * g}, qp{1000 * g}, qp{1002 * g}, + qp{1010 * g}, qp{983 * g}, qp{971 * g}, qp{993 * g}, qp{1023 * g}}; - print_header(initial); - state next = initial; - for (int index = 1; const auto& v : measurements) { - const auto& previous = next; - const auto gain = 1. / index * one; - const auto current = state_update(previous, v, gain); + print_header(initial_guess); + state next = initial_guess; + for (int index = 1; const auto& m : measurements) { + const state& previous = next; + const quantity gain = 1. / index * one; + const state current = state_update(previous, m, gain); next = current; - print(index++, gain, v, current, next); + print(index++, gain, m, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_2.cpp b/example/kalman_filter/kalman_filter-example_2.cpp index 0260d2de..83489d76 100644 --- a/example/kalman_filter/kalman_filter-example_2.cpp +++ b/example/kalman_filter/kalman_filter-example_2.cpp @@ -39,36 +39,38 @@ inline constexpr bool mp_units::is_vector = true; using namespace mp_units; -void print_header(const kalman::State auto& initial) +void print_header(const kalman::SystemState auto& initial) { std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate", "Next Estimate"); } -void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) +void print(auto iteration, QuantityPoint auto measured, const kalman::SystemState auto& current, + const kalman::SystemState auto& next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:.1} | {:.1}\n", iteration, measured, current, next); + std::cout << MP_UNITS_STD_FMT::vformat("{:2} | {:8} | {:23:0[:N[.2f]]1[:N[.2f]]} | {:23:0[:N[.2f]]1[:N[.2f]]}\n", + MP_UNITS_STD_FMT::make_format_args(iteration, measured, current, next)); } int main() { using namespace mp_units::si::unit_symbols; - using state = kalman::state, quantity>; + using qp = quantity_point; + using state = kalman::system_state>; - const auto interval = isq::duration(5 * s); - const state initial = {30 * km, 40 * m / s}; - const quantity measurements[] = {30'110 * m, 30'265 * m, 30'740 * m, 30'750 * m, - 31'135 * m, 31'015 * m, 31'180 * m, 31'610 * m, - 31'960 * m, 31'865 * m}; + const quantity interval = isq::duration(5 * s); + const state initial{qp{30 * km}, quantity_point{40 * m / s}}; + const std::array measurements = {qp{30'171 * m}, qp{30'353 * m}, qp{30'756 * m}, qp{30'799 * m}, qp{31'018 * m}, + qp{31'278 * m}, qp{31'276 * m}, qp{31'379 * m}, qp{31'748 * m}, qp{32'175 * m}}; std::array gain = {0.2 * one, 0.1 * one}; print_header(initial); state next = state_extrapolation(initial, interval); - for (int index = 1; const auto& measured : measurements) { - const auto& previous = next; - const auto current = state_update(previous, measured, gain, interval); + for (int index = 1; const auto& m : measurements) { + const state& previous = next; + const state current = state_update(previous, m, gain, interval); next = state_extrapolation(current, interval); - print(index++, measured, current, next); + print(index++, m, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_3.cpp b/example/kalman_filter/kalman_filter-example_3.cpp index 5d453d48..352d603e 100644 --- a/example/kalman_filter/kalman_filter-example_3.cpp +++ b/example/kalman_filter/kalman_filter-example_3.cpp @@ -39,36 +39,38 @@ inline constexpr bool mp_units::is_vector = true; using namespace mp_units; -void print_header(const kalman::State auto& initial) +void print_header(const kalman::SystemState auto& initial) { std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); - std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>24} | {:>24}\n", "N", "Measured", "Curr. Estimate", + std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate", "Next Estimate"); } -void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) +void print(auto iteration, QuantityPoint auto measured, const kalman::SystemState auto& current, + const kalman::SystemState auto& next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:>24.1} | {:>24.1}\n", iteration, measured, current, next); + std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:23:0[:N[.2f]]1[:N[.2f]]} | {:23:0[:N[.2f]]1[:N[.2f]]}\n", + iteration, measured, current, next); } int main() { using namespace mp_units::si::unit_symbols; - using state = kalman::state, quantity>; + using qp = quantity_point; + using state = kalman::system_state>; - const auto interval = isq::duration(5 * s); - const state initial = {30 * km, 50 * m / s}; - const quantity measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m, - 31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m, - 36'010 * m, 37'265 * m}; + const quantity interval = isq::duration(5 * s); + const state initial{qp{30 * km}, quantity_point{50 * m / s}}; + const std::array measurements = {qp{30'221 * m}, qp{30'453 * m}, qp{30'906 * m}, qp{30'999 * m}, qp{31'368 * m}, + qp{31'978 * m}, qp{32'526 * m}, qp{33'379 * m}, qp{34'698 * m}, qp{36'275 * m}}; std::array gain = {0.2 * one, 0.1 * one}; print_header(initial); state next = state_extrapolation(initial, interval); - for (int index = 1; const auto& measured : measurements) { - const auto& previous = next; - const auto current = state_update(previous, measured, gain, interval); + for (int index = 1; const auto& m : measurements) { + const state& previous = next; + const state current = state_update(previous, m, gain, interval); next = state_extrapolation(current, interval); - print(index++, measured, current, next); + print(index++, m, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_4.cpp b/example/kalman_filter/kalman_filter-example_4.cpp index 00696c02..99492954 100644 --- a/example/kalman_filter/kalman_filter-example_4.cpp +++ b/example/kalman_filter/kalman_filter-example_4.cpp @@ -39,37 +39,40 @@ inline constexpr bool mp_units::is_vector = true; using namespace mp_units; -void print_header(const kalman::State auto& initial) +void print_header(const kalman::SystemState auto& initial) { std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>35} | {:>35}\n", "N", "Measured", "Curr. Estimate", "Next Estimate"); } -void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) +void print(auto iteration, QuantityPoint auto measured, const kalman::SystemState auto& current, + const kalman::SystemState auto& next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:>35.1} | {:>35.1}\n", iteration, measured, current, next); + std::cout << MP_UNITS_STD_FMT::format( + "{:2} | {:8} | {:35:0[:N[.2f]]1[:N[.2f]]2[:N[.2f]]} | {:35:0[:N[.2f]]1[:N[.2f]]2[:N[.2f]]}\n", iteration, measured, + current, next); } int main() { using namespace mp_units::si::unit_symbols; - using state = kalman::state, quantity, - quantity>; - const auto interval = isq::duration(5. * s); - const state initial = {30 * km, 50 * m / s, 0 * m / s2}; + using qp = quantity_point; + using state = + kalman::system_state, quantity_point>; - const quantity measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m, - 31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m, - 36'010 * m, 37'265 * m}; + const quantity interval = isq::duration(5. * s); + const state initial{qp{30 * km}, quantity_point{50 * m / s}, quantity_point{0 * m / s2}}; + const std::array measurements = {qp{30'221 * m}, qp{30'453 * m}, qp{30'906 * m}, qp{30'999 * m}, qp{31'368 * m}, + qp{31'978 * m}, qp{32'526 * m}, qp{33'379 * m}, qp{34'698 * m}, qp{36'275 * m}}; std::array gain = {0.5 * one, 0.4 * one, 0.1 * one}; print_header(initial); state next = state_extrapolation(initial, interval); - for (int index = 1; const auto& measured : measurements) { - const auto& previous = next; - const auto current = state_update(previous, measured, gain, interval); + for (int index = 1; const auto& m : measurements) { + const state& previous = next; + const state current = state_update(previous, m, gain, interval); next = state_extrapolation(current, interval); - print(index++, measured, current, next); + print(index++, m, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_5.cpp b/example/kalman_filter/kalman_filter-example_5.cpp index b4be7e3c..861e6596 100644 --- a/example/kalman_filter/kalman_filter-example_5.cpp +++ b/example/kalman_filter/kalman_filter-example_5.cpp @@ -36,45 +36,45 @@ import mp_units; using namespace mp_units; -template -void print_header(kalman::estimation initial) +template +void print_header(kalman::system_state_estimate initial) { - std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); - std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>5} | {:>8} | {:>16} | {:>16}\n", "N", "Gain", "Measured", + std::cout << MP_UNITS_STD_FMT::format("Initial: {} {}\n", initial.state(), initial.variance()); + std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>5} | {:>16} | {:>16}\n", "N", "Measured", "Gain", "Curr. Estimate", "Next Estimate"); } -template K> -void print(auto iteration, K gain, Q measured, kalman::estimation current, kalman::estimation next) +template K> +void print(auto iteration, QP measured, K gain, kalman::system_state_estimate current, + kalman::system_state_estimate next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured, - current, next); + std::cout << MP_UNITS_STD_FMT::format( + "{:2} | {:8} | {:5:N[.2f]} | {:6:0[:N[.2f]]} {:8:N[.2f]} | {:6:0[:N[.2f]]} {:8:N[.2f]}\n", iteration, measured, + gain, current.state(), current.variance(), next.state(), next.variance()); } int main() { - using namespace kalman; using namespace mp_units::si::unit_symbols; + using qp = quantity_point; + using estimate = kalman::system_state_estimate; + using state = estimate::state_type; - const estimation initial = {state{isq::height(60. * m)}, pow<2>(isq::height(15. * m))}; - const quantity measurements[] = {48.54 * m, 47.11 * m, 55.01 * m, 55.15 * m, 49.89 * m, - 40.85 * m, 46.72 * m, 50.05 * m, 51.27 * m, 49.95 * m}; - const auto measurement_uncertainty = pow<2>(isq::height(5. * m)); + const estimate initial{state{qp{60. * m}}, 15. * m}; + const std::array measurements = {qp{49.03 * m}, qp{48.44 * m}, qp{55.21 * m}, qp{49.98 * m}, qp{50.6 * m}, + qp{52.61 * m}, qp{45.87 * m}, qp{42.64 * m}, qp{48.26 * m}, qp{55.84 * m}}; + const quantity measurement_error = isq::height(5. * m); + const quantity measurement_variance = pow<2>(measurement_error); - auto update = [=](const estimation& previous, const Q& measurement, - QuantityOf auto gain) { - return estimation{state_update(previous.state, measurement, gain), covariance_update(previous.uncertainty, gain)}; - }; - - auto predict = [](const estimation& current) { return current; }; + auto predict = [](const estimate& current) { return current; }; print_header(initial); - estimation next = predict(initial); - for (int index = 1; const auto& measured : measurements) { - const auto& previous = next; - const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty); - const estimation current = update(previous, measured, gain); + estimate next = predict(initial); + for (int index = 1; const auto& m : measurements) { + const estimate& previous = next; + const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); + const estimate current = state_estimate_update(previous, m, gain); next = predict(current); - print(index++, gain, measured, current, next); + print(index++, m, gain, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_6.cpp b/example/kalman_filter/kalman_filter-example_6.cpp index a6ab2131..1881d55e 100644 --- a/example/kalman_filter/kalman_filter-example_6.cpp +++ b/example/kalman_filter/kalman_filter-example_6.cpp @@ -27,62 +27,59 @@ import mp_units; #else #include +#include #include -#include #include -#include +#include #endif -// Based on: https://www.kalmanfilter.net/kalman1d.html#ex6 +// Based on: https://www.kalmanfilter.net/kalman1d_pn.html#ex6 using namespace mp_units; template -void print_header(kalman::estimation initial) +void print_header(kalman::system_state_estimate initial) { - std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); - std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", + std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial.state(), initial.variance()); + std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>10} | {:>7} | {:>22} | {:>22}\n", "N", "Measured", "Gain", "Curr. Estimate", "Next Estimate"); } template K> -void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) +void print(auto iteration, QP measured, K gain, kalman::system_state_estimate current, + kalman::system_state_estimate next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, - measured.quantity_ref_from(QP::point_origin), current, next); + std::cout << MP_UNITS_STD_FMT::format( + "{:2} | {:10} | {:7:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]}\n", iteration, measured, + gain, current.state(), current.variance(), next.state(), next.variance()); } int main() { - constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius]; + using namespace mp_units::si::unit_symbols; + using qp = quantity_point; + using estimate = kalman::system_state_estimate; + using state = estimate::state_type; - using namespace kalman; + const quantity process_noise_variance = 0.0001 * pow<2>(deg_C); + const estimate initial{state{qp{60. * deg_C}}, 100. * deg_C}; + const std::array measurements = {qp{49.986 * deg_C}, qp{49.963 * deg_C}, qp{50.09 * deg_C}, qp{50.001 * deg_C}, + qp{50.018 * deg_C}, qp{50.05 * deg_C}, qp{49.938 * deg_C}, qp{49.858 * deg_C}, + qp{49.965 * deg_C}, qp{50.114 * deg_C}}; + const quantity measurement_error = 0.1 * deg_C; + const quantity measurement_variance = pow<2>(measurement_error); - const auto process_noise_variance = 0.0001 * (deg_C * deg_C); - const estimation initial = {state{si::ice_point + 10. * deg_C}, pow<2>(100. * deg_C)}; - const std::array measurements = {si::ice_point + 49.95 * deg_C, si::ice_point + 49.967 * deg_C, - si::ice_point + 50.1 * deg_C, si::ice_point + 50.106 * deg_C, - si::ice_point + 49.992 * deg_C, si::ice_point + 49.819 * deg_C, - si::ice_point + 49.933 * deg_C, si::ice_point + 50.007 * deg_C, - si::ice_point + 50.023 * deg_C, si::ice_point + 49.99 * deg_C}; - const auto measurement_uncertainty = pow<2>(0.1 * deg_C); - - auto update = [=](const estimation& previous, const QP& meassurement, - QuantityOf auto gain) { - return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; - }; - - auto predict = [=](const estimation& current) { - return estimation{current.state, covariance_extrapolation(current.uncertainty, process_noise_variance)}; + auto predict = [=](const estimate& current) { + return estimate{current.state(), kalman::covariance_extrapolation(current.variance(), process_noise_variance)}; }; print_header(initial); - estimation next = predict(initial); + estimate next = predict(initial); for (int index = 1; const auto& m : measurements) { - const auto& previous = next; - const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty); - const estimation current = update(previous, m, gain); + const estimate& previous = next; + const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); + const estimate current = state_estimate_update(previous, m, gain); next = predict(current); - print(index++, gain, m, current, next); + print(index++, m, gain, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_7.cpp b/example/kalman_filter/kalman_filter-example_7.cpp index 49c832fe..f7ab2450 100644 --- a/example/kalman_filter/kalman_filter-example_7.cpp +++ b/example/kalman_filter/kalman_filter-example_7.cpp @@ -27,62 +27,59 @@ import mp_units; #else #include +#include #include -#include #include -#include +#include #endif -// Based on: https://www.kalmanfilter.net/kalman1d.html#ex7 +// Based on: https://www.kalmanfilter.net/kalman1d_pn.html#ex7 using namespace mp_units; template -void print_header(kalman::estimation initial) +void print_header(kalman::system_state_estimate initial) { - std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); - std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", + std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial.state(), initial.variance()); + std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>10} | {:>7} | {:>22} | {:>22}\n", "N", "Measured", "Gain", "Curr. Estimate", "Next Estimate"); } template K> -void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) +void print(auto iteration, QP measured, K gain, kalman::system_state_estimate current, + kalman::system_state_estimate next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, - measured.quantity_ref_from(QP::point_origin), current, next); + std::cout << MP_UNITS_STD_FMT::format( + "{:2} | {:10} | {:7:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]}\n", iteration, measured, + gain, current.state(), current.variance(), next.state(), next.variance()); } int main() { - constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius]; + using namespace mp_units::si::unit_symbols; + using qp = quantity_point; + using estimate = kalman::system_state_estimate; + using state = estimate::state_type; - using namespace kalman; + const quantity process_noise_variance = 0.0001 * pow<2>(deg_C); + const estimate initial{state{qp{10. * deg_C}}, 100. * deg_C}; + const std::array measurements = {qp{50.486 * deg_C}, qp{50.963 * deg_C}, qp{51.597 * deg_C}, qp{52.001 * deg_C}, + qp{52.518 * deg_C}, qp{53.05 * deg_C}, qp{53.438 * deg_C}, qp{53.858 * deg_C}, + qp{54.465 * deg_C}, qp{55.114 * deg_C}}; + const quantity measurement_error = 0.1 * deg_C; + const quantity measurement_variance = pow<2>(measurement_error); - const auto process_noise_variance = 0.0001 * (deg_C * deg_C); - const estimation initial = {state{si::ice_point + 10. * deg_C}, pow<2>(100. * deg_C)}; - const std::array measurements = {si::ice_point + 50.45 * deg_C, si::ice_point + 50.967 * deg_C, - si::ice_point + 51.6 * deg_C, si::ice_point + 52.106 * deg_C, - si::ice_point + 52.492 * deg_C, si::ice_point + 52.819 * deg_C, - si::ice_point + 53.433 * deg_C, si::ice_point + 54.007 * deg_C, - si::ice_point + 54.523 * deg_C, si::ice_point + 54.99 * deg_C}; - const auto measurement_uncertainty = pow<2>(0.1 * deg_C); - - auto update = [=](const estimation& previous, const QP& meassurement, - QuantityOf auto gain) { - return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; - }; - - auto predict = [=](const estimation& current) { - return estimation{current.state, covariance_extrapolation(current.uncertainty, process_noise_variance)}; + auto predict = [=](const estimate& current) { + return estimate{current.state(), kalman::covariance_extrapolation(current.variance(), process_noise_variance)}; }; print_header(initial); - estimation next = predict(initial); + estimate next = predict(initial); for (int index = 1; const auto& m : measurements) { - const auto& previous = next; - const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty); - const estimation current = update(previous, m, gain); + const estimate& previous = next; + const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); + const estimate current = state_estimate_update(previous, m, gain); next = predict(current); - print(index++, gain, m, current, next); + print(index++, m, gain, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_8.cpp b/example/kalman_filter/kalman_filter-example_8.cpp index 474ecff7..e7cedfdb 100644 --- a/example/kalman_filter/kalman_filter-example_8.cpp +++ b/example/kalman_filter/kalman_filter-example_8.cpp @@ -27,62 +27,59 @@ import mp_units; #else #include +#include #include -#include #include -#include +#include #endif -// Based on: https://www.kalmanfilter.net/kalman1d.html#ex8 +// Based on: https://www.kalmanfilter.net/kalman1d_pn.html#ex8 using namespace mp_units; template -void print_header(kalman::estimation initial) +void print_header(kalman::system_state_estimate initial) { - std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial); - std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>16} | {:>16}\n", "N", "Gain", "Measured", + std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial.state(), initial.variance()); + std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>10} | {:>7} | {:>22} | {:>22}\n", "N", "Measured", "Gain", "Curr. Estimate", "Next Estimate"); } template K> -void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) +void print(auto iteration, QP measured, K gain, kalman::system_state_estimate current, + kalman::system_state_estimate next) { - std::cout << MP_UNITS_STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain, - measured.quantity_ref_from(QP::point_origin), current, next); + std::cout << MP_UNITS_STD_FMT::format( + "{:2} | {:10} | {:7:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]}\n", iteration, measured, + gain, current.state(), current.variance(), next.state(), next.variance()); } int main() { - constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius]; + using namespace mp_units::si::unit_symbols; + using qp = quantity_point; + using estimate = kalman::system_state_estimate; + using state = estimate::state_type; - using namespace kalman; + const quantity process_noise_variance = 0.15 * pow<2>(deg_C); + const estimate initial{state{qp{10. * deg_C}}, 100. * deg_C}; + const std::array measurements = {qp{50.486 * deg_C}, qp{50.963 * deg_C}, qp{51.597 * deg_C}, qp{52.001 * deg_C}, + qp{52.518 * deg_C}, qp{53.05 * deg_C}, qp{53.438 * deg_C}, qp{53.858 * deg_C}, + qp{54.465 * deg_C}, qp{55.114 * deg_C}}; + const quantity measurement_error = 0.1 * deg_C; + const quantity measurement_variance = pow<2>(measurement_error); - const auto process_noise_variance = 0.15 * (deg_C * deg_C); - const estimation initial = {state{si::ice_point + 10. * deg_C}, pow<2>(100. * deg_C)}; - const std::array measurements = {si::ice_point + 50.45 * deg_C, si::ice_point + 50.967 * deg_C, - si::ice_point + 51.6 * deg_C, si::ice_point + 52.106 * deg_C, - si::ice_point + 52.492 * deg_C, si::ice_point + 52.819 * deg_C, - si::ice_point + 53.433 * deg_C, si::ice_point + 54.007 * deg_C, - si::ice_point + 54.523 * deg_C, si::ice_point + 54.99 * deg_C}; - const auto measurement_uncertainty = pow<2>(0.1 * deg_C); - - auto update = [=](const estimation& previous, const QP& meassurement, - QuantityOf auto gain) { - return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)}; - }; - - auto predict = [=](const estimation& current) { - return estimation{current.state, covariance_extrapolation(current.uncertainty, process_noise_variance)}; + auto predict = [=](const estimate& current) { + return estimate{current.state(), kalman::covariance_extrapolation(current.variance(), process_noise_variance)}; }; print_header(initial); - estimation next = predict(initial); + estimate next = predict(initial); for (int index = 1; const auto& m : measurements) { - const auto& previous = next; - const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty); - const estimation current = update(previous, m, gain); + const estimate& previous = next; + const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); + const estimate current = state_estimate_update(previous, m, gain); next = predict(current); - print(index++, gain, m, current, next); + print(index++, m, gain, current, next); } } diff --git a/src/core/include/mp-units/bits/fmt.h b/src/core/include/mp-units/bits/fmt.h index 16d98363..3e59cc03 100644 --- a/src/core/include/mp-units/bits/fmt.h +++ b/src/core/include/mp-units/bits/fmt.h @@ -30,6 +30,7 @@ // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access) #pragma once +#include #include #include @@ -45,6 +46,9 @@ namespace mp_units::detail { +// TODO the below should be exposed by the C++ Standard Library (used in our examples) +MP_UNITS_EXPORT_BEGIN + enum class fmt_align : std::int8_t { none, left, right, center, numeric }; enum class fmt_arg_id_kind : std::int8_t { none, @@ -109,6 +113,8 @@ public: [[nodiscard]] constexpr const Char& operator[](size_t index) const { return data_[index]; } }; +MP_UNITS_EXPORT_END + template inline constexpr bool is_integer = std::is_integral_v && !std::is_same_v && !std::is_same_v && !std::is_same_v; @@ -135,20 +141,6 @@ template return static_cast>(value); } -struct width_checker { - template - [[nodiscard]] constexpr unsigned long long operator()(T value) const - { - if constexpr (is_integer) { - if constexpr (std::numeric_limits::is_signed) - if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative width")); - return static_cast(value); - } - MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("width is not integer")); - return 0; - } -}; - template [[nodiscard]] constexpr int get_dynamic_spec(FormatArg arg) { @@ -167,6 +159,9 @@ template return arg; } +// TODO the below should be exposed by the C++ Standard Library (used in our examples) +MP_UNITS_EXPORT_BEGIN + template constexpr void handle_dynamic_spec(int& value, fmt_arg_ref ref, Context& ctx) { @@ -184,6 +179,22 @@ constexpr void handle_dynamic_spec(int& value, fmt_arg_ref + [[nodiscard]] constexpr unsigned long long operator()(T value) const + { + if constexpr (is_integer) { + if constexpr (std::numeric_limits::is_signed) + if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative width")); + return static_cast(value); + } + MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("width is not integer")); + return 0; + } +}; + +MP_UNITS_EXPORT_END + // Parses the range [begin, end) as an unsigned integer. This function assumes // that the range is non-empty and the first character is a digit. template diff --git a/src/core/include/mp-units/format.h b/src/core/include/mp-units/format.h index 544ce69c..88616c1f 100644 --- a/src/core/include/mp-units/format.h +++ b/src/core/include/mp-units/format.h @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include #include @@ -33,14 +34,6 @@ namespace mp_units::detail { -template -struct fill_align_width_format_specs { - fill_t fill; - fmt_align align : 4 = fmt_align::none; - int width = 0; - fmt_arg_ref width_ref; -}; - template [[nodiscard]] constexpr const Char* at_most_one_of(const Char* begin, const Char* end, std::string_view modifiers) { @@ -51,6 +44,17 @@ template return it; } +// TODO the below should be exposed by the C++ Standard Library (used in our examples) +MP_UNITS_EXPORT_BEGIN + +template +struct fill_align_width_format_specs { + fill_t fill; + fmt_align align : 4 = fmt_align::none; + int width = 0; + fmt_arg_ref width_ref; +}; + template [[nodiscard]] constexpr const Char* parse_fill_align_width(MP_UNITS_STD_FMT::basic_format_parse_context& ctx, const Char* begin, const Char* end, Specs& specs, @@ -89,6 +93,8 @@ OutputIt format_global_buffer(OutputIt out, const fill_align_width_format_specs< return MP_UNITS_STD_FMT::format_to(out, "}}"); } +MP_UNITS_EXPORT_END + } // namespace mp_units::detail // From 9c4a87e2e4534cf2699565c16b03bb1ace651a84 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 29 May 2024 20:45:26 +0200 Subject: [PATCH 26/61] docs: gcc-14 does not support C++ modules correctly --- docs/getting_started/cpp_compiler_support.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/getting_started/cpp_compiler_support.md b/docs/getting_started/cpp_compiler_support.md index 73f1c6c6..c3f4130a 100644 --- a/docs/getting_started/cpp_compiler_support.md +++ b/docs/getting_started/cpp_compiler_support.md @@ -13,13 +13,13 @@ The table below provides the minimum compiler version required to compile the code using a specific C++ feature: -| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC | -|-----------------------------------------------------------|:-----------:|:---:|:-----:|:-----------:|:----:| -| **Minimum support** | 20 | 12 | 16 | 15 | None | -| **`std::format`** | 20 | 13 | 17 | None | None | -| **C++ modules** | 20 | 14 | 17 | None | None | -| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None | -| **Explicit `this` parameter** | 23 | 14 | 18 | None | None | +| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC | +|-----------------------------------------------------------|:-----------:|:----:|:-----:|:-----------:|:----:| +| **Minimum support** | 20 | 12 | 16 | 15 | None | +| **`std::format`** | 20 | 13 | 17 | None | None | +| **C++ modules** | 20 | None | 17 | None | None | +| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None | +| **Explicit `this` parameter** | 23 | 14 | 18 | None | None | !!! important From 1d1057aa44fdcce6faf081d79c31dbf177fa8d06 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 29 May 2024 20:57:00 +0200 Subject: [PATCH 27/61] fix: symbol shadowing error on clang-16 fixed --- example/kalman_filter/kalman_filter-example_1.cpp | 6 +++--- example/kalman_filter/kalman_filter-example_2.cpp | 6 +++--- example/kalman_filter/kalman_filter-example_3.cpp | 6 +++--- example/kalman_filter/kalman_filter-example_4.cpp | 6 +++--- example/kalman_filter/kalman_filter-example_5.cpp | 6 +++--- example/kalman_filter/kalman_filter-example_6.cpp | 6 +++--- example/kalman_filter/kalman_filter-example_7.cpp | 6 +++--- example/kalman_filter/kalman_filter-example_8.cpp | 6 +++--- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/example/kalman_filter/kalman_filter-example_1.cpp b/example/kalman_filter/kalman_filter-example_1.cpp index 26746ace..a5f8778a 100644 --- a/example/kalman_filter/kalman_filter-example_1.cpp +++ b/example/kalman_filter/kalman_filter-example_1.cpp @@ -61,11 +61,11 @@ int main() print_header(initial_guess); state next = initial_guess; - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const state& previous = next; const quantity gain = 1. / index * one; - const state current = state_update(previous, m, gain); + const state current = state_update(previous, measurement, gain); next = current; - print(index++, gain, m, current, next); + print(index++, gain, measurement, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_2.cpp b/example/kalman_filter/kalman_filter-example_2.cpp index 83489d76..d9fab7f9 100644 --- a/example/kalman_filter/kalman_filter-example_2.cpp +++ b/example/kalman_filter/kalman_filter-example_2.cpp @@ -67,10 +67,10 @@ int main() print_header(initial); state next = state_extrapolation(initial, interval); - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const state& previous = next; - const state current = state_update(previous, m, gain, interval); + const state current = state_update(previous, measurement, gain, interval); next = state_extrapolation(current, interval); - print(index++, m, current, next); + print(index++, measurement, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_3.cpp b/example/kalman_filter/kalman_filter-example_3.cpp index 352d603e..931cba60 100644 --- a/example/kalman_filter/kalman_filter-example_3.cpp +++ b/example/kalman_filter/kalman_filter-example_3.cpp @@ -67,10 +67,10 @@ int main() print_header(initial); state next = state_extrapolation(initial, interval); - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const state& previous = next; - const state current = state_update(previous, m, gain, interval); + const state current = state_update(previous, measurement, gain, interval); next = state_extrapolation(current, interval); - print(index++, m, current, next); + print(index++, measurement, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_4.cpp b/example/kalman_filter/kalman_filter-example_4.cpp index 99492954..073bc4a1 100644 --- a/example/kalman_filter/kalman_filter-example_4.cpp +++ b/example/kalman_filter/kalman_filter-example_4.cpp @@ -69,10 +69,10 @@ int main() print_header(initial); state next = state_extrapolation(initial, interval); - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const state& previous = next; - const state current = state_update(previous, m, gain, interval); + const state current = state_update(previous, measurement, gain, interval); next = state_extrapolation(current, interval); - print(index++, m, current, next); + print(index++, measurement, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_5.cpp b/example/kalman_filter/kalman_filter-example_5.cpp index 861e6596..8a594ded 100644 --- a/example/kalman_filter/kalman_filter-example_5.cpp +++ b/example/kalman_filter/kalman_filter-example_5.cpp @@ -70,11 +70,11 @@ int main() print_header(initial); estimate next = predict(initial); - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const estimate& previous = next; const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); - const estimate current = state_estimate_update(previous, m, gain); + const estimate current = state_estimate_update(previous, measurement, gain); next = predict(current); - print(index++, m, gain, current, next); + print(index++, measurement, gain, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_6.cpp b/example/kalman_filter/kalman_filter-example_6.cpp index 1881d55e..59ce7a0f 100644 --- a/example/kalman_filter/kalman_filter-example_6.cpp +++ b/example/kalman_filter/kalman_filter-example_6.cpp @@ -75,11 +75,11 @@ int main() print_header(initial); estimate next = predict(initial); - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const estimate& previous = next; const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); - const estimate current = state_estimate_update(previous, m, gain); + const estimate current = state_estimate_update(previous, measurement, gain); next = predict(current); - print(index++, m, gain, current, next); + print(index++, measurement, gain, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_7.cpp b/example/kalman_filter/kalman_filter-example_7.cpp index f7ab2450..ecf52f96 100644 --- a/example/kalman_filter/kalman_filter-example_7.cpp +++ b/example/kalman_filter/kalman_filter-example_7.cpp @@ -75,11 +75,11 @@ int main() print_header(initial); estimate next = predict(initial); - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const estimate& previous = next; const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); - const estimate current = state_estimate_update(previous, m, gain); + const estimate current = state_estimate_update(previous, measurement, gain); next = predict(current); - print(index++, m, gain, current, next); + print(index++, measurement, gain, current, next); } } diff --git a/example/kalman_filter/kalman_filter-example_8.cpp b/example/kalman_filter/kalman_filter-example_8.cpp index e7cedfdb..e9e06da0 100644 --- a/example/kalman_filter/kalman_filter-example_8.cpp +++ b/example/kalman_filter/kalman_filter-example_8.cpp @@ -75,11 +75,11 @@ int main() print_header(initial); estimate next = predict(initial); - for (int index = 1; const auto& m : measurements) { + for (int index = 1; const auto& measurement : measurements) { const estimate& previous = next; const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance); - const estimate current = state_estimate_update(previous, m, gain); + const estimate current = state_estimate_update(previous, measurement, gain); next = predict(current); - print(index++, m, gain, current, next); + print(index++, measurement, gain, current, next); } } From 09fa158f21a46b01a415c55472bbf2542e627ac1 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 08:30:41 +0200 Subject: [PATCH 28/61] fix: missing `` header file added --- example/kalman_filter/kalman.h | 1 + src/core/include/mp-units/bits/core_gmf.h | 1 + src/core/include/mp-units/format.h | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/example/kalman_filter/kalman.h b/example/kalman_filter/kalman.h index bcf19646..5efd0df2 100644 --- a/example/kalman_filter/kalman.h +++ b/example/kalman_filter/kalman.h @@ -24,6 +24,7 @@ #include #include +#include #include #ifdef MP_UNITS_MODULES import mp_units; diff --git a/src/core/include/mp-units/bits/core_gmf.h b/src/core/include/mp-units/bits/core_gmf.h index dfb93961..a64e46b1 100644 --- a/src/core/include/mp-units/bits/core_gmf.h +++ b/src/core/include/mp-units/bits/core_gmf.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/src/core/include/mp-units/format.h b/src/core/include/mp-units/format.h index 88616c1f..0400b492 100644 --- a/src/core/include/mp-units/format.h +++ b/src/core/include/mp-units/format.h @@ -32,6 +32,10 @@ #include #include +#ifndef MP_UNITS_IN_MODULE_INTERFACE +#include +#endif + namespace mp_units::detail { template From c91ad7030ba86a1f45cfd8bb7785a3688fa8fc1c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 08:56:51 +0200 Subject: [PATCH 29/61] fix: `format_system_state` ambiguity fixed on apple-clang --- example/kalman_filter/kalman.h | 1 + 1 file changed, 1 insertion(+) diff --git a/example/kalman_filter/kalman.h b/example/kalman_filter/kalman.h index 5efd0df2..86659065 100644 --- a/example/kalman_filter/kalman.h +++ b/example/kalman_filter/kalman.h @@ -263,6 +263,7 @@ class MP_UNITS_STD_FMT::formatter, Char> { } template + requires(sizeof...(Rest) > 0) OutputIt format_system_state(OutputIt out, const kalman::system_state& s, FormatContext& ctx, std::index_sequence) const { From 4551a247b5bb10f6817a0a4f309d52c21db286e3 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 12:10:19 +0200 Subject: [PATCH 30/61] fix: broken links in the docs fixed --- docs/getting_started/cpp_compiler_support.md | 2 +- docs/getting_started/faq.md | 2 +- docs/getting_started/installation_and_usage.md | 4 ++-- docs/getting_started/introduction.md | 2 +- docs/users_guide/framework_basics/concepts.md | 2 +- docs/users_guide/framework_basics/text_output.md | 2 +- docs/users_guide/framework_basics/the_affine_space.md | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/getting_started/cpp_compiler_support.md b/docs/getting_started/cpp_compiler_support.md index c3f4130a..f5fd6fab 100644 --- a/docs/getting_started/cpp_compiler_support.md +++ b/docs/getting_started/cpp_compiler_support.md @@ -29,7 +29,7 @@ C++ feature: ## `std::format` -- Provides [powerful text formatting capabilities](../users_guide/framework_basics/text_output.md#stdformat) +- Provides [powerful text formatting capabilities](../users_guide/framework_basics/text_output.md#text-formatting) for C++. - An alternative [fmtlib](https://github.com/fmtlib/fmt) library can be used instead if - the C++ language feature is not supported, diff --git a/docs/getting_started/faq.md b/docs/getting_started/faq.md index 38373806..efc91977 100644 --- a/docs/getting_started/faq.md +++ b/docs/getting_started/faq.md @@ -176,7 +176,7 @@ we have to obey the rules and be consistent with ISO specifications. !!! note We do understand engineering reality and the constraints of some environments. This is why the library - has the option of [ASCII-only Quantity Symbols](../users_guide/framework_basics/text_output.md#unit-symbol-formatting). + has the option of [ASCII-only Quantity Symbols](../users_guide/framework_basics/text_output.md#unit_symbol_formatting). ## Why don't we have CMake options to disable the building of tests and examples? diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 8d2d1f48..7e623f56 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -56,7 +56,7 @@ projects: handle the dependencies. To learn more about the rationale, please check our - [FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-building-of-tests-and-examples). + [FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-the-building-of-tests-and-examples). ### Modules @@ -276,7 +276,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} : [:octicons-tag-24: 2.2.0][conan build all support] · :octicons-milestone-24: `True`/`False` (Default: `False`) Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in - [Repository Structure and Dependencies](#repository-structure-and-dependencies). + [Repository directory tree and dependencies](#repository-directory-tree-and-dependencies). It also runs unit tests during Conan build (unless [`tools.build:skip_test`](https://docs.conan.io/2/reference/commands/config.html?highlight=tools.build:skip_test#conan-config-list) configuration property is set to `True`). diff --git a/docs/getting_started/introduction.md b/docs/getting_started/introduction.md index fadce399..7efe47cf 100644 --- a/docs/getting_started/introduction.md +++ b/docs/getting_started/introduction.md @@ -80,6 +80,6 @@ To achieve this goal, several techniques are applied: [Highly adjustable text-output formatting]: ../users_guide/framework_basics/text_output.md [Each entity can be defined with a single line of code]: ../users_guide/framework_basics/interface_introduction.md#new-style-of-definitions -[User can easily extend the systems with custom dimensions, quantities, and units]: ../users_guide/use_cases/extending_the_library.md#new-style-of-definitions +[User can easily extend the systems with custom dimensions, quantities, and units]: ../users_guide/use_cases/extending_the_library.md [freestanding]: https://en.cppreference.com/w/cpp/freestanding diff --git a/docs/users_guide/framework_basics/concepts.md b/docs/users_guide/framework_basics/concepts.md index 5faab730..494675a6 100644 --- a/docs/users_guide/framework_basics/concepts.md +++ b/docs/users_guide/framework_basics/concepts.md @@ -95,7 +95,7 @@ and when `T` is implicitly convertible to `V`. and is satisfied by: - All units derived from a `named_unit` class template instantiated with a unique symbol identifier - and a [`QuantitySpec`](#quantityspec) of a [quantity kind](../../appendix/glossary.md#kind). + and a [`QuantitySpec`](#QuantitySpec) of a [quantity kind](../../appendix/glossary.md#kind). - All units being a result of [unit equations](../../appendix/glossary.md#unit-equation) on other associated units. diff --git a/docs/users_guide/framework_basics/text_output.md b/docs/users_guide/framework_basics/text_output.md index 124cf5d3..4b21b482 100644 --- a/docs/users_guide/framework_basics/text_output.md +++ b/docs/users_guide/framework_basics/text_output.md @@ -241,7 +241,7 @@ static_assert(unit_symbol<{.solidus = unit_symbol_solidus::never, `std::string_view` is returned only when C++23 is available. Otherwise, an instance of a `basic_fixed_string` is being returned. See more in the - [C++ compiler support](../../getting_started/installation_and_usage.md#static-constexpr-variables-in-constexpr-functions) + [C++ compiler support](../../getting_started/cpp_compiler_support.md#static-constexpr-variables-in-constexpr-functions) chapter. #### `unit_symbol_to()` diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index c8a48541..c21bceea 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -219,7 +219,7 @@ the origin and the _displacement vector_ measured from it to the point we create [Why can't I create a quantity by passing a number to a constructor?](../../getting_started/faq.md#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor) chapter. -Similarly to [creation of a quantity](../../getting_started/quick_start.md#creating-a-quantity), +Similarly to [creation of a quantity](../../getting_started/quick_start.md#quantities), if someone does not like the operator-based syntax to create a `quantity_point`, the same results can be achieved with a two-parameter constructor: From 3a792b4057b767b46447d6ca062a6d414d9dc849 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 12:14:58 +0200 Subject: [PATCH 31/61] fix: IWYU --- src/core/include/mp-units/bits/fmt.h | 1 + test/static/fixed_string_test.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/core/include/mp-units/bits/fmt.h b/src/core/include/mp-units/bits/fmt.h index 3e59cc03..c4848d67 100644 --- a/src/core/include/mp-units/bits/fmt.h +++ b/src/core/include/mp-units/bits/fmt.h @@ -36,6 +36,7 @@ #ifndef MP_UNITS_IN_MODULE_INTERFACE #include +#include #include #include #include diff --git a/test/static/fixed_string_test.cpp b/test/static/fixed_string_test.cpp index 87ca7660..f1885e0c 100644 --- a/test/static/fixed_string_test.cpp +++ b/test/static/fixed_string_test.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include using namespace mp_units; From a6562acde5bbb251c76f7b6a4e92986135eada89 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 12:23:14 +0200 Subject: [PATCH 32/61] feat: allow configuring GSL library use Resolves #576 --- .github/workflows/ci-conan.yml | 20 ++++++------ .github/workflows/ci-test-package-cmake.yml | 18 ++++++----- conanfile.py | 15 +++++++-- .../getting_started/installation_and_usage.md | 27 ++++++++++++---- example/CMakeLists.txt | 28 ++++++++++++---- example/include/validated_type.h | 5 ++- src/CMakeLists.txt | 3 ++ src/cmake/AddMPUnitsModule.cmake | 2 +- src/core/CMakeLists.txt | 26 +++++++++++---- src/core/include/mp-units/bits/core_gmf.h | 1 - src/core/include/mp-units/bits/fmt.h | 11 +++---- src/core/include/mp-units/bits/ratio.h | 26 +++++++-------- src/core/include/mp-units/compat_macros.h | 32 +++++++++++++++++++ src/core/include/mp-units/ext/fixed_string.h | 21 ++++++------ .../include/mp-units/framework/dimension.h | 5 +-- .../include/mp-units/framework/quantity.h | 14 ++++---- .../include/mp-units/framework/symbol_text.h | 20 ++++++------ src/core/include/mp-units/framework/unit.h | 5 +-- src/mp-unitsConfig.cmake | 6 +++- test_package/CMakeLists.txt | 8 +++++ 20 files changed, 199 insertions(+), 94 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 03768356..1526a2c5 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -35,12 +35,13 @@ env: jobs: build: - name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" + name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: formatting: ["std::format", "fmtlib"] + contracts: ["none", "gsl-lite", "ms-gsl"] std: [20, 23] config: # - { @@ -175,19 +176,20 @@ jobs: cache-name: cache-conan-data with: path: ~/.conan2/p - key: conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} + key: conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} restore-keys: | - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- conan-${{ matrix.config.os }}-${{ matrix.formatting }}- conan-${{ matrix.config.os }}- - uses: hendrikmuhs/ccache-action@v1.2 if: runner.os == 'Linux' with: - key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }} + key: ${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }} max-size: 50M - name: Install gcc-13 if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '13' @@ -241,7 +243,7 @@ jobs: run: | conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \ -b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ - -c user.mp-units.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} ${{ matrix.config.conan-config }} + -c user.mp-units.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} ${{ matrix.config.conan-config }} - name: Obtain package reference id: get-package-ref shell: bash diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 98abe43c..ea812567 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -36,12 +36,13 @@ on: jobs: test_package: - name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" + name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: formatting: ["std::format", "fmtlib"] + contracts: ["none", "gsl-lite", "ms-gsl"] std: [20, 23] config: # - { @@ -172,13 +173,14 @@ jobs: cache-name: cache-conan-data with: path: ~/.conan2/p - key: cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} + key: cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} restore-keys: | - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- cmake-${{ matrix.config.os }}-${{ matrix.formatting }}- cmake-${{ matrix.config.os }}- - name: Install gcc-13 @@ -233,7 +235,7 @@ jobs: shell: bash run: | conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=False \ - -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} + -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} - name: Provide dependencies for the build shell: bash working-directory: src diff --git a/conanfile.py b/conanfile.py index 9f51a130..c7be62dd 100644 --- a/conanfile.py +++ b/conanfile.py @@ -65,12 +65,14 @@ class MPUnitsConan(ConanFile): "std_format": ["auto", True, False], "string_view_ret": ["auto", True, False], "no_crtp": ["auto", True, False], + "contracts": ["none", "gsl-lite", "ms-gsl"], } default_options = { "cxx_modules": "auto", "std_format": "auto", "string_view_ret": "auto", "no_crtp": "auto", + "contracts": "gsl-lite", } tool_requires = "cmake/[>=3.29]" implements = "auto_header_only" @@ -199,7 +201,10 @@ class MPUnitsConan(ConanFile): self.version = version.strip() def requirements(self): - self.requires("gsl-lite/0.41.0") + if self.options.contracts == "gsl-lite": + self.requires("gsl-lite/0.41.0") + elif self.options.contracts == "ms-gsl": + self.requires("ms-gsl/4.0.0") if self._use_fmtlib: self.requires("fmt/10.2.1") @@ -237,6 +242,9 @@ class MPUnitsConan(ConanFile): self.options.string_view_ret ).upper() tc.cache_variables["MP_UNITS_API_NO_CRTP"] = str(self.options.no_crtp).upper() + tc.cache_variables["MP_UNITS_API_CONTRACTS"] = str( + self.options.contracts + ).upper() tc.generate() deps = CMakeDeps(self) deps.generate() @@ -263,7 +271,10 @@ class MPUnitsConan(ConanFile): def package_info(self): compiler = self.settings.compiler - self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] + if self.options.contracts == "gsl-lite": + self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] + elif self.options.contracts == "ms-gsl": + self.cpp_info.components["core"].requires = ["ms-gsl::ms-gsl"] if self._use_fmtlib: self.cpp_info.components["core"].requires.append("fmt::fmt") if compiler == "msvc": diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 7e623f56..e21dd6ce 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -18,8 +18,8 @@ projects: - in case this library becomes part of the C++ standard, it will have no external dependencies but until then, it depends on the following: - - [gsl-lite](https://github.com/gsl-lite/gsl-lite) to verify runtime contracts with - the `gsl_Expects` macro, + - [gsl-lite](https://github.com/gsl-lite/gsl-lite) or [ms-gsl](https://github.com/microsoft/GSL) + to verify runtime contracts (if contract checking is enabled), - [{fmt}](https://github.com/fmtlib/fmt) to provide text formatting of quantities (if `std::format` is not supported yet on a specific compiler). @@ -229,7 +229,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} ### Conan options -[cxx_modules](#cxx_modules){ #cxx_modules } +[`cxx_modules`](#cxx_modules){ #cxx_modules } : [:octicons-tag-24: 2.2.0][conan C++ modules support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -237,7 +237,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan C++ modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 -[std_format](#std_format){ #std_format } +[`std_format`](#std_format){ #std_format } : [:octicons-tag-24: 2.2.0][conan std::format support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -247,7 +247,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan std::format support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 -[string_view_ret](#string_view_ret){ #string_view_ret } +[`string_view_ret`](#string_view_ret){ #string_view_ret } : [:octicons-tag-24: 2.2.0][conan returning string_view] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -259,7 +259,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan returning string_view]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 -[no_crtp](#no_crtp){ #no_crtp } +[`no_crtp`](#no_crtp){ #no_crtp } : [:octicons-tag-24: 2.2.0][conan no crtp support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -268,6 +268,13 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`contracts`](#contracts){ #contracts } + +: [:octicons-tag-24: 2.2.0][conan contracts] · :octicons-milestone-24: `none`/`gsl-lite`/`ms-gsl` (Default: `gsl-lite`) + + Enables checking of preconditions and additional asserts in the code. + + [conan contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 ### Conan configuration properties @@ -345,6 +352,14 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [cmake no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`MP_UNITS_API_CONTRACTS`](#MP_UNITS_API_CONTRACTS){ #MP_UNITS_API_CONTRACTS } + +: [:octicons-tag-24: 2.2.0][cmake contracts] · :octicons-milestone-24: `NONE`/`GSL-LITE`/`MS-GSL` (Default: `GSL-LITE`) + + Enables checking of preconditions and additional asserts in the code. + + [cmake contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 + #### Options for mp-units project developers [`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA } diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 36364f12..5325cd8d 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -23,21 +23,37 @@ cmake_minimum_required(VERSION 3.5) # find dependencies -if(NOT TARGET gsl::gsl-lite) - find_package(gsl-lite REQUIRED) -endif() - if(${projectPrefix}BUILD_CXX_MODULES) add_library(example_utils INTERFACE) target_compile_features(example_utils INTERFACE cxx_std_20) target_compile_definitions(example_utils INTERFACE ${projectPrefix}MODULES) target_include_directories(example_utils INTERFACE include) - target_link_libraries(example_utils INTERFACE gsl::gsl-lite) endif() add_library(example_utils-headers INTERFACE) target_include_directories(example_utils-headers INTERFACE include) -target_link_libraries(example_utils-headers INTERFACE gsl::gsl-lite) + +if(${projectPrefix}API_CONTRACTS STREQUAL "NONE") + target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=0) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE") + if(NOT TARGET gsl::gsl-lite) + find_package(gsl-lite REQUIRED) + endif() + if(${projectPrefix}BUILD_CXX_MODULES) + target_link_libraries(example_utils INTERFACE gsl::gsl-lite) + endif() + target_link_libraries(example_utils-headers INTERFACE gsl::gsl-lite) + target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=2) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL") + if(NOT TARGET Microsoft.GSL::GSL) + find_package(Microsoft.GSL REQUIRED) + endif() + if(${projectPrefix}BUILD_CXX_MODULES) + target_link_libraries(example_utils INTERFACE Microsoft.GSL::GSL) + endif() + target_link_libraries(example_utils-headers INTERFACE Microsoft.GSL::GSL) + target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=3) +endif() # # add_example(target ...) diff --git a/example/include/validated_type.h b/example/include/validated_type.h index 8b05aa8f..9a429e26 100644 --- a/example/include/validated_type.h +++ b/example/include/validated_type.h @@ -22,7 +22,6 @@ #pragma once -#include #include #include #include // IWYU pragma: export @@ -50,13 +49,13 @@ public: requires std::copyable : value_(value) { - gsl_Expects(validate(value_)); + MP_UNITS_EXPECTS(validate(value_)); } constexpr explicit validated_type(T&& value) noexcept(std::is_nothrow_move_constructible_v) : value_(std::move(value)) { - gsl_Expects(validate(value_)); + MP_UNITS_EXPECTS(validate(value_)); } constexpr validated_type(const T& value, validated_tag) noexcept(std::is_nothrow_copy_constructible_v) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 363634aa..d33178c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -68,6 +68,9 @@ cache_var_values(API_STRING_VIEW_RET AUTO TRUE FALSE) set(${projectPrefix}API_NO_CRTP AUTO CACHE STRING "Enable class definitions without CRTP idiom") cache_var_values(API_NO_CRTP AUTO TRUE FALSE) +set(${projectPrefix}API_CONTRACTS GSL-LITE CACHE STRING "Enable contract checking") +cache_var_values(API_CONTRACTS NONE GSL-LITE MS-GSL) + # C++ features check_cxx_feature_supported(__cpp_lib_format ${projectPrefix}LIB_FORMAT_SUPPORTED) check_cxx_feature_supported("__cpp_constexpr >= 202211L" ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS) diff --git a/src/cmake/AddMPUnitsModule.cmake b/src/cmake/AddMPUnitsModule.cmake index be007c90..87457db2 100644 --- a/src/cmake/AddMPUnitsModule.cmake +++ b/src/cmake/AddMPUnitsModule.cmake @@ -54,7 +54,7 @@ function(add_mp_units_module name target_name) # validate and process arguments validate_unparsed(${name} ARG) - validate_arguments_exists(${name} ARG DEPENDENCIES MODULE_INTERFACE_UNIT) + validate_arguments_exists(${name} ARG MODULE_INTERFACE_UNIT) if(${projectPrefix}TARGET_SCOPE STREQUAL INTERFACE) set(SCOPE "INTERFACE") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ed4525f3..8c6aada0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -31,15 +31,9 @@ function(set_feature_flag name) endif() endfunction() -# find dependencies -if(NOT TARGET gsl::gsl-lite) - find_package(gsl-lite REQUIRED) -endif() - # core library definition add_mp_units_module( core mp-units-core - DEPENDENCIES gsl::gsl-lite HEADERS include/mp-units/bits/core_gmf.h include/mp-units/bits/fmt.h include/mp-units/bits/get_associated_quantity.h @@ -91,6 +85,7 @@ set_feature_flag(API_STD_FORMAT) set_feature_flag(API_STRING_VIEW_RET) set_feature_flag(API_NO_CRTP) +# Text formatting if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO" AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED) ) @@ -100,6 +95,24 @@ if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_F target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} fmt::fmt) endif() +# Contracts checking +if(${projectPrefix}API_CONTRACTS STREQUAL "NONE") + target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=0) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE") + if(NOT TARGET gsl::gsl-lite) + find_package(gsl-lite REQUIRED) + endif() + target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} gsl::gsl-lite) + target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=2) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL") + if(NOT TARGET Microsoft.GSL::GSL) + find_package(Microsoft.GSL REQUIRED) + endif() + target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} Microsoft.GSL::GSL) + target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=3) +endif() + +# C++20 modules if(${projectPrefix}BUILD_CXX_MODULES) if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18) @@ -112,6 +125,7 @@ if(${projectPrefix}BUILD_CXX_MODULES) endif() endif() +# UTF-8 source and execution character set if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options( mp-units-core ${${projectPrefix}TARGET_SCOPE} diff --git a/src/core/include/mp-units/bits/core_gmf.h b/src/core/include/mp-units/bits/core_gmf.h index a64e46b1..f7e4b9ba 100644 --- a/src/core/include/mp-units/bits/core_gmf.h +++ b/src/core/include/mp-units/bits/core_gmf.h @@ -22,7 +22,6 @@ #pragma once -#include #include #include #include diff --git a/src/core/include/mp-units/bits/fmt.h b/src/core/include/mp-units/bits/fmt.h index c4848d67..8e84ac0b 100644 --- a/src/core/include/mp-units/bits/fmt.h +++ b/src/core/include/mp-units/bits/fmt.h @@ -35,7 +35,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include #include #include @@ -138,7 +137,7 @@ template template [[nodiscard]] constexpr std::make_unsigned_t to_unsigned(Int value) { - gsl_Expects(std::is_unsigned_v || value >= 0); + MP_UNITS_EXPECTS(std::is_unsigned_v || value >= 0); return static_cast>(value); } @@ -201,7 +200,7 @@ MP_UNITS_EXPORT_END template [[nodiscard]] constexpr int parse_nonnegative_int(const Char*& begin, const Char* end, int error_value) { - gsl_Expects(begin != end && '0' <= *begin && *begin <= '9'); + MP_UNITS_EXPECTS(begin != end && '0' <= *begin && *begin <= '9'); unsigned value = 0, prev = 0; auto p = begin; do { @@ -262,7 +261,7 @@ template template [[nodiscard]] constexpr const Char* parse_arg_id(const Char* begin, const Char* end, Handler& handler) { - gsl_Expects(begin != end); + MP_UNITS_EXPECTS(begin != end); Char c = *begin; if (c != '}' && c != ':') return ::mp_units::detail::do_parse_arg_id(begin, end, handler); handler.on_auto(); @@ -304,7 +303,7 @@ template fmt_arg_ref& ref, MP_UNITS_STD_FMT::basic_format_parse_context& ctx) { - gsl_Expects(begin != end); + MP_UNITS_EXPECTS(begin != end); if ('0' <= *begin && *begin <= '9') { const int val = ::mp_units::detail::parse_nonnegative_int(begin, end, -1); if (val != -1) @@ -341,7 +340,7 @@ template [[nodiscard]] constexpr const Char* parse_align(const Char* begin, const Char* end, Specs& specs, fmt_align default_align = fmt_align::none) { - gsl_Expects(begin != end); + MP_UNITS_EXPECTS(begin != end); auto align = fmt_align::none; auto p = begin + code_point_length(begin); if (end - p <= 0) p = begin; diff --git a/src/core/include/mp-units/bits/ratio.h b/src/core/include/mp-units/bits/ratio.h index 96c67747..bcf778dd 100644 --- a/src/core/include/mp-units/bits/ratio.h +++ b/src/core/include/mp-units/bits/ratio.h @@ -24,9 +24,9 @@ #include #include +#include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #include @@ -44,16 +44,16 @@ template { constexpr std::intmax_t c = std::uintmax_t{1} << (sizeof(std::intmax_t) * 4); - const std::intmax_t a0 = abs(lhs) % c; - const std::intmax_t a1 = abs(lhs) / c; - const std::intmax_t b0 = abs(rhs) % c; - const std::intmax_t b1 = abs(rhs) / c; + [[maybe_unused]] const std::intmax_t a0 = abs(lhs) % c; + [[maybe_unused]] const std::intmax_t a1 = abs(lhs) / c; + [[maybe_unused]] const std::intmax_t b0 = abs(rhs) % c; + [[maybe_unused]] const std::intmax_t b1 = abs(rhs) / c; // overflow in multiplication - gsl_Assert(a1 == 0 || b1 == 0); - gsl_Assert(a0 * b1 + b0 * a1 < (c >> 1)); // NOLINT(hicpp-signed-bitwise) - gsl_Assert(b0 * a0 <= INTMAX_MAX); - gsl_Assert((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); + MP_UNITS_ASSERT(a1 == 0 || b1 == 0); + MP_UNITS_ASSERT(a0 * b1 + b0 * a1 < (c >> 1)); // NOLINT(hicpp-signed-bitwise) + MP_UNITS_ASSERT(b0 * a0 <= INTMAX_MAX); + MP_UNITS_ASSERT((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); return lhs * rhs; } @@ -72,7 +72,7 @@ MP_UNITS_EXPORT struct ratio { // NOLINTNEXTLINE(bugprone-easily-swappable-parameters, google-explicit-constructor, hicpp-explicit-conversions) MP_UNITS_CONSTEVAL explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num{n}, den{d} { - gsl_Expects(den != 0); + MP_UNITS_EXPECTS(den != 0); if (num == 0) den = 1; else { @@ -112,9 +112,9 @@ MP_UNITS_EXPORT struct ratio { if (r1.num == r2.num && r1.den == r2.den) return ratio{r1.num, r1.den}; // gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d - gsl_Assert(std::numeric_limits::max() / r1.num > r2.den); - gsl_Assert(std::numeric_limits::max() / r2.num > r1.den); - gsl_Assert(std::numeric_limits::max() / r1.den > r2.den); + MP_UNITS_ASSERT(std::numeric_limits::max() / r1.num > r2.den); + MP_UNITS_ASSERT(std::numeric_limits::max() / r2.num > r1.den); + MP_UNITS_ASSERT(std::numeric_limits::max() / r1.den > r2.den); const std::intmax_t num = std::gcd(r1.num * r2.den, r2.num * r1.den); const std::intmax_t den = r1.den * r2.den; diff --git a/src/core/include/mp-units/compat_macros.h b/src/core/include/mp-units/compat_macros.h index 227551c7..a9c21813 100644 --- a/src/core/include/mp-units/compat_macros.h +++ b/src/core/include/mp-units/compat_macros.h @@ -102,5 +102,37 @@ MP_UNITS_DIAGNOSTIC_POP #endif // IWYU pragma: end_exports +#endif + +#if MP_UNITS_API_CONTRACTS == 2 || __has_include() + +#include + +#define MP_UNITS_EXPECTS(expr) gsl_Expects(expr) +#define MP_UNITS_EXPECTS_DEBUG(expr) gsl_ExpectsDebug(expr) +#define MP_UNITS_ASSERT(expr) gsl_Assert(expr) +#define MP_UNITS_ASSERT_DEBUG(expr) gsl_AssertDebug(expr) + +#elif MP_UNITS_API_CONTRACTS == 3 || __has_include() + +#include +#include + +#define MP_UNITS_EXPECTS(expr) Expects(expr) +#if defined NDEBUG +#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast(0) +#else +#define MP_UNITS_EXPECTS_DEBUG(expr) Expects(expr) +#endif +#define MP_UNITS_ASSERT(expr) Expects(expr) +#define MP_UNITS_ASSERT_DEBUG(expr) assert(expr) + +#else + +#define MP_UNITS_EXPECTS(expr) static_cast(0) +#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast(0) +#define MP_UNITS_ASSERT(expr) static_cast(0) +#define MP_UNITS_ASSERT_DEBUG(expr) static_cast(0) + #endif // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 1b1d13cf..c67332bd 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -32,7 +32,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #include @@ -78,7 +77,7 @@ public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) consteval explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept { - gsl_Expects(txt[N] == CharT{}); + MP_UNITS_EXPECTS(txt[N] == CharT{}); for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i]; } @@ -86,7 +85,7 @@ public: requires std::convertible_to, CharT> constexpr basic_fixed_string(It begin, S end) { - gsl_Expects(std::distance(begin, end) == N); + MP_UNITS_EXPECTS(std::distance(begin, end) == N); for (auto it = data_; begin != end; ++begin, ++it) *it = *begin; } @@ -94,7 +93,7 @@ public: requires std::convertible_to, CharT> constexpr basic_fixed_string(std::from_range_t, R&& r) { - gsl_Expects(std::ranges::size(r) == N); + MP_UNITS_EXPECTS(std::ranges::size(r) == N); for (auto it = data_; auto&& v : std::forward(r)) *it++ = std::forward(v); } @@ -120,7 +119,7 @@ public: // element access [[nodiscard]] constexpr const_reference operator[](size_type pos) const { - gsl_Expects(pos < N); + MP_UNITS_EXPECTS(pos < N); return data()[pos]; } @@ -131,12 +130,12 @@ public: } [[nodiscard]] constexpr const_reference front() const { - gsl_Expects(!empty()); + MP_UNITS_EXPECTS(!empty()); return (*this)[0]; } [[nodiscard]] constexpr const_reference back() const { - gsl_Expects(!empty()); + MP_UNITS_EXPECTS(!empty()); return (*this)[N - 1]; } @@ -191,7 +190,7 @@ public: [[nodiscard]] consteval friend basic_fixed_string operator+( const basic_fixed_string& lhs, const CharT (&rhs)[N2]) noexcept { - gsl_Expects(rhs[N2 - 1] == CharT{}); + MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{}); CharT txt[N + N2]; CharT* it = txt; for (CharT c : lhs) *it++ = c; @@ -203,7 +202,7 @@ public: [[nodiscard]] consteval friend basic_fixed_string operator+( const CharT (&lhs)[N1], const basic_fixed_string& rhs) noexcept { - gsl_Expects(lhs[N1 - 1] == CharT{}); + MP_UNITS_EXPECTS(lhs[N1 - 1] == CharT{}); CharT txt[N1 + N]; CharT* it = txt; for (size_t i = 0; i != N1 - 1; ++i) *it++ = lhs[i]; @@ -222,7 +221,7 @@ public: template [[nodiscard]] friend consteval bool operator==(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) { - gsl_Expects(rhs[N2 - 1] == CharT{}); + MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{}); return lhs.view() == std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); } @@ -235,7 +234,7 @@ public: template [[nodiscard]] friend consteval auto operator<=>(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) { - gsl_Expects(rhs[N2 - 1] == CharT{}); + MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{}); return lhs.view() <=> std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); } diff --git a/src/core/include/mp-units/framework/dimension.h b/src/core/include/mp-units/framework/dimension.h index a708afc2..efa0810a 100644 --- a/src/core/include/mp-units/framework/dimension.h +++ b/src/core/include/mp-units/framework/dimension.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include #include #include @@ -280,7 +280,8 @@ template Out, typename... Expr> constexpr Out dimension_symbol_impl(Out out, const derived_dimension&, const dimension_symbol_formatting& fmt, bool negative_power) { - gsl_Expects(negative_power == false); + (void)negative_power; + MP_UNITS_EXPECTS(negative_power == false); return dimension_symbol_impl(out, typename derived_dimension::_num_{}, typename derived_dimension::_den_{}, fmt); } diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index a4880f48..f67a79bd 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -26,6 +26,7 @@ // IWYU pragma: private, include #include #include +#include #include #include #include @@ -36,7 +37,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #endif @@ -353,7 +353,7 @@ public: friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity& rhs) { - gsl_ExpectsDebug(rhs != zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != zero()); lhs.numerical_value_is_an_implementation_detail_ %= rhs.numerical_value_is_an_implementation_detail_; return std::forward(lhs); } @@ -393,7 +393,7 @@ public: } friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v) { - gsl_ExpectsDebug(v != quantity_values::zero()); + MP_UNITS_EXPECTS_DEBUG(v != quantity_values::zero()); lhs.numerical_value_is_an_implementation_detail_ /= v; return std::forward(lhs); } @@ -407,7 +407,7 @@ public: } friend constexpr decltype(auto) operator/=(Q1&& lhs, const Q2& rhs) { - gsl_ExpectsDebug(rhs != rhs.zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero()); lhs.numerical_value_is_an_implementation_detail_ /= rhs.numerical_value_is_an_implementation_detail_; return std::forward(lhs); } @@ -451,7 +451,7 @@ template detail::CommonlyInvocableQuantities, quantity, quantity> [[nodiscard]] constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) { - gsl_ExpectsDebug(rhs != rhs.zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero()); using ret = detail::common_quantity_for, quantity, quantity>; const ret ret_lhs(lhs); const ret ret_rhs(rhs); @@ -486,7 +486,7 @@ template requires detail::InvocableQuantities, quantity, quantity> [[nodiscard]] constexpr Quantity auto operator/(const quantity& lhs, const quantity& rhs) { - gsl_ExpectsDebug(rhs != rhs.zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero()); return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), R1 / R2}; } @@ -495,7 +495,7 @@ template detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] constexpr QuantityOf auto operator/(const quantity& q, const Value& v) { - gsl_ExpectsDebug(v != quantity_values::zero()); + MP_UNITS_EXPECTS_DEBUG(v != quantity_values::zero()); return quantity{q.numerical_value_ref_in(get_unit(R)) / v, R}; } diff --git a/src/core/include/mp-units/framework/symbol_text.h b/src/core/include/mp-units/framework/symbol_text.h index f7c3efaa..40f93a9a 100644 --- a/src/core/include/mp-units/framework/symbol_text.h +++ b/src/core/include/mp-units/framework/symbol_text.h @@ -27,11 +27,11 @@ // TODO use when moved to C++20 modules (parsing takes too long for each translation unit) #include #include +#include #include #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #include @@ -95,34 +95,34 @@ public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(false) symbol_text(char ch) : unicode_(static_cast(ch)), ascii_(ch) { - gsl_Expects(detail::is_basic_literal_character_set_char(ch)); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set_char(ch)); } // NOLINTNEXTLINE(*-avoid-c-arrays, google-explicit-constructor, hicpp-explicit-conversions) consteval explicit(false) symbol_text(const char (&txt)[N + 1]) : unicode_(detail::to_u8string(basic_fixed_string{txt})), ascii_(txt) { - gsl_Expects(txt[N] == char{}); - gsl_Expects(detail::is_basic_literal_character_set(txt)); + MP_UNITS_EXPECTS(txt[N] == char{}); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(txt)); } // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(false) symbol_text(const fixed_string& txt) : unicode_(detail::to_u8string(txt)), ascii_(txt) { - gsl_Expects(detail::is_basic_literal_character_set(txt.data_)); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(txt.data_)); } // NOLINTNEXTLINE(*-avoid-c-arrays) consteval symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a) { - gsl_Expects(u[N] == char8_t{}); - gsl_Expects(a[M] == char{}); - gsl_Expects(detail::is_basic_literal_character_set(a)); + MP_UNITS_EXPECTS(u[N] == char8_t{}); + MP_UNITS_EXPECTS(a[M] == char{}); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(a)); } constexpr symbol_text(const fixed_u8string& u, const fixed_string& a) : unicode_(u), ascii_(a) { - gsl_Expects(detail::is_basic_literal_character_set(a.data_)); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(a.data_)); } [[nodiscard]] constexpr const auto& unicode() const { return unicode_; } @@ -130,7 +130,7 @@ public: [[nodiscard]] constexpr bool empty() const { - gsl_AssertDebug(unicode().empty() == ascii().empty()); + MP_UNITS_ASSERT_DEBUG(unicode().empty() == ascii().empty()); return unicode().empty(); } diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 8e8ad5df..bc9cdf58 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include #include #include @@ -808,7 +808,8 @@ template Out, typename... Expr> constexpr Out unit_symbol_impl(Out out, const derived_unit&, const unit_symbol_formatting& fmt, bool negative_power) { - gsl_Expects(negative_power == false); + (void)negative_power; + MP_UNITS_EXPECTS(negative_power == false); return unit_symbol_impl(out, typename derived_unit::_num_{}, typename derived_unit::_den_{}, fmt); } diff --git a/src/mp-unitsConfig.cmake b/src/mp-unitsConfig.cmake index b42e3b55..017d6131 100644 --- a/src/mp-unitsConfig.cmake +++ b/src/mp-unitsConfig.cmake @@ -26,6 +26,10 @@ if(NOT MP_UNITS_API_STD_FORMAT) find_dependency(fmt) endif() -find_dependency(gsl-lite) +if(MP_UNITS_API_CONTRACTS STREQUAL "GSL-LITE") + find_dependency(gsl-lite) +elseif(MP_UNITS_API_CONTRACTS STREQUAL "MS-GSL") + find_dependency(Microsoft.GSL) +endif() include("${CMAKE_CURRENT_LIST_DIR}/mp-unitsTargets.cmake") diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index 074aa5fd..12ef88bd 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -28,3 +28,11 @@ find_package(mp-units REQUIRED) add_executable(test_package test_package.cpp) target_link_libraries(test_package PRIVATE mp-units::mp-units) target_compile_definitions(test_package PRIVATE MP_UNITS_API_STD_FORMAT=$) + +if(MP_UNITS_API_CONTRACTS STREQUAL "NONE") + target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=0) +elseif(MP_UNITS_API_CONTRACTS STREQUAL "GSL-LITE") + target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=2) +elseif(MP_UNITS_API_CONTRACTS STREQUAL "MS-GSL") + target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=3) +endif() From 641a743684d23bd72fcefe81ec550f6c618426d3 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 12:54:34 +0200 Subject: [PATCH 33/61] refactor: `terminate` replaced with `abort` and a header file added --- src/core/include/mp-units/bits/core_gmf.h | 1 + src/core/include/mp-units/framework/magnitude.h | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/include/mp-units/bits/core_gmf.h b/src/core/include/mp-units/bits/core_gmf.h index f7e4b9ba..500ef701 100644 --- a/src/core/include/mp-units/bits/core_gmf.h +++ b/src/core/include/mp-units/bits/core_gmf.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/src/core/include/mp-units/framework/magnitude.h b/src/core/include/mp-units/framework/magnitude.h index 1653ad12..5fd09326 100644 --- a/src/core/include/mp-units/framework/magnitude.h +++ b/src/core/include/mp-units/framework/magnitude.h @@ -38,6 +38,7 @@ #ifndef MP_UNITS_IN_MODULE_INTERFACE #include #include +#include #include #include #endif @@ -278,7 +279,7 @@ template // As this function should only be called at compile time, the terminations herein function as // "parameter-compatible static_asserts", and should not result in terminations at runtime. if (exp < 0) { - std::terminate(); // int_power only supports positive integer powers + std::abort(); // int_power only supports positive integer powers } constexpr auto checked_multiply = [](auto a, auto b) { @@ -286,7 +287,7 @@ template MP_UNITS_DIAGNOSTIC_PUSH MP_UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL if (result / a != b) { - std::terminate(); // Wraparound detected + std::abort(); // Wraparound detected } MP_UNITS_DIAGNOSTIC_POP return result; @@ -319,12 +320,12 @@ template // terminations is to act as "static_assert substitutes", not to actually terminate at runtime. const auto exp = get_exponent(el); if (exp.den != 1) { - std::terminate(); // Rational powers not yet supported + std::abort(); // Rational powers not yet supported } if (exp.num < 0) { if constexpr (std::is_integral_v) { - std::terminate(); // Cannot represent reciprocal as integer + std::abort(); // Cannot represent reciprocal as integer } else { return T{1} / compute_base_power(inverse(el)); } @@ -347,7 +348,7 @@ template // to produce compiler errors, because we cannot `static_assert` on function arguments. if constexpr (std::is_integral_v) { if (!std::in_range(x)) { - std::terminate(); // Cannot represent magnitude in this type + std::abort(); // Cannot represent magnitude in this type } } From 0889286b12ad1c79f021f6324e7cdd15dc28116b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 13:11:39 +0200 Subject: [PATCH 34/61] fix(conan): `core` component added unconditionally --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index c7be62dd..de466486 100644 --- a/conanfile.py +++ b/conanfile.py @@ -271,6 +271,7 @@ class MPUnitsConan(ConanFile): def package_info(self): compiler = self.settings.compiler + self.cpp_info.components["core"] if self.options.contracts == "gsl-lite": self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] elif self.options.contracts == "ms-gsl": From 15d981ba3fd787d4afaff17bc68b50bb53952005 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 14:18:38 +0200 Subject: [PATCH 35/61] docs: CHANGELOG updatd --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce89ea18..68a45116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,17 @@ - (!) feat: formatting grammar improved and units formatting support added - (!) feat: `has_unit_symbol` support removed - (!) feat: ABI concerns resolved with introduction of u8 strings for symbols +- (!) feat: API-related Conan, CMake, and preprocessor options redesigned +- (!) feat: :boom: `core.h` removed - feat: implicit point origins support added - feat: unit default point origin support added - feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by [@NAThompson](https://github.com/NAThompson) +- feat: `fma` for quantity points added - feat: `quantity_point` support added for `quantity_cast` and `value_cast` - feat: `value_cast` added - feat: `interconvertible(QuantitySpec, QuantitySpec)` added - feat: `qp.quantity_from_zero()` added -- feat: `underlying_type` type trait added +- feat: `value_type` type trait added - feat: do not print space between a number and `percent` or `per_mille` - feat: `ppm` parts per million added by [@nebkat](https://github.com/nebkat) - feat: `atan2` 2-argument arctangent added by [@nebkat](https://github.com/nebkat) @@ -25,6 +28,10 @@ - feat: unit text output support added - feat: formatting error messages improved - feat: improve types readability by eliminating extraneous `()` in references, prefixes, and `kind_of` +- feat: dimension text output added +- feat: some light and radiation ISQ quantities added +- feat: New formatting specification implemented +- feat: allow configuring GSL library use - (!) refactor: `zero_Fahrenheit` renamed to `zeroth_degree_Fahrenheit` - (!) refactor: SI-related trigonometric functions moved to the `si` subnamespace - (!) refactor: `math.h` header file broke up to smaller pieces @@ -32,15 +39,28 @@ - (!) refactor: `ReferenceOf` does not take a dimension anymore - (!) refactor: 'o' replaced with '1' as a modifier for `unit_symbol_solidus::one_denominator` - (!) refactor: `get_kind()` now returns `kind_of` +- (!) refactor: FMT macros moved to `compat_macros.h` +- (!) refactor: `fixed_string` refactored to reflect the latest changes to [P3094R2](https://wg21.link/P3094R2) +- (!) refactor: `basic_symbol_text` renamed to `symbol_text` +- (!) refactor: `ratio` hidden as an implementation detail behind `mag_ratio` +- (!) refactor: `framework.h` introduced +- (!) refactor: type list tools made an implementation detail of the library +- (!) refactor: header files with the entire system definitions moved up in the directory tree - refactor: math functions constraints refactored - refactor: `si_quantities.h` added to improve compile-times - refactor: `validate_ascii_string` refactored to `is_basic_literal_character_set` +- refactor: `underlying_type` split to `wrapped_type` and `value_type` and used in code +- refactor: code refactored to comply with clang-tidy +- refactor: remove dependency on `` header and switch to use an iterator-based `copy` algorithm +- refactor: `terminate` replaced with `abort` and a header file added - fix: `QuantityLike` conversions required `Q::rep` instead of using one provided by `quantity_like_traits` - fix: `QuantitySpec[Unit]` replaced with `make_reference` in `value_cast` - fix: `ice_point` is now defined with the integral offset from `absolute_zero` - fix: performance regression in `sudo_cast` fixed - fix: explicit object parameter support fixed - fix: missing `version` header file added to `hacks.h` +- fix: `quantity_cast` to accept lvalue references (thanks [@burnpanck](https://github.com/burnpanck)) +- fix: `value_cast` with lvalue references to `quantity_point` (thanks [@burnpanck](https://github.com/burnpanck)) - docs: project blog and first posts added - docs: project documentation layout refactored - docs: "Interoperability with Other Libraries" chapter added @@ -55,16 +75,27 @@ - docs: mkdocs social plugin enabled - docs: project logo and custom color scheme added - docs: minimum compiler requirements updated +- docs: unit symbols admonition extended in the "Quick Start" chapter - docs: Cairo dependency described in the MkDocs section +- docs: "hello units" example updated with dimensions output +- docs: "Text Output" chapter updated with the recent formatting changes +- docs: formatting grammar language changed to EBNF +- docs: "Project structure" chapter expanded - (!) build: Conan and CMake options refactored -- (!) build: `MP_UNITS_AS_SYSTEM_HEADERS` support removed +- (!) build: `MP_UNITS_AS_SYSTEM_HEADERS` renamed to `MP_UNITS_BUILD_AS_SYSTEM_HEADERS` +- (!) build: `MP_UNITS_BUILD_LA` and `MP_UNITS_IWYU` CMake options now have `_DEV_` in the name - build: gsl-lite updated to 0.41.0 - build: catch2 updated to 3.5.1 - build: fmt updated to 10.2.1 - build: gitpod environment updated - build: `check_cxx_feature_supported` added +- build: IWYU path handling fixed +- build: IWYU enabled on GCC +- build: `CMAKE_EXPORT_COMPILE_COMMANDS` flag enabled for the developer's build - build(conan): `generate()` now set `cache_variables` - build(conan): `can_run` check added before running tests +- ci: Conan and CMake CI now use different cache names +- ci: gcc-14 added ### 2.1.1 May 16, 2024 { id="2.1.1" } From ad15bb97c1f70c171f8d9336f55c2c7d8355edc1 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 14:24:35 +0200 Subject: [PATCH 36/61] docs: 2.2 release announcement updated --- docs/blog/posts/2.2.0-released.md | 42 +++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/docs/blog/posts/2.2.0-released.md b/docs/blog/posts/2.2.0-released.md index 2ea97e73..84dbb07d 100644 --- a/docs/blog/posts/2.2.0-released.md +++ b/docs/blog/posts/2.2.0-released.md @@ -31,12 +31,12 @@ the C++ modules' support is still really limited. To benefit from C++ modules, we need at least: - - CMake 3.28.1 + - CMake 3.29.3 - Ninja 1.11 - clang-17 -In the upcoming months, hopefully, the situation will improve with the gcc-14 release and -bug fixes in MSVC. +In the upcoming months, hopefully, the situation will improve with the bug fixes in +CMake, gcc-14, and MSVC. !!! note @@ -64,6 +64,11 @@ flowchart TD The easiest way to use them is just to `import mp_units;` at the beginning of your translation unit (see the [Quick Start](../../getting_started/quick_start.md) chapter for some usage examples). +!!! note + + C++20 modules support still have some issues when imported from the installed CMake target. + See the following [GitLab issue for more details](https://gitlab.kitware.com/cmake/cmake/-/issues/25909#note_1525377). + In this release, we also highly limited the number of CMake targets (:boom: **breaking change** :boom:). Now, they correspond exactly to the C++ modules they provide. This means that many smaller partial targets were removed. We also merged text output targets with the core library's definition. @@ -93,9 +98,9 @@ In version 2.2, the following headers have a new location or contents: Benefiting from this opportunity, we also cleaned up core and systems definitions (:boom: **breaking change** :boom:). -Regarding the library's core, we exposed only one header `framework.h` that exposes all of -the library's framework so the user does not have to enumerate files like `unit.h`, `quantity.h`, -and `quantity_point.h` anymore. Those headers are not gone, they were put to +Regarding the library's core, we removed `core.h` and exposed only one header `framework.h` that +provides all of the library's framework so the user does not have to enumerate files like `unit.h`, +`quantity.h`, and `quantity_point.h` anymore. Those headers are not gone, they were put to the `mp-units/framework` subheader. So they are still there if you really need them. Regarding the changes in systems definitions, we moved the wrapper header files with the entire @@ -153,6 +158,11 @@ three values: - `False` - The feature is disabled, and an older alternative is always used. - `Auto` - The feature is automatically enabled if the compiler supports it (old behavior). +Before this release, the library always depended on [gsl-lite](https://github.com/gsl-lite/gsl-lite) +to perform runtime contract and asserts checking. In this release we introduced new options +to control if contract checking should be based on [gsl-lite](https://github.com/gsl-lite/gsl-lite), +[ms-gsl](https://github.com/microsoft/GSL), or maybe completely disabled. + Additionally, some CMake options were renamed to better express the impact on our users (:boom: **breaking change** :boom:). For example, now CMake options include: @@ -191,7 +201,7 @@ origins. For example: ``` As we can see above, the new design allows -[direct-initialization](https://en.cppreference.com/w/cpp/language/direct_initialization) of a +[direct-initializing](https://en.cppreference.com/w/cpp/language/direct_initialization) `quantity_point` class template from a `quantity`, but only if the former one is defined in terms of the implicit point origin. Otherwise, an explicit origin still always has to be provided during initialization. @@ -199,7 +209,7 @@ initialization. Also, we introduced the possibility of specifying a default point origin in the unit definition. With that, we could provide proper temperature scales without forcing the user to always use the origins explicitly. Also, a new member function, `.quantity_from_zero(),` was introduced -that always returns the quantity from the unit's specific point origin or from the absolute +that always returns the quantity from the unit's specific point origin or from the implicit point origin otherwise. === "Now" @@ -244,20 +254,30 @@ named with its corresponding unit and with the `si::zeroth_degree_Celsius` about potential ABI issues when different translation units are compiled with different ordinary literal encodings. Those issues were resolved with a change to units definitions (:boom: **breaking change** :boom:). It affects only units that specify both Unicode and ASCII -symbols. The new design requires the Unicode symbol to be provided as a UTF-8 literal: +symbols. The new design requires the Unicode symbol to be provided as a UTF-8 literal. + +This also means that the `basic_symbol_text` has fixed character types for both symbols. This +is why it was renamed to `symbol_text` (:boom: **breaking change** :boom:). === "Now" ```cpp - inline constexpr struct ohm : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm; + inline constexpr struct ohm : named_unit {} ohm; ``` === "Before" ```cpp - inline constexpr struct ohm : named_unit<{"Ω", "ohm"}, volt / ampere> {} ohm; + inline constexpr struct ohm : named_unit {} ohm; ``` +!!! note + + On C++20-compliant compilers it should be enough to type the following in the unit's definition: + + ```cpp + inline constexpr struct ohm : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm; + ``` ## Improved text output From 28993d27b15f1f7f7e1d950b18712dd4aec17345 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 14:53:45 +0200 Subject: [PATCH 37/61] ci: cleanup of Conan local cache --- .github/workflows/ci-conan.yml | 4 ++-- .github/workflows/ci-test-package-cmake.yml | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 1526a2c5..44bdf8e4 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -260,8 +260,8 @@ jobs: shell: bash run: | conan remove mp-units --confirm - conan remove *#!latest --confirm - conan remove *:*#!latest --confirm + conan remove *#~latest --confirm + conan remove *:*#~latest --confirm conan cache clean "*" -s -b -d outputs: package_ref: ${{ steps.get-package-ref.outputs.PACKAGE_REF }} diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index ea812567..024ad0ac 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -306,3 +306,9 @@ jobs: working-directory: test_package/build/install/${{ matrix.build_type }} run: | ./test_package + - name: Clean Conan cache before backup + shell: bash + run: | + conan remove *#~latest --confirm + conan remove *:*#~latest --confirm + conan cache clean "*" -s -b -d From 6486c85773e80b257dc3bbb5535f1acb001a2ddd Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 15:00:25 +0200 Subject: [PATCH 38/61] ci: `clang-tidy` CI added --- .github/workflows/ci-clang-tidy.yml | 144 ++++++++++++++++++ README.md | 1 + conanfile.py | 6 + .../getting_started/installation_and_usage.md | 8 + 4 files changed, 159 insertions(+) create mode 100644 .github/workflows/ci-clang-tidy.yml diff --git a/.github/workflows/ci-clang-tidy.yml b/.github/workflows/ci-clang-tidy.yml new file mode 100644 index 00000000..284803a7 --- /dev/null +++ b/.github/workflows/ci-clang-tidy.yml @@ -0,0 +1,144 @@ +# 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. + +name: clang-tidy + +on: + push: + paths-ignore: + - "docs/**" + pull_request: + paths-ignore: + - "docs/**" + +env: + CHANNEL: ${{ fromJSON('["testing", "stable"]')[github.ref_type == 'tag' && startsWith(github.ref_name, 'v')] }} + +jobs: + build: + name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + formatting: ["std::format", "fmtlib"] + contracts: ["none", "gsl-lite", "ms-gsl"] + std: [20, 23] + config: + - { + name: "Clang-18", + os: ubuntu-24.04, + compiler: + { + type: CLANG, + version: 18, + cc: "clang-18", + cxx: "clang++-18", + }, + lib: "libc++", + cxx_modules: "False", + std_format_support: "True", + conan-config: "", + } + build_type: ["Release", "Debug"] + exclude: + - formatting: "std::format" + config: { std_format_support: "False" } + + env: + CC: ${{ matrix.config.compiler.cc }} + CXX: ${{ matrix.config.compiler.cxx }} + + steps: + - uses: actions/checkout@v4 + - run: echo "cache_id=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_ENV + - name: Cache Conan data + uses: actions/cache@v4 + if: always() + env: + cache-name: cache-conan-data + with: + path: ~/.conan2/p + key: clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} + restore-keys: | + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}- + clang-tidy-${{ matrix.config.os }}- + - uses: hendrikmuhs/ccache-action@v1.2 + if: runner.os == 'Linux' + with: + key: ${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }} + max-size: 50M + - name: Install Clang + if: matrix.config.compiler.type == 'CLANG' + shell: bash + working-directory: ${{ env.HOME }} + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh ${{ matrix.config.compiler.version }} + sudo apt install -y clang-tools-${{ matrix.config.compiler.version }} + - name: Install Libc++ + if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' + shell: bash + run: | + sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Install Ninja + shell: bash + run: | + pip install -U ninja + - name: Install Conan + shell: bash + run: | + pip install -U conan + - name: Configure Conan + shell: bash + run: | + conan profile detect --force + if [[ "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.libcxx=.*/compiler.libcxx=${{ matrix.config.lib }}/' ~/.conan2/profiles/default + fi + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default + sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default + conan profile show -pr default + - run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV + - name: Run clang-tidy + shell: bash + run: | + conan build . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ + -c user.mp-units.build:all=True -c user.mp-units.analyze:clang-tidy=True \ + -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} ${{ matrix.config.conan-config }} + - name: Clean Conan cache before backup + shell: bash + run: | + conan remove *#~latest --confirm + conan remove *:*#~latest --confirm + conan cache clean "*" -s -b -d diff --git a/README.md b/README.md index dd6ea350..3b334e98 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster) [![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster) +[![clang-tidy](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-clang-tidy.yml?branch=master&label=clang-tidy)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22clang-tidy%20CI%22+branch%3Amaster) [![Formatting CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-formatting.yml?branch=master&label=Formatting%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Formatting%20CI%22+branch%3Amaster) [![GitHub Workflow Documentation](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/documentation.yml?branch=master&label=Documentation)](https://github.com/mpusz/mp-units/actions?query=workflow%3ADocumentation+branch%3Amaster) diff --git a/conanfile.py b/conanfile.py index de466486..50855bd9 100644 --- a/conanfile.py +++ b/conanfile.py @@ -193,6 +193,10 @@ class MPUnitsConan(ConanFile): def _skip_la(self): return bool(self.conf.get("user.mp-units.build:skip_la", default=False)) + @property + def _run_clang_tidy(self): + return bool(self.conf.get("user.mp-units.analyze:clang-tidy", default=False)) + def set_version(self): content = load(self, os.path.join(self.recipe_folder, "src/CMakeLists.txt")) version = re.search( @@ -230,6 +234,8 @@ class MPUnitsConan(ConanFile): tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = True tc.cache_variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = True tc.cache_variables["MP_UNITS_DEV_BUILD_LA"] = not self._skip_la + if self._run_clang_tidy: + tc.cache_variables["MP_UNITS_DEV_CLANG_TIDY"] = True if self._build_cxx_modules: tc.cache_variables["CMAKE_CXX_SCAN_FOR_MODULES"] = True tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = str( diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index e21dd6ce..20d3c788 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -303,6 +303,14 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan skip la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`user.mp-units.analyze:clang-tidy`](#user.mp-units.analyze-clang-tidy){ #user.mp-units.analyze-clang-tidy } + +: [:octicons-tag-24: 2.2.0][conan clang-tidy support] · :octicons-milestone-24: `True`/`False` (Default: `False`) + + Enables clang-tidy analysis. + + [conan clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 + ### CMake options [`MP_UNITS_BUILD_AS_SYSTEM_HEADERS`](#MP_UNITS_BUILD_AS_SYSTEM_HEADERS){ #MP_UNITS_BUILD_AS_SYSTEM_HEADERS } From c151740a43d829ab2e184af3fead3708595d99dc Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 15:29:02 +0200 Subject: [PATCH 39/61] ci: codeql actions updated to v3 --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index eb45fb53..072cb7e9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -59,7 +59,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -74,7 +74,7 @@ jobs: # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild if: matrix.language != 'cpp' - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -108,6 +108,6 @@ jobs: conan build .. -s compiler.cppstd=20 -c user.mp-units.build:all=True -o std_format=False -b missing - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" From cd36e6f9746299e78992da69417b6a2d5f2bbe2f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 19:50:02 +0200 Subject: [PATCH 40/61] feat: freestanding support added Resolves #564, #565, and #556 --- .github/workflows/ci-freestanding.yml | 152 ++++++++++++++++++ CMakeLists.txt | 6 +- conanfile.py | 7 + src/CMakeLists.txt | 7 + src/core/CMakeLists.txt | 28 +++- src/core/include/mp-units/bits/core_gmf.h | 16 +- src/core/include/mp-units/bits/hacks.h | 4 + .../include/mp-units/bits/requires_hosted.h | 29 ++++ src/core/include/mp-units/bits/text_tools.h | 5 +- src/core/include/mp-units/compat_macros.h | 31 ++-- src/core/include/mp-units/ext/fixed_string.h | 13 +- src/core/include/mp-units/format.h | 2 + .../include/mp-units/framework/dimension.h | 10 +- src/core/include/mp-units/framework/unit.h | 14 +- src/core/include/mp-units/math.h | 2 + src/core/include/mp-units/ostream.h | 2 + src/core/mp-units-core.cpp | 5 +- src/systems/CMakeLists.txt | 19 ++- .../include/mp-units/systems/angular.h | 2 + .../include/mp-units/systems/angular/math.h | 2 + src/systems/include/mp-units/systems/si.h | 4 +- .../include/mp-units/systems/si/chrono.h | 2 + .../include/mp-units/systems/si/math.h | 2 + test/CMakeLists.txt | 4 +- test/static/CMakeLists.txt | 8 +- test/static/concepts_test.cpp | 38 ++++- test/static/fixed_string_test.cpp | 6 +- test/static/iau_test.cpp | 4 +- test/static/quantity_point_test.cpp | 16 +- test/static/quantity_test.cpp | 7 +- 30 files changed, 387 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/ci-freestanding.yml create mode 100644 src/core/include/mp-units/bits/requires_hosted.h diff --git a/.github/workflows/ci-freestanding.yml b/.github/workflows/ci-freestanding.yml new file mode 100644 index 00000000..8607955e --- /dev/null +++ b/.github/workflows/ci-freestanding.yml @@ -0,0 +1,152 @@ +# 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. + +name: Freestanding CI + +on: + push: + paths-ignore: + - "docs/**" + pull_request: + paths-ignore: + - "docs/**" + +jobs: + build: + name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + formatting: ["std::format", "fmtlib"] + contracts: ["none"] + std: [23] + config: + - { + name: "GCC-14", + os: ubuntu-24.04, + compiler: + { + type: GCC, + version: 14, + cc: "gcc-14", + cxx: "g++-14", + }, + cxx_modules: "False", + std_format_support: "True", + conan-config: "", + } + - { + name: "Clang-18", + os: ubuntu-24.04, + compiler: + { + type: CLANG, + version: 18, + cc: "clang-18", + cxx: "clang++-18", + }, + lib: "libc++", + cxx_modules: "True", + std_format_support: "True", + conan-config: "", + } + build_type: ["Release", "Debug"] + + env: + CC: ${{ matrix.config.compiler.cc }} + CXX: ${{ matrix.config.compiler.cxx }} + + steps: + - uses: actions/checkout@v4 + - run: echo "cache_id=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_ENV + - name: Cache Conan data + uses: actions/cache@v4 + if: always() + env: + cache-name: cache-conan-data + with: + path: ~/.conan2/p + key: clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} + restore-keys: | + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- + clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}- + clang-tidy-${{ matrix.config.os }}- + - uses: hendrikmuhs/ccache-action@v1.2 + if: runner.os == 'Linux' + with: + key: ${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }} + max-size: 50M + - name: Install Clang + if: matrix.config.compiler.type == 'CLANG' + shell: bash + working-directory: ${{ env.HOME }} + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh ${{ matrix.config.compiler.version }} + sudo apt install -y clang-tools-${{ matrix.config.compiler.version }} + - name: Install Libc++ + if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' + shell: bash + run: | + sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Install Ninja + shell: bash + run: | + pip install -U ninja + - name: Install Conan + shell: bash + run: | + pip install -U conan + - name: Configure Conan + shell: bash + run: | + conan profile detect --force + if [[ "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.libcxx=.*/compiler.libcxx=${{ matrix.config.lib }}/' ~/.conan2/profiles/default + fi + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default + sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default + conan profile show -pr default + - run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV + - name: Run clang-tidy + shell: bash + run: | + conan build . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ + -c user.mp-units.build:all=True -c tools.build:cxxflags="['-ffreestanding']" \ + -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} -o freestanding=True ${{ matrix.config.conan-config }} + - name: Clean Conan cache before backup + shell: bash + run: | + conan remove *#~latest --confirm + conan remove *:*#~latest --confirm + conan cache clean "*" -s -b -d diff --git a/CMakeLists.txt b/CMakeLists.txt index 88713d7f..a9e82b33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,8 +67,10 @@ endif() # add project code add_subdirectory(src) -# add usage example -add_subdirectory(example) +if(!${projectPrefix}API_FREESTANDING) + # add usage example + add_subdirectory(example) +endif() # add unit tests enable_testing() diff --git a/conanfile.py b/conanfile.py index 50855bd9..4c532637 100644 --- a/conanfile.py +++ b/conanfile.py @@ -66,6 +66,7 @@ class MPUnitsConan(ConanFile): "string_view_ret": ["auto", True, False], "no_crtp": ["auto", True, False], "contracts": ["none", "gsl-lite", "ms-gsl"], + "freestanding": [True, False], } default_options = { "cxx_modules": "auto", @@ -73,6 +74,7 @@ class MPUnitsConan(ConanFile): "string_view_ret": "auto", "no_crtp": "auto", "contracts": "gsl-lite", + "freestanding": "False", } tool_requires = "cmake/[>=3.29]" implements = "auto_header_only" @@ -223,6 +225,10 @@ class MPUnitsConan(ConanFile): for key, value in self._option_feature_map.items(): if self.options.get_safe(key) == True: self._check_feature_supported(key, value) + if self.options.freestanding and self.options.contracts != "none": + raise ConanInvalidConfiguration( + "'contracts' should be set to 'none' for a freestanding build" + ) def layout(self): cmake_layout(self) @@ -251,6 +257,7 @@ class MPUnitsConan(ConanFile): tc.cache_variables["MP_UNITS_API_CONTRACTS"] = str( self.options.contracts ).upper() + tc.cache_variables["MP_UNITS_API_FREESTANDING"] = self.options.freestanding tc.generate() deps = CMakeDeps(self) deps.generate() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d33178c5..b237f37c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,6 +42,9 @@ message(STATUS "${projectPrefix}BUILD_AS_SYSTEM_HEADERS: ${${projectPrefix}BUILD option(${projectPrefix}BUILD_CXX_MODULES "Add C++ modules to the list of default targets" OFF) message(STATUS "${projectPrefix}BUILD_CXX_MODULES: ${${projectPrefix}BUILD_CXX_MODULES}") +option(${projectPrefix}API_FREESTANDING "Builds only freestanding part of the library" OFF) +message(STATUS "${projectPrefix}API_FREESTANDING: ${${projectPrefix}API_FREESTANDING}") + if(${projectPrefix}BUILD_AS_SYSTEM_HEADERS) set(${projectPrefix}_AS_SYSTEM SYSTEM) endif() @@ -71,6 +74,10 @@ cache_var_values(API_NO_CRTP AUTO TRUE FALSE) set(${projectPrefix}API_CONTRACTS GSL-LITE CACHE STRING "Enable contract checking") cache_var_values(API_CONTRACTS NONE GSL-LITE MS-GSL) +if(${projectPrefix}API_FREESTANDING AND NOT ${projectPrefix}API_CONTRACTS STREQUAL "NONE") + message(FATAL_ERROR "'${projectPrefix}API_CONTRACTS' should be set to 'NONE' for a freestanding build") +endif() + # C++ features check_cxx_feature_supported(__cpp_lib_format ${projectPrefix}LIB_FORMAT_SUPPORTED) check_cxx_feature_supported("__cpp_constexpr >= 202211L" ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8c6aada0..99289ec8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -35,7 +35,6 @@ endfunction() add_mp_units_module( core mp-units-core HEADERS include/mp-units/bits/core_gmf.h - include/mp-units/bits/fmt.h include/mp-units/bits/get_associated_quantity.h include/mp-units/bits/get_common_base.h include/mp-units/bits/hacks.h @@ -73,14 +72,27 @@ add_mp_units_module( include/mp-units/framework/value_cast.h include/mp-units/compat_macros.h include/mp-units/concepts.h - include/mp-units/format.h include/mp-units/framework.h - include/mp-units/math.h - include/mp-units/ostream.h - include/mp-units/random.h MODULE_INTERFACE_UNIT mp-units-core.cpp ) +if(NOT ${projectPrefix}API_FREESTANDING) + target_sources( + mp-units-core + PUBLIC FILE_SET + HEADERS + BASE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/include + FILES + include/mp-units/bits/fmt.h + include/mp-units/bits/requires_hosted.h + include/mp-units/math.h + include/mp-units/ostream.h + include/mp-units/format.h + include/mp-units/random.h + ) +endif() + set_feature_flag(API_STD_FORMAT) set_feature_flag(API_STRING_VIEW_RET) set_feature_flag(API_NO_CRTP) @@ -133,4 +145,8 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") ) endif() -# target_compile_options(mp-units-core ${${projectPrefix}TARGET_SCOPE} "-ftime-trace") +# Freestanding +target_compile_definitions( + mp-units-core ${${projectPrefix}TARGET_SCOPE} + ${projectPrefix}HOSTED=$> +) diff --git a/src/core/include/mp-units/bits/core_gmf.h b/src/core/include/mp-units/bits/core_gmf.h index 500ef701..21d97e8f 100644 --- a/src/core/include/mp-units/bits/core_gmf.h +++ b/src/core/include/mp-units/bits/core_gmf.h @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -35,18 +34,23 @@ #include #include #include -#include #include #include #include -#include -#include -#include +#include #include #include #include #include +#if MP_UNITS_HOSTED +#include +#include +#include +#include +#include +#include + #if MP_UNITS_USE_FMTLIB MP_UNITS_DIAGNOSTIC_PUSH MP_UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE @@ -57,6 +61,8 @@ MP_UNITS_DIAGNOSTIC_POP #include #endif +#endif + #if __cpp_lib_text_encoding #include #endif diff --git a/src/core/include/mp-units/bits/hacks.h b/src/core/include/mp-units/bits/hacks.h index f8522059..2dd10f84 100644 --- a/src/core/include/mp-units/bits/hacks.h +++ b/src/core/include/mp-units/bits/hacks.h @@ -73,6 +73,10 @@ #define MP_UNITS_DIAGNOSTIC_IGNORE_DEPRECATED #endif +#if !defined MP_UNITS_HOSTED && defined __STDC_HOSTED__ +#define MP_UNITS_HOSTED __STDC_HOSTED__ +#endif + #if MP_UNITS_COMP_MSVC #define MP_UNITS_TYPENAME typename diff --git a/src/core/include/mp-units/bits/requires_hosted.h b/src/core/include/mp-units/bits/requires_hosted.h new file mode 100644 index 00000000..dbacc01a --- /dev/null +++ b/src/core/include/mp-units/bits/requires_hosted.h @@ -0,0 +1,29 @@ +// 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 + +#if !MP_UNITS_HOSTED +#error "This header is not available in freestanding mode." +#endif diff --git a/src/core/include/mp-units/bits/text_tools.h b/src/core/include/mp-units/bits/text_tools.h index dccd3e91..c79ff946 100644 --- a/src/core/include/mp-units/bits/text_tools.h +++ b/src/core/include/mp-units/bits/text_tools.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include @@ -100,12 +101,12 @@ constexpr Out copy(const symbol_text& txt, text_encoding encoding, Out out for (const char8_t ch : txt.unicode()) *out++ = static_cast(ch); return out; } else - throw std::invalid_argument("Unicode text can't be copied to CharT output"); + MP_UNITS_THROW(std::invalid_argument("Unicode text can't be copied to CharT output")); } else { if constexpr (is_same_v) return ::mp_units::detail::copy(txt.ascii().begin(), txt.ascii().end(), out); else - throw std::invalid_argument("ASCII text can't be copied to CharT output"); + MP_UNITS_THROW(std::invalid_argument("ASCII text can't be copied to CharT output")); } } diff --git a/src/core/include/mp-units/compat_macros.h b/src/core/include/mp-units/compat_macros.h index a9c21813..026f04e9 100644 --- a/src/core/include/mp-units/compat_macros.h +++ b/src/core/include/mp-units/compat_macros.h @@ -40,6 +40,15 @@ #endif +#if MP_UNITS_HOSTED +#define MP_UNITS_THROW(expr) throw expr +#else +#include +#define MP_UNITS_THROW(expr) std::abort() +#endif + +#if MP_UNITS_HOSTED + #if defined MP_UNITS_API_STD_FORMAT && !MP_UNITS_API_STD_FORMAT #define MP_UNITS_USE_FMTLIB 1 @@ -59,21 +68,7 @@ #define MP_UNITS_FMT_TO_ARG_ID(arg) (arg) #define MP_UNITS_FMT_FROM_ARG_ID(arg) (arg) -// This re-uses code from fmt; -#if FMT_EXCEPTIONS -#if FMT_MSC_VERSION || defined(__NVCC__) -#define MP_UNITS_THROW(x) ::fmt::detail::do_throw(x) -#else -#define MP_UNITS_THROW(x) throw x -#endif -#else -#define MP_UNITS_THROW(x) \ - do { \ - FMT_ASSERT(false, (x).what()); \ - } while (false) -#endif - -#else +#else // MP_UNITS_USE_FMTLIB #if !defined __cpp_lib_format && !defined MP_UNITS_COMP_CLANG #error "std::formatting facility not supported" @@ -83,10 +78,8 @@ #define MP_UNITS_FMT_LOCALE(loc) loc #define MP_UNITS_FMT_TO_ARG_ID(arg) static_cast(arg) #define MP_UNITS_FMT_FROM_ARG_ID(arg) static_cast(arg) -#define MP_UNITS_THROW(arg) throw arg - -#endif +#endif // MP_UNITS_USE_FMTLIB #ifndef MP_UNITS_IN_MODULE_INTERFACE @@ -104,6 +97,8 @@ MP_UNITS_DIAGNOSTIC_POP #endif +#endif // MP_UNITS_HOSTED + #if MP_UNITS_API_CONTRACTS == 2 || __has_include() #include diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index c67332bd..1cfe2369 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -35,8 +35,11 @@ #include // IWYU pragma: export #include #include -#include +#include #include +#if MP_UNITS_HOSTED +#include +#endif #endif MP_UNITS_EXPORT @@ -123,11 +126,14 @@ public: return data()[pos]; } +#if MP_UNITS_HOSTED [[nodiscard]] constexpr const_reference at(size_type pos) const { if (pos >= size()) throw std::out_of_range("basic_fixed_string::at"); return (*this)[pos]; } +#endif + [[nodiscard]] constexpr const_reference front() const { MP_UNITS_EXPECTS(!empty()); @@ -239,11 +245,13 @@ public: } // inserters and extractors +#if MP_UNITS_HOSTED friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_fixed_string& str) { return os << str.c_str(); } +#endif }; // deduction guides @@ -282,6 +290,7 @@ struct std::hash> : std::hash template struct std::hash> : std::hash {}; +#if MP_UNITS_HOSTED // formatting support template struct MP_UNITS_STD_FMT::formatter> : formatter> { @@ -291,4 +300,6 @@ struct MP_UNITS_STD_FMT::formatter> : for return formatter>::format(std::basic_string_view(str), ctx); } }; +#endif + // NOLINTEND(*-avoid-c-arrays) diff --git a/src/core/include/mp-units/format.h b/src/core/include/mp-units/format.h index 0400b492..d901b8dc 100644 --- a/src/core/include/mp-units/format.h +++ b/src/core/include/mp-units/format.h @@ -24,6 +24,8 @@ #pragma once +#include +// #include #include #include diff --git a/src/core/include/mp-units/framework/dimension.h b/src/core/include/mp-units/framework/dimension.h index efa0810a..4aa746dd 100644 --- a/src/core/include/mp-units/framework/dimension.h +++ b/src/core/include/mp-units/framework/dimension.h @@ -37,8 +37,10 @@ #include #include #include -#include #include +#if MP_UNITS_HOSTED +#include +#endif #endif namespace mp_units { @@ -318,9 +320,15 @@ MP_UNITS_EXPORT template buffer; dimension_symbol_to(std::back_inserter(buffer), D{}, fmt); return buffer.size(); +#else + std::array buffer; // TODO unsafe + auto end = dimension_symbol_to(buffer.begin(), D{}, fmt); + return end - buffer.begin(); +#endif }; #if MP_UNITS_API_STRING_VIEW_RET // Permitting static constexpr variables in constexpr functions diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index bc9cdf58..a96c043f 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -44,8 +44,10 @@ #include #include #include -#include #include +#if MP_UNITS_HOSTED +#include +#endif #endif namespace mp_units { @@ -718,8 +720,8 @@ constexpr Out print_separator(Out out, const unit_symbol_formatting& fmt) { if (fmt.separator == unit_symbol_separator::half_high_dot) { if (fmt.encoding != text_encoding::unicode) - throw std::invalid_argument( - "'unit_symbol_separator::half_high_dot' can be only used with 'text_encoding::unicode'"); + MP_UNITS_THROW( + std::invalid_argument("'unit_symbol_separator::half_high_dot' can be only used with 'text_encoding::unicode'")); const std::string_view dot = "⋅"; out = detail::copy(dot.begin(), dot.end(), out); } else { @@ -844,9 +846,15 @@ MP_UNITS_EXPORT template buffer; unit_symbol_to(std::back_inserter(buffer), U{}, fmt); return buffer.size(); +#else + std::array buffer; // TODO unsafe + auto end = unit_symbol_to(buffer.begin(), U{}, fmt); + return end - buffer.begin(); +#endif }; #if MP_UNITS_API_STRING_VIEW_RET // Permitting static constexpr variables in constexpr functions diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index c93883c6..50dd222a 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -22,6 +22,8 @@ #pragma once +#include +// #include #include #include diff --git a/src/core/include/mp-units/ostream.h b/src/core/include/mp-units/ostream.h index e03bb33c..fbf7d575 100644 --- a/src/core/include/mp-units/ostream.h +++ b/src/core/include/mp-units/ostream.h @@ -23,6 +23,8 @@ #pragma once +#include +// #include #include #include diff --git a/src/core/mp-units-core.cpp b/src/core/mp-units-core.cpp index f0278d29..e14347a8 100644 --- a/src/core/mp-units-core.cpp +++ b/src/core/mp-units-core.cpp @@ -8,8 +8,11 @@ export module mp_units.core; #include #include -#include #include + +#if MP_UNITS_HOSTED +#include #include #include #include +#endif diff --git a/src/systems/CMakeLists.txt b/src/systems/CMakeLists.txt index 3f902053..0cac8872 100644 --- a/src/systems/CMakeLists.txt +++ b/src/systems/CMakeLists.txt @@ -25,8 +25,7 @@ cmake_minimum_required(VERSION 3.23) add_mp_units_module( systems mp-units-systems DEPENDENCIES mp-units::core - HEADERS include/mp-units/systems/angular/math.h - include/mp-units/systems/angular/units.h + HEADERS include/mp-units/systems/angular/units.h include/mp-units/systems/iec80000/binary_prefixes.h include/mp-units/systems/iec80000/quantities.h include/mp-units/systems/iec80000/unit_symbols.h @@ -39,9 +38,7 @@ add_mp_units_module( include/mp-units/systems/isq/si_quantities.h include/mp-units/systems/isq/space_and_time.h include/mp-units/systems/isq/thermodynamics.h - include/mp-units/systems/si/chrono.h include/mp-units/systems/si/constants.h - include/mp-units/systems/si/math.h include/mp-units/systems/si/prefixes.h include/mp-units/systems/si/unit_symbols.h include/mp-units/systems/si/units.h @@ -60,3 +57,17 @@ add_mp_units_module( include/mp-units/systems/usc.h MODULE_INTERFACE_UNIT mp-units-systems.cpp ) + +if(NOT ${projectPrefix}API_FREESTANDING) + target_sources( + mp-units-systems + PUBLIC FILE_SET + HEADERS + BASE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/include + FILES + include/mp-units/systems/angular/math.h + include/mp-units/systems/si/math.h + include/mp-units/systems/si/chrono.h + ) +endif() diff --git a/src/systems/include/mp-units/systems/angular.h b/src/systems/include/mp-units/systems/angular.h index 326ea227..41e30bb9 100644 --- a/src/systems/include/mp-units/systems/angular.h +++ b/src/systems/include/mp-units/systems/angular.h @@ -23,7 +23,9 @@ #pragma once // IWYU pragma: begin_exports +#if MP_UNITS_HOSTED #include +#endif #include #ifndef MP_UNITS_IN_MODULE_INTERFACE diff --git a/src/systems/include/mp-units/systems/angular/math.h b/src/systems/include/mp-units/systems/angular/math.h index 00e03b7a..6dc96e86 100644 --- a/src/systems/include/mp-units/systems/angular/math.h +++ b/src/systems/include/mp-units/systems/angular/math.h @@ -22,6 +22,8 @@ #pragma once +#include +// #include #include diff --git a/src/systems/include/mp-units/systems/si.h b/src/systems/include/mp-units/systems/si.h index b9b374d7..95f9fc87 100644 --- a/src/systems/include/mp-units/systems/si.h +++ b/src/systems/include/mp-units/systems/si.h @@ -23,9 +23,11 @@ #pragma once // IWYU pragma: begin_exports +#if MP_UNITS_HOSTED #include -#include #include +#endif +#include #include #include #include diff --git a/src/systems/include/mp-units/systems/si/chrono.h b/src/systems/include/mp-units/systems/si/chrono.h index e0e5422d..2ddd71a1 100644 --- a/src/systems/include/mp-units/systems/si/chrono.h +++ b/src/systems/include/mp-units/systems/si/chrono.h @@ -22,6 +22,8 @@ #pragma once +#include +// #include #include #include diff --git a/src/systems/include/mp-units/systems/si/math.h b/src/systems/include/mp-units/systems/si/math.h index 400a5a25..590a14ea 100644 --- a/src/systems/include/mp-units/systems/si/math.h +++ b/src/systems/include/mp-units/systems/si/math.h @@ -22,6 +22,8 @@ #pragma once +#include +// #include #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8430bfa4..5519a7df 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,5 +22,7 @@ cmake_minimum_required(VERSION 3.5) -add_subdirectory(runtime) +if(!${projectPrefix}API_FREESTANDING) + add_subdirectory(runtime) +endif() add_subdirectory(static) diff --git a/test/static/CMakeLists.txt b/test/static/CMakeLists.txt index 4e5a709e..584e7a23 100644 --- a/test/static/CMakeLists.txt +++ b/test/static/CMakeLists.txt @@ -32,7 +32,6 @@ add_library( unit_tests_static angular_test.cpp cgs_test.cpp - chrono_test.cpp compare_test.cpp concepts_test.cpp # custom_rep_test_min_expl.cpp @@ -40,7 +39,6 @@ add_library( dimension_test.cpp dimension_symbol_test.cpp fixed_string_test.cpp - fractional_exponent_quantity.cpp hep_test.cpp iau_test.cpp iec80000_test.cpp @@ -49,7 +47,6 @@ add_library( isq_test.cpp isq_angle_test.cpp # magnitude_test.cpp - math_test.cpp natural_test.cpp prime_test.cpp quantity_point_test.cpp @@ -64,6 +61,11 @@ add_library( unit_symbol_test.cpp usc_test.cpp ) + +if(NOT ${projectPrefix}API_FREESTANDING) + target_sources(unit_tests_static PRIVATE chrono_test.cpp fractional_exponent_quantity.cpp math_test.cpp) +endif() + target_compile_options(unit_tests_static PRIVATE $<$:-Wno-subobject-linkage>) target_link_libraries(unit_tests_static PRIVATE mp-units::mp-units) target_link_libraries(unit_tests_static PRIVATE unit_tests_static_truncating) diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index 65762ddb..b43ed0dd 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -23,14 +23,18 @@ #include #include #include +#include +#include +#if MP_UNITS_HOSTED #include #include -#include #include -#include +#endif +#if MP_UNITS_HOSTED template inline constexpr bool mp_units::is_scalar> = true; +#endif namespace { @@ -157,7 +161,9 @@ static_assert(!Unit>); static_assert(!Unit, si::second>>); static_assert(!Unit); static_assert(!Unit); +#if MP_UNITS_HOSTED static_assert(!Unit); +#endif // NamedUnit static_assert(detail::NamedUnit); @@ -181,7 +187,9 @@ static_assert(!detail::NamedUnit>); static_assert(!detail::NamedUnit, si::second>>); static_assert(!detail::NamedUnit); static_assert(!detail::NamedUnit); +#if MP_UNITS_HOSTED static_assert(!detail::NamedUnit); +#endif // PrefixableUnit static_assert(PrefixableUnit); @@ -205,7 +213,9 @@ static_assert(!PrefixableUnit>); static_assert(!PrefixableUnit, si::second>>); static_assert(!PrefixableUnit); static_assert(!PrefixableUnit); +#if MP_UNITS_HOSTED static_assert(!PrefixableUnit); +#endif // AssociatedUnit static_assert(AssociatedUnit); @@ -229,7 +239,9 @@ static_assert(!AssociatedUnit>); static_assert(!AssociatedUnit, si::second>>); static_assert(!AssociatedUnit); static_assert(!AssociatedUnit); +#if MP_UNITS_HOSTED static_assert(!AssociatedUnit); +#endif // UnitOf static_assert(UnitOf); @@ -284,27 +296,33 @@ static_assert(!ReferenceOf, is // Representation static_assert(Representation); static_assert(Representation); -static_assert(Representation>); static_assert(!Representation); static_assert(!Representation>); -static_assert(!Representation); +#if MP_UNITS_HOSTED +static_assert(Representation>); static_assert(!Representation); +static_assert(!Representation); +#endif // RepresentationOf static_assert(RepresentationOf); static_assert(RepresentationOf); -static_assert(RepresentationOf, quantity_character::scalar>); static_assert(!RepresentationOf); static_assert(!RepresentationOf, quantity_character::scalar>); +#if MP_UNITS_HOSTED +static_assert(RepresentationOf, quantity_character::scalar>); static_assert(!RepresentationOf); static_assert(!RepresentationOf); +#endif // Quantity static_assert(Quantity>); static_assert(Quantity>); static_assert(Quantity>); static_assert(Quantity>); +#if MP_UNITS_HOSTED static_assert(!Quantity); +#endif static_assert(!Quantity>); static_assert(!Quantity>); @@ -332,8 +350,10 @@ static_assert(!QuantityOf, isq::rotation>); static_assert(!QuantityOf, isq::angular_measure>); // QuantityLike +#if MP_UNITS_HOSTED static_assert(QuantityLike); static_assert(QuantityLike); +#endif static_assert(!QuantityLike>); static_assert(!QuantityLike>); static_assert(!QuantityLike); @@ -349,8 +369,10 @@ static_assert(!QuantityPoint>); static_assert(!QuantityPoint); static_assert(!QuantityPoint); +#if MP_UNITS_HOSTED static_assert(!QuantityPoint); static_assert(!QuantityPoint>); +#endif static_assert(!QuantityPoint); // QuantityPointOf @@ -384,8 +406,10 @@ static_assert(!PointOrigin>); static_assert(!PointOrigin>); static_assert(!PointOrigin>); static_assert(!PointOrigin>); +#if MP_UNITS_HOSTED static_assert(!PointOrigin); static_assert(!PointOrigin>); +#endif static_assert(!PointOrigin); // PointOriginFor @@ -408,13 +432,17 @@ static_assert(!PointOriginFor, isq::radius>); static_assert(!PointOriginFor, isq::time>); static_assert(!PointOriginFor, isq::length>); +#if MP_UNITS_HOSTED static_assert(!PointOriginFor); static_assert(!PointOriginFor, isq::length>); +#endif static_assert(!PointOriginFor); // QuantityPointLike +#if MP_UNITS_HOSTED static_assert(QuantityPointLike>); static_assert(!QuantityPointLike); +#endif static_assert(!QuantityPointLike>); static_assert(!QuantityPointLike>); static_assert(!QuantityPointLike); diff --git a/test/static/fixed_string_test.cpp b/test/static/fixed_string_test.cpp index f1885e0c..f0001c79 100644 --- a/test/static/fixed_string_test.cpp +++ b/test/static/fixed_string_test.cpp @@ -31,12 +31,12 @@ namespace { constexpr std::array array = {'a', 'b', 'c'}; auto from_string = [] { - std::string txt = "abc"; + std::string_view txt = "abc"; return fixed_string<3>(std::from_range, txt); }; auto from_string_iter = [] { - std::string txt = "abc"; + std::string_view txt = "abc"; return fixed_string<3>(txt.begin(), txt.end()); }; @@ -90,6 +90,7 @@ static_assert(txt9[0] == 'c'); static_assert(txt9[1] == 'b'); static_assert(txt9[2] == 'a'); +#if MP_UNITS_HOSTED static_assert(txt1.at(0) == 'a'); static_assert(txt2.at(0) == 'a'); static_assert(txt2.at(1) == 'b'); @@ -97,6 +98,7 @@ static_assert(txt2.at(2) == 'c'); static_assert(txt9.at(0) == 'c'); static_assert(txt9.at(1) == 'b'); static_assert(txt9.at(2) == 'a'); +#endif static_assert(txt1.front() == 'a'); static_assert(txt1.back() == 'a'); diff --git a/test/static/iau_test.cpp b/test/static/iau_test.cpp index fd33c4bc..339c7125 100644 --- a/test/static/iau_test.cpp +++ b/test/static/iau_test.cpp @@ -20,7 +20,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#if MP_UNITS_HOSTED #include // IWYU pragma: keep +#endif #include #include #include @@ -42,7 +44,7 @@ static_assert(isq::length(1 * LD) == 384'399 * si::kilo); static_assert(isq::length(1 * ly) == 9'460'730'472'580'800 * si::metre); static_assert(isq::length(10'000'000'000 * A) == 1 * si::metre); -#if __cpp_lib_constexpr_cmath || MP_UNITS_COMP_GCC +#if MP_UNITS_HOSTED && (__cpp_lib_constexpr_cmath || MP_UNITS_COMP_GCC) // TODO Should the below work for `1 * pc`? If yes, how to extent the type and how to convert it to a floating-point // representation for comparison purposes? static_assert(round(isq::length(1.L * pc)) == 30'856'775'814'913'673 * si::metre); diff --git a/test/static/quantity_point_test.cpp b/test/static/quantity_point_test.cpp index acc53416..24c5206d 100644 --- a/test/static/quantity_point_test.cpp +++ b/test/static/quantity_point_test.cpp @@ -26,20 +26,25 @@ #include #include #include -#include #include #include #include #include #include +#if MP_UNITS_HOSTED +#include +#endif namespace { using namespace mp_units; using namespace mp_units::si::unit_symbols; using namespace mp_units::usc::unit_symbols; + +#if MP_UNITS_HOSTED using namespace std::chrono_literals; using sys_seconds = std::chrono::time_point; +#endif inline constexpr struct zeroth_length : absolute_point_origin { } zeroth_length; @@ -444,6 +449,7 @@ static_assert(!std::convertible_to, quantity_point, quantity>); static_assert(!std::convertible_to, quantity_point>); +#if MP_UNITS_HOSTED // quantity-like static_assert(!std::constructible_from, std::chrono::seconds>); static_assert(!std::convertible_to>); @@ -453,7 +459,7 @@ static_assert(!std::convertible_to, std::chrono::seconds>); static_assert(!std::convertible_to>); - +#endif // ---------------------- // explicit point origins @@ -499,6 +505,7 @@ static_assert(!std::convertible_to, quantity_point, quantity>); static_assert(!std::convertible_to, quantity_point>); +#if MP_UNITS_HOSTED // quantity-like static_assert(!std::constructible_from>, std::chrono::seconds>); @@ -516,6 +523,7 @@ static_assert( static_assert( !std::convertible_to>>); +#endif /////////////////////////////////////// @@ -775,6 +783,7 @@ static_assert(!std::constructible_from, quantity_point>); +#if MP_UNITS_HOSTED // quantity-point-like static_assert( std::constructible_from>, sys_seconds>); @@ -786,6 +795,7 @@ static_assert( !std::constructible_from>, sys_seconds>); static_assert( !std::convertible_to>>); +#endif ////////////////////////////////// @@ -889,6 +899,7 @@ static_assert(std::is_same_v); +#if MP_UNITS_HOSTED using namespace std::chrono_literals; static_assert(std::is_same_v); static_assert(std::is_same_v, @@ -897,6 +908,7 @@ static_assert(std::is_same_v>); static_assert(quantity_point{sys_seconds{24h}}.unit == si::second); static_assert(quantity_point{sys_seconds{24h}}.quantity_spec == kind_of); +#endif //////////// diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index 69b653f7..1489a4df 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -27,12 +27,14 @@ #include #include #include -#include #include #include #include #include #include +#if MP_UNITS_HOSTED +#include +#endif template<> inline constexpr bool mp_units::is_vector = true; @@ -311,12 +313,13 @@ static_assert(quantity{123. * m}.quantity_spec == kind_of); static_assert(quantity{123. * h}.unit == si::hour); static_assert(quantity{123. * h}.quantity_spec == kind_of); +#if MP_UNITS_HOSTED using namespace std::chrono_literals; static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(quantity{24h}.unit == si::hour); static_assert(quantity{24h}.quantity_spec == kind_of); - +#endif //////////////////////// // assignment operator From 03625b38fd2326917a8f5572c2bcf2214abd73d4 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 19:55:04 +0200 Subject: [PATCH 41/61] ci: clang-tidy CI cleanup --- .github/workflows/ci-clang-tidy.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ci-clang-tidy.yml b/.github/workflows/ci-clang-tidy.yml index 284803a7..9f4e6a5e 100644 --- a/.github/workflows/ci-clang-tidy.yml +++ b/.github/workflows/ci-clang-tidy.yml @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -name: clang-tidy +name: clang-tidy CI on: push: @@ -30,9 +30,6 @@ on: paths-ignore: - "docs/**" -env: - CHANNEL: ${{ fromJSON('["testing", "stable"]')[github.ref_type == 'tag' && startsWith(github.ref_name, 'v')] }} - jobs: build: name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" @@ -60,9 +57,6 @@ jobs: conan-config: "", } build_type: ["Release", "Debug"] - exclude: - - formatting: "std::format" - config: { std_format_support: "False" } env: CC: ${{ matrix.config.compiler.cc }} From a7042d3ff00ee62be38ebbd8f1e924f53ae384e9 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:13:32 +0200 Subject: [PATCH 42/61] ci: freestanding CI cleanup --- .github/workflows/ci-freestanding.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-freestanding.yml b/.github/workflows/ci-freestanding.yml index 8607955e..3d46883d 100644 --- a/.github/workflows/ci-freestanding.yml +++ b/.github/workflows/ci-freestanding.yml @@ -86,16 +86,16 @@ jobs: cache-name: cache-conan-data with: path: ~/.conan2/p - key: clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} + key: freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} restore-keys: | - clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- - clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- - clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- - clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- - clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- - clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- - clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}- - clang-tidy-${{ matrix.config.os }}- + freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- + freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- + freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- + freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- + freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- + freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- + freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}- + freestanding-${{ matrix.config.os }}- - uses: hendrikmuhs/ccache-action@v1.2 if: runner.os == 'Linux' with: @@ -138,7 +138,7 @@ jobs: sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default conan profile show -pr default - run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV - - name: Run clang-tidy + - name: Build freestanding shell: bash run: | conan build . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ From 278edef4cc8bc7df41a912672f2a8fa554ba6c6a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:14:11 +0200 Subject: [PATCH 43/61] build: some Conan dependencies disabled for a freestanding build --- conanfile.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/conanfile.py b/conanfile.py index 4c532637..0d2cb39b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -211,12 +211,13 @@ class MPUnitsConan(ConanFile): self.requires("gsl-lite/0.41.0") elif self.options.contracts == "ms-gsl": self.requires("ms-gsl/4.0.0") - if self._use_fmtlib: + if self._use_fmtlib and not self.options.freestanding: self.requires("fmt/10.2.1") def build_requirements(self): if self._build_all: - self.test_requires("catch2/3.5.1") + if not self.options.freestanding: + self.test_requires("catch2/3.5.1") if not self._skip_la: self.test_requires("wg21-linear_algebra/0.7.3") @@ -289,7 +290,7 @@ class MPUnitsConan(ConanFile): self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] elif self.options.contracts == "ms-gsl": self.cpp_info.components["core"].requires = ["ms-gsl::ms-gsl"] - if self._use_fmtlib: + if self._use_fmtlib and not self.options.freestanding: self.cpp_info.components["core"].requires.append("fmt::fmt") if compiler == "msvc": self.cpp_info.components["core"].cxxflags = ["/utf-8"] From 9dc44243c5d627c7a1771eb97731f317edd96da0 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:14:30 +0200 Subject: [PATCH 44/61] docs: README badges updated --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3b334e98..f3ac13f9 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,11 @@ [![GitHub license](https://img.shields.io/github/license/mpusz/mp-units?cacheSeconds=3600&color=informational&label=License)](./LICENSE.md) [![GitHub license](https://img.shields.io/badge/C%2B%2B-20-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20) -[![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster) -[![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster) -[![clang-tidy](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-clang-tidy.yml?branch=master&label=clang-tidy)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22clang-tidy%20CI%22+branch%3Amaster) -[![Formatting CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-formatting.yml?branch=master&label=Formatting%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Formatting%20CI%22+branch%3Amaster) +[![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-conan.yml) +[![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-test-package-cmake.yml) +[![clang-tidy CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-clang-tidy.yml?branch=master&label=clang-tidy%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-clang-tidy.yml) +[![Freestanding CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-freestanding.yml?branch=master&label=Freestanding%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-freestanding.yml) +[![Formatting CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-formatting.yml?branch=master&label=Formatting%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-formatting.yml) [![GitHub Workflow Documentation](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/documentation.yml?branch=master&label=Documentation)](https://github.com/mpusz/mp-units/actions?query=workflow%3ADocumentation+branch%3Amaster) [![Conan stable](https://img.shields.io/conan/v/mp-units?label=ConanCenter&color=blue)](https://conan.io/center/mp-units) From aaa394ac798ae9a9c0e483cbeef8e902c0bae902 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:14:50 +0200 Subject: [PATCH 45/61] docs: CITATION.cff updated --- CITATION.cff | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 00692afb..650a157b 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -36,13 +36,13 @@ contact: given-names: Mateusz family-names: Pusz -repository-code: "https://github.com/mpusz/units" -url: "https://mpusz.github.io/units" +repository-code: "https://github.com/mpusz/mp-units" +url: "https://mpusz.github.io/mp-units" repository-artifact: "https://conan.io/center/mp-units" -version: 0.7.0 -date-released: "2021-05-11" +version: 2.1.1 +date-released: "2024-05-16" identifiers: - - description: "The GitHub release URL of tag 0.7.0" + - description: "The GitHub release URL of tag 2.1.1" type: url - value: "https://github.com/mpusz/units/releases/tag/v0.7.0" + value: "https://github.com/mpusz/mp-units/releases/tag/v2.1.1" From 496ec59c0b6372e6264b0775d02a8f57f8c5294e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:41:58 +0200 Subject: [PATCH 46/61] docs: 2.2 release annonuncement updated --- docs/blog/posts/2.2.0-released.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/blog/posts/2.2.0-released.md b/docs/blog/posts/2.2.0-released.md index 84dbb07d..1eb92de4 100644 --- a/docs/blog/posts/2.2.0-released.md +++ b/docs/blog/posts/2.2.0-released.md @@ -158,11 +158,6 @@ three values: - `False` - The feature is disabled, and an older alternative is always used. - `Auto` - The feature is automatically enabled if the compiler supports it (old behavior). -Before this release, the library always depended on [gsl-lite](https://github.com/gsl-lite/gsl-lite) -to perform runtime contract and asserts checking. In this release we introduced new options -to control if contract checking should be based on [gsl-lite](https://github.com/gsl-lite/gsl-lite), -[ms-gsl](https://github.com/microsoft/GSL), or maybe completely disabled. - Additionally, some CMake options were renamed to better express the impact on our users (:boom: **breaking change** :boom:). For example, now CMake options include: @@ -171,6 +166,21 @@ Additionally, some CMake options were renamed to better express the impact on ou - `MP_UNITS_DEV_*` - options primarily useful for the project developers or people who want to compile our unit tests and examples. + +## Configurable contracts checking + +Before this release, the library always depended on [gsl-lite](https://github.com/gsl-lite/gsl-lite) +to perform runtime contract and asserts checking. In this release we introduced new options +to control if contract checking should be based on [gsl-lite](https://github.com/gsl-lite/gsl-lite), +[ms-gsl](https://github.com/microsoft/GSL), or maybe completely disabled. + + +## Freestanding support + +From this release it is possible to configure the library in the +[freestanding](https://en.cppreference.com/w/cpp/freestanding) mode. This limits the functionality +and interface of the library to be compatible with the freestanding implementations. + !!! info To learn more, please refer to the [Build options](../../getting_started/installation_and_usage.md#build-options) From 68d88335c4751f59133561a7759d494d758ad6cc Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:42:20 +0200 Subject: [PATCH 47/61] docs: C++ badge updated in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f3ac13f9..bb3a7cc2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![GitHub license](https://img.shields.io/github/license/mpusz/mp-units?cacheSeconds=3600&color=informational&label=License)](./LICENSE.md) -[![GitHub license](https://img.shields.io/badge/C%2B%2B-20-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20) +[![GitHub license](https://img.shields.io/badge/C%2B%2B-20%2F23-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20) [![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-conan.yml) [![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-test-package-cmake.yml) From bbd048b9be6d05e6ae25e6ff4c07ba56599b7422 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:42:53 +0200 Subject: [PATCH 48/61] docs: freestanding flags documentation added --- .../getting_started/installation_and_usage.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 20d3c788..ed46249e 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -276,6 +276,17 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`freestanding`](#freestanding){ #freestanding } + +: [:octicons-tag-24: 2.2.0][conan freestanding] · :octicons-milestone-24: `True`/`False` (Default: `False`) + + Configures the library in the [freestanding](https://en.cppreference.com/w/cpp/freestanding) + mode. When enabled, the library's source code should build with the compiler's + [`-ffreestanding`](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) compilation option + without any issues. + + [conan freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 + ### Conan configuration properties [`user.mp-units.build:all`](#user.mp-units.build-all){ #user.mp-units.build-all } @@ -368,6 +379,17 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [cmake contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`MP_UNITS_API_FREESTANDING`](#MP_UNITS_API_FREESTANDING){ #MP_UNITS_API_FREESTANDING } + +: [:octicons-tag-24: 2.2.0][cmake freestanding] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`) + + Configures the library in the [freestanding](https://en.cppreference.com/w/cpp/freestanding) + mode. When enabled, the library's source code should build with the compiler's + [`-ffreestanding`](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) compilation option + without any issues. + + [cmake freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 + #### Options for mp-units project developers [`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA } From 3cd5a7dd4214ff23fdf928cdd0ec5c7e286eeb8d Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 30 May 2024 20:50:11 +0200 Subject: [PATCH 49/61] build: CMake dependencies handling improved for the freestanding mode --- src/CMakeLists.txt | 3 ++- src/core/CMakeLists.txt | 5 +++-- src/mp-unitsConfig.cmake | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b237f37c..cee2b7f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,8 @@ check_cxx_feature_supported("__cpp_constexpr >= 202211L" ${projectPrefix}STATIC_ check_cxx_feature_supported(__cpp_explicit_this_parameter ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) # validate settings -if(${projectPrefix}API_STD_FORMAT STREQUAL "TRUE" +if(NOT ${projectPrefix}API_FREESTANDING + AND ${projectPrefix}API_STD_FORMAT STREQUAL "TRUE" AND NOT (${projectPrefix}LIB_FORMAT_SUPPORTED # libc++ has a basic supports for std::format but does not set __cpp_lib_format diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 99289ec8..9d2839f3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -98,8 +98,9 @@ set_feature_flag(API_STRING_VIEW_RET) set_feature_flag(API_NO_CRTP) # Text formatting -if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO" - AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED) +if(NOT ${projectPrefix}API_FREESTANDING + AND (${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO" + AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED)) ) if(NOT TARGET fmt::fmt) find_package(fmt REQUIRED) diff --git a/src/mp-unitsConfig.cmake b/src/mp-unitsConfig.cmake index 017d6131..f5bee933 100644 --- a/src/mp-unitsConfig.cmake +++ b/src/mp-unitsConfig.cmake @@ -22,7 +22,7 @@ include(CMakeFindDependencyMacro) -if(NOT MP_UNITS_API_STD_FORMAT) +if(NOT MP_UNITS_API_FREESTANDING AND NOT MP_UNITS_API_STD_FORMAT) find_dependency(fmt) endif() From 5b1d5d797d0d977365bfe4cf6fd6c5ce0005934b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 31 May 2024 08:45:12 +0200 Subject: [PATCH 50/61] ci: Clang-18 Debug configuration disabled in Freestanding CI --- .github/workflows/ci-freestanding.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci-freestanding.yml b/.github/workflows/ci-freestanding.yml index 3d46883d..e9efc059 100644 --- a/.github/workflows/ci-freestanding.yml +++ b/.github/workflows/ci-freestanding.yml @@ -71,6 +71,10 @@ jobs: conan-config: "", } build_type: ["Release", "Debug"] + # TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler + exclude: + - build_type: "Debug" + config: { name: "Clang-18" } env: CC: ${{ matrix.config.compiler.cc }} From 22c82f1c00a19b873d1d94ba2c6f2857f73e211a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 31 May 2024 09:02:00 +0200 Subject: [PATCH 51/61] docs: `MP_UNITS_DEV_CLANG_TIDY` added and release tags updated for other `_DEV_` options --- .../getting_started/installation_and_usage.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index ed46249e..175bf545 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -394,20 +394,27 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA } -: [:octicons-tag-24: 2.0.0][build la support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`) +: [:octicons-tag-24: 2.2.0][cmake build la support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`) Enables building code depending on the linear algebra library. - [build la support]: https://github.com/mpusz/mp-units/releases/tag/v2.0.0 - + [cmake build la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 [`MP_UNITS_DEV_IWYU`](#MP_UNITS_DEV_IWYU){ #MP_UNITS_DEV_IWYU } -: [:octicons-tag-24: 2.0.0][iwyu support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`) +: [:octicons-tag-24: 2.2.0][cmake iwyu support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`) - Enables `include-what-you-use` when compiling with a clang compiler. + Enables include-what-you-use analysis. - [iwyu support]: https://github.com/mpusz/mp-units/releases/tag/v2.0.0 + [cmake iwyu support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 + +[`MP_UNITS_DEV_CLANG_TIDY`](#MP_UNITS_DEV_CLANG_TIDY){ #MP_UNITS_DEV_CLANG_TIDY } + +: [:octicons-tag-24: 2.2.0][cmake clang-tidy support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`) + + Enables clang-tidy analysis. + + [cmake clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 ## CMake with presets support From ad94c2e6deaec84e2533ac082424bd0fc241d6c6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 31 May 2024 09:03:32 +0200 Subject: [PATCH 52/61] style: whitespaces trimmed in `ci-freestanding.yml` --- .github/workflows/ci-freestanding.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-freestanding.yml b/.github/workflows/ci-freestanding.yml index e9efc059..407949d3 100644 --- a/.github/workflows/ci-freestanding.yml +++ b/.github/workflows/ci-freestanding.yml @@ -71,7 +71,7 @@ jobs: conan-config: "", } build_type: ["Release", "Debug"] - # TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler + # TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler exclude: - build_type: "Debug" config: { name: "Clang-18" } From a874d643014cf9b64381ed405b82413ea1d0e02e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 31 May 2024 09:27:05 +0200 Subject: [PATCH 53/61] fix: incorrect CMake boolean logic fixed --- CMakeLists.txt | 2 +- test/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9e82b33..17624eb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ endif() # add project code add_subdirectory(src) -if(!${projectPrefix}API_FREESTANDING) +if(NOT ${projectPrefix}API_FREESTANDING) # add usage example add_subdirectory(example) endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5519a7df..cdc5120d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,7 +22,7 @@ cmake_minimum_required(VERSION 3.5) -if(!${projectPrefix}API_FREESTANDING) +if(NOT ${projectPrefix}API_FREESTANDING) add_subdirectory(runtime) endif() add_subdirectory(static) From ec287664ee9d58e8bb41adf1f2098bd0943325b4 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 31 May 2024 21:07:42 +0200 Subject: [PATCH 54/61] refactor: some TODO comments resolved --- example/glide_computer.cpp | 4 ---- src/core/include/mp-units/bits/hacks.h | 5 ----- src/core/include/mp-units/ext/type_traits.h | 5 ++--- test/static/quantity_test.cpp | 8 ++------ test/static/unit_test.cpp | 1 - 5 files changed, 4 insertions(+), 19 deletions(-) diff --git a/example/glide_computer.cpp b/example/glide_computer.cpp index 533002a1..4da86872 100644 --- a/example/glide_computer.cpp +++ b/example/glide_computer.cpp @@ -90,8 +90,6 @@ void print(const R& gliders) for (const auto& p : g.polar) { const auto ratio = glide_ratio(g.polar[0]).force_in(one); std::cout << MP_UNITS_STD_FMT::format(" * {::N[.4]} @ {::N[.1]} -> {::N[.1]} ({::N[.1]})\n", p.climb, p.v, ratio, - // TODO is it possible to make ADL work below (we need another set of trig - // functions for strong angle in a different namespace) si::asin(1 / ratio).force_in(si::degree)); } std::cout << "\n"; @@ -168,8 +166,6 @@ void example() const auto weather_conditions = get_weather_conditions(); const task t = {waypoints[0], waypoints[1], waypoints[0]}; const aircraft_tow tow = {400 * m, 1.6 * m / s}; - // TODO use C++20 date library when available - // set `start_time` to 11:00 am today const timestamp start_time(std::chrono::system_clock::now()); print(sfty); diff --git a/src/core/include/mp-units/bits/hacks.h b/src/core/include/mp-units/bits/hacks.h index 2dd10f84..5a0d3e11 100644 --- a/src/core/include/mp-units/bits/hacks.h +++ b/src/core/include/mp-units/bits/hacks.h @@ -87,14 +87,9 @@ #endif -// TODO revise the below when clang-18 is released -#if MP_UNITS_COMP_CLANG >= 18 && __cplusplus >= 202300L && !defined __cpp_explicit_this_parameter - -#define __cpp_explicit_this_parameter 202110L #endif - #if !defined __cpp_lib_ranges_to_container namespace std { diff --git a/src/core/include/mp-units/ext/type_traits.h b/src/core/include/mp-units/ext/type_traits.h index ce68be69..2058d3c8 100644 --- a/src/core/include/mp-units/ext/type_traits.h +++ b/src/core/include/mp-units/ext/type_traits.h @@ -88,9 +88,8 @@ void to_base_specialization_of(const volatile Type*); } // namespace detail template typename Type> -// inline constexpr bool // TODO: Replace with concept when it works with MSVC -concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of(t); }; - +inline constexpr bool is_derived_from_specialization_of = + requires(T* t) { detail::to_base_specialization_of(t); }; namespace detail { diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index 1489a4df..5cea40a5 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -406,9 +406,6 @@ static_assert((std::uint8_t{255}* m %= 257 * m).numerical_value_in(m) != [] { return ui %= 257; }()); -// TODO ICE -// (https://developercommunity2.visualstudio.com/t/ICE-on-a-constexpr-operator-in-mp-unit/1302907) -#ifndef MP_UNITS_COMP_MSVC // clang-17 with modules build on ignores disabling conversion warnings #if !(defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 18 && defined MP_UNITS_MODULES) // next two lines trigger conversions warnings @@ -418,7 +415,6 @@ static_assert((22 * m /= 3.33).numerical_value_in(m) == 6); static_assert((22 * m *= 33.33 * one).numerical_value_in(m) == 733); static_assert((22 * m /= 3.33 * one).numerical_value_in(m) == 6); #endif -#endif template typename Q> concept invalid_compound_assignments = requires() { @@ -847,8 +843,8 @@ static_assert(10 * isq::mechanical_energy[J] == 5 * isq::force[N] * (2 * isq::le static_assert(1 * si::si2019::speed_of_light_in_vacuum == 299'792'458 * isq::speed[m / s]); // Different named dimensions -template // TODO Use `Reference` when Clang supports it. -concept invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; }; +template +inline constexpr bool invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; }; static_assert(invalid_comparison); diff --git a/test/static/unit_test.cpp b/test/static/unit_test.cpp index 2d4a6092..25ef2ce2 100644 --- a/test/static/unit_test.cpp +++ b/test/static/unit_test.cpp @@ -210,7 +210,6 @@ static_assert(kilojoule.symbol == "kJ"); static_assert(is_of_type, si::kilo_>); static_assert(is_of_type, si::kilo_>); -// TODO Should the below be a scaled version of metre^2? static_assert(is_of_type>); // !!! static_assert(is_of_type>>); // !!! From 1ea2df92092baa7ff481ef552d19306d5eee3db0 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 1 Jun 2024 09:12:16 +0200 Subject: [PATCH 55/61] refactor: most `std::remove_const_t` removed and some replaced with the GCC-specific workaround --- docs/blog/posts/2.2.0-released.md | 4 +- .../mp-units/bits/get_associated_quantity.h | 2 +- src/core/include/mp-units/bits/hacks.h | 7 + src/core/include/mp-units/ext/type_traits.h | 4 +- .../mp-units/framework/dimension_concepts.h | 2 +- .../mp-units/framework/expression_template.h | 4 +- .../include/mp-units/framework/quantity.h | 3 +- .../mp-units/framework/quantity_point.h | 12 +- .../framework/quantity_point_concepts.h | 4 +- .../mp-units/framework/quantity_spec.h | 48 ++++--- .../framework/quantity_spec_concepts.h | 4 +- .../include/mp-units/framework/reference.h | 6 +- .../mp-units/framework/reference_concepts.h | 2 +- .../mp-units/framework/system_reference.h | 6 +- src/core/include/mp-units/framework/unit.h | 16 +-- .../mp-units/framework/unit_concepts.h | 4 +- .../systems/iec80000/binary_prefixes.h | 16 +-- .../include/mp-units/systems/si/prefixes.h | 48 +++---- test/static/concepts_test.cpp | 126 +++++++++--------- test/static/quantity_spec_test.cpp | 8 +- test/static/reference_test.cpp | 2 +- test/static/si_test.cpp | 2 +- 22 files changed, 166 insertions(+), 164 deletions(-) diff --git a/docs/blog/posts/2.2.0-released.md b/docs/blog/posts/2.2.0-released.md index 1eb92de4..936840a1 100644 --- a/docs/blog/posts/2.2.0-released.md +++ b/docs/blog/posts/2.2.0-released.md @@ -363,7 +363,7 @@ Example 1 (clang): 61 | concept QuantityOf = Quantity && QuantitySpecOf, QS>; | ^ note: because 'implicitly_convertible(kind_of_ > >{}, struct speed{{{}}})' evaluated to false - 147 | QuantitySpec && QuantitySpec> && implicitly_convertible(T{}, QS) && + 147 | QuantitySpec && QuantitySpec && implicitly_convertible(T{}, QS) && | ^ 1 error generated. Compiler returned: 1 @@ -386,7 +386,7 @@ Example 1 (clang): 61 | concept QuantityOf = Quantity && QuantitySpecOf, QS>; | ^ note: because 'implicitly_convertible(kind_of_ >{{}, {{}}}>{}, struct speed{{{}}})' evaluated to false - 147 | QuantitySpec && QuantitySpec> && implicitly_convertible(T{}, QS) && + 147 | QuantitySpec && QuantitySpec && implicitly_convertible(T{}, QS) && | ^ 1 error generated. Compiler returned: 1 diff --git a/src/core/include/mp-units/bits/get_associated_quantity.h b/src/core/include/mp-units/bits/get_associated_quantity.h index 2a474303..881c61e8 100644 --- a/src/core/include/mp-units/bits/get_associated_quantity.h +++ b/src/core/include/mp-units/bits/get_associated_quantity.h @@ -59,7 +59,7 @@ template [[nodiscard]] consteval auto get_associated_quantity_impl(U u); template -using to_quantity_spec = std::remove_const_t; +using to_quantity_spec = decltype(get_associated_quantity_impl(U{})); template [[nodiscard]] consteval auto get_associated_quantity_impl(U u) diff --git a/src/core/include/mp-units/bits/hacks.h b/src/core/include/mp-units/bits/hacks.h index 5a0d3e11..5030debb 100644 --- a/src/core/include/mp-units/bits/hacks.h +++ b/src/core/include/mp-units/bits/hacks.h @@ -87,6 +87,13 @@ #endif +#if MP_UNITS_COMP_GCC + +#define MP_UNITS_REMOVE_CONST(expr) std::remove_const_t + +#else + +#define MP_UNITS_REMOVE_CONST(expr) expr #endif diff --git a/src/core/include/mp-units/ext/type_traits.h b/src/core/include/mp-units/ext/type_traits.h index 2058d3c8..a31b7214 100644 --- a/src/core/include/mp-units/ext/type_traits.h +++ b/src/core/include/mp-units/ext/type_traits.h @@ -136,7 +136,7 @@ concept one_of = (false || ... || std::same_as); template [[nodiscard]] consteval bool contains() { - return (false || ... || is_same_v, T>); + return (false || ... || is_same_v); } template typename T, typename... Ts> @@ -160,7 +160,7 @@ template auto V> template [[nodiscard]] consteval auto get() { - if constexpr (is_same_v>) + if constexpr (is_same_v) return V1; else return get(); diff --git a/src/core/include/mp-units/framework/dimension_concepts.h b/src/core/include/mp-units/framework/dimension_concepts.h index 9826e1b0..11be622d 100644 --- a/src/core/include/mp-units/framework/dimension_concepts.h +++ b/src/core/include/mp-units/framework/dimension_concepts.h @@ -109,6 +109,6 @@ concept Dimension = detail::BaseDimension || detail::DerivedDimension; * Satisfied when both argument satisfy a `Dimension` concept and when they compare equal. */ MP_UNITS_EXPORT template -concept DimensionOf = Dimension && Dimension> && (T{} == D); +concept DimensionOf = Dimension && Dimension && (T{} == D); } // namespace mp_units diff --git a/src/core/include/mp-units/framework/expression_template.h b/src/core/include/mp-units/framework/expression_template.h index c0eddbda..291bfedb 100644 --- a/src/core/include/mp-units/framework/expression_template.h +++ b/src/core/include/mp-units/framework/expression_template.h @@ -534,8 +534,8 @@ template typename Proj, template typename To, ty expr_type_projectable... Dens> [[nodiscard]] consteval auto expr_map_impl(type_list, type_list) { - return (OneType{} * ... * map_power(typename expr_type_map, Proj>::type{})) / - (OneType{} * ... * map_power(typename expr_type_map, Proj>::type{})); + return (OneType{} * ... * map_power(typename expr_type_map::type{})) / + (OneType{} * ... * map_power(typename expr_type_map::type{})); } /** diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index f67a79bd..fb63eb82 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -140,8 +140,7 @@ public: template requires std::same_as, Rep> - constexpr quantity(Value&& v, std::remove_const_t) : - numerical_value_is_an_implementation_detail_(std::forward(v)) + constexpr quantity(Value&& v, decltype(R)) : numerical_value_is_an_implementation_detail_(std::forward(v)) { } diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index adacb6b7..818eea6c 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -169,8 +169,7 @@ public: template requires QuantityOf, get_quantity_spec(R)> && std::constructible_from - constexpr quantity_point(Q&& q, std::remove_const_t) : - quantity_from_origin_is_an_implementation_detail_(std::forward(q)) + constexpr quantity_point(Q&& q, decltype(PO)) : quantity_from_origin_is_an_implementation_detail_(std::forward(q)) { } @@ -187,7 +186,6 @@ public: template QP> requires std::constructible_from - // TODO add perfect forwarding // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(!std::convertible_to) quantity_point(const QP& qp) : quantity_from_origin_is_an_implementation_detail_([&] { @@ -222,7 +220,7 @@ public: [[nodiscard]] constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(QuantityPointOf) auto point_for( NewPO new_origin) const { - if constexpr (is_same_v>) + if constexpr (is_same_v) return *this; else return ::mp_units::quantity_point{*this - new_origin, new_origin}; @@ -388,7 +386,7 @@ explicit( template // TODO simplify when gcc catches up - requires ReferenceOf, PO1.quantity_spec> + requires ReferenceOf [[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point& qp, const quantity& q) requires requires { qp.quantity_ref_from(PO1) + q; } @@ -401,7 +399,7 @@ template template // TODO simplify when gcc catches up - requires ReferenceOf, PO2.quantity_spec> + requires ReferenceOf [[nodiscard]] constexpr QuantityPoint auto operator+(const quantity& q, const quantity_point& qp) requires requires { q + qp.quantity_ref_from(PO2); } @@ -425,7 +423,7 @@ template template // TODO simplify when gcc catches up - requires ReferenceOf, PO1.quantity_spec> + requires ReferenceOf [[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point& qp, const quantity& q) requires requires { qp.quantity_ref_from(PO1) - q; } diff --git a/src/core/include/mp-units/framework/quantity_point_concepts.h b/src/core/include/mp-units/framework/quantity_point_concepts.h index ee5ca8eb..aa2a13cc 100644 --- a/src/core/include/mp-units/framework/quantity_point_concepts.h +++ b/src/core/include/mp-units/framework/quantity_point_concepts.h @@ -105,7 +105,7 @@ concept PointOrigin = detail::AbsolutePointOrigin || detail::RelativePointOri * Satisfied by all quantity point origins that are defined using a provided quantity specification. */ MP_UNITS_EXPORT template -concept PointOriginFor = PointOrigin && QuantitySpecOf, T::quantity_spec>; +concept PointOriginFor = PointOrigin && QuantitySpecOf; MP_UNITS_EXPORT template auto PO, RepresentationOf Rep> @@ -141,7 +141,7 @@ template template concept SameAbsolutePointOriginAs = - PointOrigin && PointOrigin> && same_absolute_point_origins(T{}, V); + PointOrigin && PointOrigin && same_absolute_point_origins(T{}, V); } // namespace detail diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 78d039c1..3ec46f40 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -218,11 +218,11 @@ MP_UNITS_EXPORT_END */ #ifdef MP_UNITS_API_NO_CRTP template auto... Args> - requires(... && !QuantitySpec>) + requires(... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #else template auto... Args> - requires(... && !QuantitySpec>) + requires(... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #endif static constexpr detail::BaseDimension auto dimension = Dim; @@ -261,11 +261,11 @@ struct quantity_spec : detail::quantity_spec_interface */ #ifdef MP_UNITS_API_NO_CRTP template auto... Args> - requires(... && !QuantitySpec>) + requires(... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #else template auto... Args> - requires(... && !QuantitySpec>) + requires(... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #endif static constexpr auto _equation_ = Eq; @@ -302,12 +302,12 @@ struct quantity_spec : detail::quantity_spec_interface */ #ifdef MP_UNITS_API_NO_CRTP template auto... Args> - requires(... && !QuantitySpec>) -struct quantity_spec : std::remove_const_t { + requires(... && !QuantitySpec) +struct quantity_spec : decltype(QS) { #else template auto... Args> - requires(... && !QuantitySpec>) -struct quantity_spec : std::remove_const_t { + requires(... && !QuantitySpec) +struct quantity_spec : decltype(QS) { #endif static constexpr auto _parent_ = QS; static constexpr quantity_character character = detail::quantity_character_init(QS.character); @@ -361,16 +361,16 @@ struct quantity_spec : std::remove_const_t { #ifdef MP_UNITS_API_NO_CRTP template auto... Args> - requires(!requires { QS._equation_; } || - (requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) && - (... && !QuantitySpec>) + requires(!requires { QS._equation_; } || (requires { + QS._equation_; + } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) struct quantity_spec : quantity_spec { #else template auto... Args> - requires(!requires { QS._equation_; } || - (requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) && - (... && !QuantitySpec>) + requires(!requires { QS._equation_; } || (requires { + QS._equation_; + } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) struct quantity_spec : quantity_spec { #endif static constexpr auto _equation_ = Eq; @@ -483,7 +483,7 @@ struct kind_of_ : quantity_spec, Q{}> { MP_UNITS_EXPORT template requires(detail::get_kind_tree_root(Q) == Q) -inline constexpr kind_of_> kind_of; +inline constexpr kind_of_ kind_of; namespace detail { @@ -493,7 +493,7 @@ struct is_dimensionless : std::true_type {}; template [[nodiscard]] consteval QuantitySpec auto clone_kind_of(Q q) { - if constexpr ((... && QuantityKindSpec>)) + if constexpr ((... && QuantityKindSpec)) return kind_of; else return q; @@ -566,11 +566,9 @@ template detail::expr_pow( detail::remove_kind(q))); else if constexpr (Den == 1) - return detail::clone_kind_of( - derived_quantity_spec, Num>>{}); + return detail::clone_kind_of(derived_quantity_spec>{}); else - return detail::clone_kind_of( - derived_quantity_spec, Num, Den>>{}); + return detail::clone_kind_of(derived_quantity_spec>{}); } @@ -1398,7 +1396,7 @@ template return are_ingredients_convertible(from, to); } else if constexpr (IntermediateDerivedQuantitySpec) { auto res = explode(from); - if constexpr (NamedQuantitySpec>) + if constexpr (NamedQuantitySpec) return convertible_impl(res.quantity, to); else if constexpr (requires { to._equation_; }) { auto eq = explode_to_equation(to); @@ -1407,7 +1405,7 @@ template return are_ingredients_convertible(from, to); } else if constexpr (IntermediateDerivedQuantitySpec) { auto res = explode(to); - if constexpr (NamedQuantitySpec>) + if constexpr (NamedQuantitySpec) return min(res.result, convertible_impl(from, res.quantity)); else if constexpr (requires { from._equation_; }) return min(res.result, convertible_impl(from._equation_, res.quantity)); @@ -1452,7 +1450,7 @@ namespace detail { template requires requires(Q q) { get_kind_tree_root(q); } -using to_kind = std::remove_const_t; +using to_kind = decltype(get_kind_tree_root(Q{})); #ifdef MP_UNITS_API_NO_CRTP template @@ -1509,8 +1507,8 @@ template requires(implicitly_convertible(get_kind_tree_root(q1), get_kind_tree_root(q2)) || implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1))) { - using QQ1 = std::remove_const_t; - using QQ2 = std::remove_const_t; + using QQ1 = decltype(detail::remove_kind(q1)); + using QQ2 = decltype(detail::remove_kind(q2)); // NOLINTBEGIN(bugprone-branch-clone) if constexpr (is_same_v) diff --git a/src/core/include/mp-units/framework/quantity_spec_concepts.h b/src/core/include/mp-units/framework/quantity_spec_concepts.h index 920e9cf5..2887ae5e 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -140,7 +140,7 @@ namespace detail { template concept NestedQuantityKindSpecOf = - QuantitySpec> && QuantitySpec> && + QuantitySpec && QuantitySpec && get_kind(From) != get_kind(To) && std::derived_from, std::remove_cvref_t>; @@ -148,7 +148,7 @@ concept NestedQuantityKindSpecOf = MP_UNITS_EXPORT template concept QuantitySpecOf = - QuantitySpec && QuantitySpec> && implicitly_convertible(T{}, QS) && + QuantitySpec && QuantitySpec && implicitly_convertible(T{}, QS) && // the below is to make the following work // static_assert(ReferenceOf); // static_assert(!ReferenceOf); diff --git a/src/core/include/mp-units/framework/reference.h b/src/core/include/mp-units/framework/reference.h index 8050055a..6bd334e2 100644 --- a/src/core/include/mp-units/framework/reference.h +++ b/src/core/include/mp-units/framework/reference.h @@ -38,7 +38,7 @@ namespace mp_units { namespace detail { template -using reference_t = reference, std::remove_const_t>; +using reference_t = reference; } @@ -258,13 +258,13 @@ MP_UNITS_EXPORT_END namespace detail { template -[[nodiscard]] consteval std::remove_const_t clone_reference_with(From) +[[nodiscard]] consteval decltype(To) clone_reference_with(From) { return {}; } template -[[nodiscard]] consteval reference> clone_reference_with(reference) +[[nodiscard]] consteval reference clone_reference_with(reference) { return {}; } diff --git a/src/core/include/mp-units/framework/reference_concepts.h b/src/core/include/mp-units/framework/reference_concepts.h index 6ac2187b..a2c78365 100644 --- a/src/core/include/mp-units/framework/reference_concepts.h +++ b/src/core/include/mp-units/framework/reference_concepts.h @@ -76,7 +76,7 @@ template * the provided quantity_spec type. */ template -concept ReferenceOf = Reference && QuantitySpecOf, QS>; +concept ReferenceOf = Reference && QuantitySpecOf; MP_UNITS_EXPORT_END diff --git a/src/core/include/mp-units/framework/system_reference.h b/src/core/include/mp-units/framework/system_reference.h index 39f16bcf..0bfcabae 100644 --- a/src/core/include/mp-units/framework/system_reference.h +++ b/src/core/include/mp-units/framework/system_reference.h @@ -60,7 +60,7 @@ namespace mp_units { * @tparam CoU coherent unit for a quantity in this system */ template - requires(!AssociatedUnit>) || (CoU == one) + requires(!AssociatedUnit) || (CoU == one) struct system_reference { static constexpr auto quantity_spec = Q; static constexpr auto coherent_unit = CoU; @@ -68,9 +68,9 @@ struct system_reference { template requires(convertible(coherent_unit, U{})) #if MP_UNITS_COMP_MSVC - [[nodiscard]] constexpr decltype(reference, U>{}) operator[](U) const + [[nodiscard]] constexpr decltype(reference{}) operator[](U) const #else - [[nodiscard]] constexpr reference, U> operator[](U) const + [[nodiscard]] constexpr reference operator[](U) const #endif { return {}; diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index a96c043f..c00952ff 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -175,13 +175,13 @@ struct named_unit { */ template requires(!Symbol.empty()) -struct named_unit : std::remove_const_t { +struct named_unit : decltype(U) { static constexpr auto symbol = Symbol; ///< Unique unit identifier }; template requires(!Symbol.empty()) -struct named_unit : std::remove_const_t { +struct named_unit : decltype(U) { static constexpr auto symbol = Symbol; ///< Unique unit identifier static constexpr auto point_origin = PO; }; @@ -197,14 +197,14 @@ struct named_unit : std::remove_const_t { */ template requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension) -struct named_unit : std::remove_const_t { +struct named_unit : decltype(U) { static constexpr auto symbol = Symbol; ///< Unique unit identifier static constexpr auto quantity_spec = QS; }; template requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension) -struct named_unit : std::remove_const_t { +struct named_unit : decltype(U) { static constexpr auto symbol = Symbol; ///< Unique unit identifier static constexpr auto quantity_spec = QS; static constexpr auto point_origin = PO; @@ -234,7 +234,7 @@ struct named_unit : std::remove_const_t { */ MP_UNITS_EXPORT template requires(!Symbol.empty()) -struct prefixed_unit : std::remove_const_t { +struct prefixed_unit : decltype(M * U) { static constexpr auto symbol = Symbol + U.symbol; }; @@ -392,7 +392,7 @@ template return canonical_unit{pow(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit}; } else { return canonical_unit{pow(base.mag), - derived_unit, Num, Den...>>{}}; + derived_unit>{}}; } } @@ -573,7 +573,7 @@ template else if constexpr (detail::ratio{Num, Den} == 1) return u; else if constexpr (detail::is_specialization_of_scaled_unit) - return scaled_unit(U::mag), std::remove_const_t(U::reference_unit))>>{}; + return scaled_unit(U::mag), decltype(pow(U::reference_unit))>{}; else if constexpr (detail::is_specialization_of_derived_unit) return detail::expr_pow(u); else if constexpr (Den == 1) @@ -659,7 +659,7 @@ template return u1; else { constexpr auto cm = detail::common_magnitude(canonical_lhs.mag, canonical_rhs.mag); - return scaled_unit>{}; + return scaled_unit{}; } } } diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index c536e0b4..02a00c7a 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -190,7 +190,7 @@ concept AssociatedUnit = Unit && detail::has_associated_quantity(U{}); */ MP_UNITS_EXPORT template concept UnitOf = - AssociatedUnit && QuantitySpec> && + AssociatedUnit && QuantitySpec && implicitly_convertible(get_quantity_spec(U{}), QS) && // the below is to make `dimensionless[radian]` invalid (get_kind(QS) == get_kind(get_quantity_spec(U{})) || !detail::NestedQuantityKindSpecOf); @@ -209,7 +209,7 @@ namespace detail { */ MP_UNITS_EXPORT template concept UnitCompatibleWith = - Unit && Unit> && QuantitySpec> && + Unit && Unit && QuantitySpec && (!AssociatedUnit || UnitOf)&&detail::have_same_canonical_reference_unit(U{}, U2); diff --git a/src/systems/include/mp-units/systems/iec80000/binary_prefixes.h b/src/systems/include/mp-units/systems/iec80000/binary_prefixes.h index bfcc9b4f..921ab90b 100644 --- a/src/systems/include/mp-units/systems/iec80000/binary_prefixes.h +++ b/src/systems/include/mp-units/systems/iec80000/binary_prefixes.h @@ -42,14 +42,14 @@ template struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, MP_UNITS_EXPORT_BEGIN -template inline constexpr kibi_> kibi; -template inline constexpr mebi_> mebi; -template inline constexpr gibi_> gibi; -template inline constexpr tebi_> tebi; -template inline constexpr pebi_> pebi; -template inline constexpr exbi_> exbi; -template inline constexpr zebi_> zebi; -template inline constexpr yobi_> yobi; +template inline constexpr kibi_ kibi; +template inline constexpr mebi_ mebi; +template inline constexpr gibi_ gibi; +template inline constexpr tebi_ tebi; +template inline constexpr pebi_ pebi; +template inline constexpr exbi_ exbi; +template inline constexpr zebi_ zebi; +template inline constexpr yobi_ yobi; // clang-format on MP_UNITS_EXPORT_END diff --git a/src/systems/include/mp-units/systems/si/prefixes.h b/src/systems/include/mp-units/systems/si/prefixes.h index da2fb5b7..fa7bbf1f 100644 --- a/src/systems/include/mp-units/systems/si/prefixes.h +++ b/src/systems/include/mp-units/systems/si/prefixes.h @@ -58,30 +58,30 @@ template struct quetta_ : prefixed_unit<"Q", mag_power<10, 30> MP_UNITS_EXPORT_BEGIN -template inline constexpr quecto_> quecto; -template inline constexpr ronto_> ronto; -template inline constexpr yocto_> yocto; -template inline constexpr zepto_> zepto; -template inline constexpr atto_> atto; -template inline constexpr femto_> femto; -template inline constexpr pico_> pico; -template inline constexpr nano_> nano; -template inline constexpr micro_> micro; -template inline constexpr milli_> milli; -template inline constexpr centi_> centi; -template inline constexpr deci_> deci; -template inline constexpr deca_> deca; -template inline constexpr hecto_> hecto; -template inline constexpr kilo_> kilo; -template inline constexpr mega_> mega; -template inline constexpr giga_> giga; -template inline constexpr tera_> tera; -template inline constexpr peta_> peta; -template inline constexpr exa_> exa; -template inline constexpr zetta_> zetta; -template inline constexpr yotta_> yotta; -template inline constexpr ronna_> ronna; -template inline constexpr quetta_> quetta; +template inline constexpr quecto_ quecto; +template inline constexpr ronto_ ronto; +template inline constexpr yocto_ yocto; +template inline constexpr zepto_ zepto; +template inline constexpr atto_ atto; +template inline constexpr femto_ femto; +template inline constexpr pico_ pico; +template inline constexpr nano_ nano; +template inline constexpr micro_ micro; +template inline constexpr milli_ milli; +template inline constexpr centi_ centi; +template inline constexpr deci_ deci; +template inline constexpr deca_ deca; +template inline constexpr hecto_ hecto; +template inline constexpr kilo_ kilo; +template inline constexpr mega_ mega; +template inline constexpr giga_ giga; +template inline constexpr tera_ tera; +template inline constexpr peta_ peta; +template inline constexpr exa_ exa; +template inline constexpr zetta_ zetta; +template inline constexpr yotta_ yotta; +template inline constexpr ronna_ ronna; +template inline constexpr quetta_ quetta; // clang-format on MP_UNITS_EXPORT_END diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index b43ed0dd..68033efa 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -49,9 +49,9 @@ struct dim_speed : decltype(isq::dim_length / isq::dim_time) {}; // BaseDimension static_assert(detail::BaseDimension); -static_assert(!detail::BaseDimension>); -static_assert(!detail::BaseDimension>); -static_assert(!detail::BaseDimension(isq::dim_length))>>); +static_assert(!detail::BaseDimension); +static_assert(!detail::BaseDimension); +static_assert(!detail::BaseDimension(isq::dim_length))>); static_assert(!detail::BaseDimension>>); static_assert(!detail::BaseDimension); static_assert(!detail::BaseDimension>); @@ -59,9 +59,9 @@ static_assert(!detail::BaseDimension); static_assert(!detail::BaseDimension); // DerivedDimension -static_assert(detail::DerivedDimension>); -static_assert(detail::DerivedDimension>); -static_assert(detail::DerivedDimension(isq::dim_length))>>); +static_assert(detail::DerivedDimension); +static_assert(detail::DerivedDimension); +static_assert(detail::DerivedDimension(isq::dim_length))>); static_assert(detail::DerivedDimension>>); static_assert(detail::DerivedDimension); static_assert(!detail::DerivedDimension); @@ -71,9 +71,9 @@ static_assert(!detail::DerivedDimension); // Dimension static_assert(Dimension); -static_assert(Dimension>); -static_assert(Dimension>); -static_assert(Dimension(isq::dim_length))>>); +static_assert(Dimension); +static_assert(Dimension); +static_assert(Dimension(isq::dim_length))>); static_assert(Dimension>>); static_assert(Dimension); static_assert(!Dimension); @@ -90,9 +90,9 @@ struct speed : decltype(isq::length / isq::time) {}; // this is not recommended static_assert(QuantitySpec); static_assert(QuantitySpec); static_assert(QuantitySpec); -static_assert(QuantitySpec)>>); -static_assert(QuantitySpec>); -static_assert(QuantitySpec(isq::length))>>); +static_assert(QuantitySpec)>); +static_assert(QuantitySpec); +static_assert(QuantitySpec(isq::length))>); static_assert(QuantitySpec); static_assert(!QuantitySpec); static_assert(!QuantitySpec); @@ -103,8 +103,8 @@ static_assert(detail::NamedQuantitySpec); static_assert(detail::NamedQuantitySpec); static_assert(detail::NamedQuantitySpec); static_assert(!detail::NamedQuantitySpec)>>); -static_assert(!detail::NamedQuantitySpec>); -static_assert(!detail::NamedQuantitySpec(isq::length))>>); +static_assert(!detail::NamedQuantitySpec); +static_assert(!detail::NamedQuantitySpec(isq::length))>); static_assert(detail::NamedQuantitySpec); static_assert(!detail::NamedQuantitySpec); static_assert(!detail::NamedQuantitySpec); @@ -113,10 +113,10 @@ static_assert(!detail::NamedQuantitySpec); // IntermediateDerivedQuantitySpec static_assert(!detail::IntermediateDerivedQuantitySpec); static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec)>>); +static_assert(!detail::IntermediateDerivedQuantitySpec)>); static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(detail::IntermediateDerivedQuantitySpec>); -static_assert(detail::IntermediateDerivedQuantitySpec(isq::length))>>); +static_assert(detail::IntermediateDerivedQuantitySpec); +static_assert(detail::IntermediateDerivedQuantitySpec(isq::length))>); static_assert(!detail::IntermediateDerivedQuantitySpec); static_assert(!detail::IntermediateDerivedQuantitySpec); static_assert(!detail::IntermediateDerivedQuantitySpec); @@ -127,8 +127,8 @@ static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec); static_assert(detail::QuantityKindSpec)>>); static_assert(!detail::QuantityKindSpec); -static_assert(!detail::QuantityKindSpec>); -static_assert(!detail::QuantityKindSpec(isq::length))>>); +static_assert(!detail::QuantityKindSpec); +static_assert(!detail::QuantityKindSpec(isq::length))>); static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec); @@ -142,13 +142,13 @@ struct metre_per_second : decltype(si::metre / si::second) {}; static_assert(Unit); static_assert(Unit); -static_assert(Unit)>>); +static_assert(Unit)>); static_assert(Unit); -static_assert(Unit>); -static_assert(Unit>); -static_assert(Unit * si::second)>>); -static_assert(Unit>); -static_assert(Unit(si::metre))>>); +static_assert(Unit); +static_assert(Unit); +static_assert(Unit * si::second)>); +static_assert(Unit); +static_assert(Unit(si::metre))>); static_assert(Unit); static_assert(Unit, struct si::second>>); static_assert(Unit); @@ -169,12 +169,12 @@ static_assert(!Unit); static_assert(detail::NamedUnit); static_assert(detail::NamedUnit); static_assert(!detail::NamedUnit); -static_assert(!detail::NamedUnit)>>); -static_assert(!detail::NamedUnit>); -static_assert(!detail::NamedUnit>); -static_assert(!detail::NamedUnit * si::second)>>); -static_assert(!detail::NamedUnit>); -static_assert(!detail::NamedUnit(si::metre))>>); +static_assert(!detail::NamedUnit)>); +static_assert(!detail::NamedUnit); +static_assert(!detail::NamedUnit); +static_assert(!detail::NamedUnit * si::second)>); +static_assert(!detail::NamedUnit); +static_assert(!detail::NamedUnit(si::metre))>); static_assert(detail::NamedUnit); static_assert(!detail::NamedUnit, struct si::second>>); static_assert(!detail::NamedUnit); @@ -195,12 +195,12 @@ static_assert(!detail::NamedUnit); static_assert(PrefixableUnit); static_assert(PrefixableUnit); static_assert(!PrefixableUnit); -static_assert(!PrefixableUnit)>>); -static_assert(!PrefixableUnit>); -static_assert(!PrefixableUnit>); -static_assert(!PrefixableUnit * si::second)>>); -static_assert(!PrefixableUnit>); -static_assert(!PrefixableUnit(si::metre))>>); +static_assert(!PrefixableUnit)>); +static_assert(!PrefixableUnit); +static_assert(!PrefixableUnit); +static_assert(!PrefixableUnit * si::second)>); +static_assert(!PrefixableUnit); +static_assert(!PrefixableUnit(si::metre))>); static_assert(PrefixableUnit); static_assert(!PrefixableUnit, struct si::second>>); static_assert(!PrefixableUnit); @@ -221,12 +221,12 @@ static_assert(!PrefixableUnit); static_assert(AssociatedUnit); static_assert(!AssociatedUnit); static_assert(AssociatedUnit); -static_assert(AssociatedUnit)>>); -static_assert(AssociatedUnit>); -static_assert(AssociatedUnit>); -static_assert(AssociatedUnit * si::second)>>); -static_assert(AssociatedUnit>); -static_assert(AssociatedUnit(si::metre))>>); +static_assert(AssociatedUnit)>); +static_assert(AssociatedUnit); +static_assert(AssociatedUnit); +static_assert(AssociatedUnit * si::second)>); +static_assert(AssociatedUnit); +static_assert(AssociatedUnit(si::metre))>); static_assert(AssociatedUnit); static_assert(AssociatedUnit, struct si::second>>); static_assert(AssociatedUnit); @@ -260,13 +260,13 @@ static_assert(!UnitOf); // Reference static_assert(Reference); -static_assert(Reference>); -static_assert(Reference>); -static_assert(Reference>); -static_assert(Reference>); +static_assert(Reference); +static_assert(Reference); +static_assert(Reference); +static_assert(Reference); static_assert(!Reference); static_assert(!Reference); -static_assert(!Reference)>>); +static_assert(!Reference)>); static_assert(!Reference); static_assert(!Reference); @@ -274,24 +274,24 @@ static_assert(!Reference); static_assert(ReferenceOf); static_assert(ReferenceOf); static_assert(!ReferenceOf); -static_assert(ReferenceOf, isq::length>); -static_assert(!ReferenceOf, isq::radius>); -static_assert(ReferenceOf, isq::length>); -static_assert(ReferenceOf, isq::radius>); +static_assert(ReferenceOf); +static_assert(!ReferenceOf); +static_assert(ReferenceOf); +static_assert(ReferenceOf); static_assert(!ReferenceOf); static_assert(ReferenceOf); -static_assert(ReferenceOf, dimensionless>); -static_assert(ReferenceOf, isq::rotation>); -static_assert(ReferenceOf, dimensionless>); +static_assert(ReferenceOf); +static_assert(ReferenceOf); +static_assert(ReferenceOf); static_assert(ReferenceOf); static_assert(!ReferenceOf); -static_assert(ReferenceOf, isq::angular_measure>); -static_assert(!ReferenceOf, dimensionless>); +static_assert(ReferenceOf); +static_assert(!ReferenceOf); static_assert(ReferenceOf); static_assert(ReferenceOf); -static_assert(!ReferenceOf, isq::rotation>); -static_assert(!ReferenceOf, isq::angular_measure>); +static_assert(!ReferenceOf); +static_assert(!ReferenceOf); // Representation static_assert(Representation); @@ -324,7 +324,7 @@ static_assert(Quantity>); static_assert(!Quantity); #endif static_assert(!Quantity>); -static_assert(!Quantity>); +static_assert(!Quantity); // QuantityOf static_assert(QuantityOf, isq::length>); @@ -365,7 +365,7 @@ static_assert(QuantityPoint>); static_assert(QuantityPoint>); static_assert(QuantityPoint>); static_assert(QuantityPoint>); -static_assert(!QuantityPoint>); +static_assert(!QuantityPoint); static_assert(!QuantityPoint>); static_assert(!QuantityPoint); static_assert(!QuantityPoint); @@ -405,7 +405,7 @@ static_assert(!PointOrigin>); static_assert(!PointOrigin>); static_assert(!PointOrigin>); static_assert(!PointOrigin>); -static_assert(!PointOrigin>); +static_assert(!PointOrigin); #if MP_UNITS_HOSTED static_assert(!PointOrigin); static_assert(!PointOrigin>); @@ -431,7 +431,7 @@ static_assert(!PointOriginFor, static_assert(!PointOriginFor, isq::length>); static_assert(!PointOriginFor, isq::radius>); static_assert(!PointOriginFor, isq::time>); -static_assert(!PointOriginFor, isq::length>); +static_assert(!PointOriginFor); #if MP_UNITS_HOSTED static_assert(!PointOriginFor); static_assert(!PointOriginFor, isq::length>); diff --git a/test/static/quantity_spec_test.cpp b/test/static/quantity_spec_test.cpp index 49a2d87a..7a62e5c4 100644 --- a/test/static/quantity_spec_test.cpp +++ b/test/static/quantity_spec_test.cpp @@ -121,10 +121,10 @@ static_assert(!detail::NamedQuantitySpec); static_assert(detail::IntermediateDerivedQuantitySpec); static_assert(!detail::QuantityKindSpec); -static_assert(QuantitySpec>>); -static_assert(!detail::NamedQuantitySpec>>); -static_assert(detail::IntermediateDerivedQuantitySpec>>); -static_assert(detail::QuantityKindSpec>>); +static_assert(QuantitySpec>); +static_assert(!detail::NamedQuantitySpec>); +static_assert(detail::IntermediateDerivedQuantitySpec>); +static_assert(detail::QuantityKindSpec>); static_assert(QuantitySpec / kind_of