diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dc6b88789af7f82399f3ba350f60f8d9335acdf..d1b2d69f1ea2bd288621dc545771cd116d493448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception also supports the syntax `DUNE_THROW(ExceptionType, a << b) << c << d` and `DUNE_THROW(ExceptionType) << a << b`. +- Add concepts `Std::three_way_comparable` and `Std::three_way_comparable_with` as well as an + algorithm `Std::lexicographical_compare_three_way` to provide library utilities for the `<=>` + comparison operator. + - Add deduction guides to `TupleVector` analogous to `std::tuple`. ## Build system: Changelog diff --git a/dune/common/std/CMakeLists.txt b/dune/common/std/CMakeLists.txt index ebe7a94c007671b88a449a9f67ebd9b5a4e9c22d..fd07678e466fd9717ea9d0a812e1e375d80c9151 100644 --- a/dune/common/std/CMakeLists.txt +++ b/dune/common/std/CMakeLists.txt @@ -2,6 +2,8 @@ # SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception install(FILES + algorithm.hh + compare.hh default_accessor.hh extents.hh functional.hh diff --git a/dune/common/std/algorithm.hh b/dune/common/std/algorithm.hh new file mode 100644 index 0000000000000000000000000000000000000000..eae792643dd1e3dcc7328fd9e85081c6415013d2 --- /dev/null +++ b/dune/common/std/algorithm.hh @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file LICENSE.md in module root +// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_STD_ALGORITHM_HH +#define DUNE_COMMON_STD_ALGORITHM_HH + +#include <version> +#if !(__cpp_impl_three_way_comparison >= 201907L && __cpp_lib_concepts && __has_include(<compare>)) + #error "Three-way comparison requires language support!" +#endif + +#include <algorithm> +#include <compare> +#include <type_traits> + +#include <dune/common/std/compare.hh> + +namespace Dune::Std { + +/** + * \brief Lexicographically compares two ranges `[first1, last1)` and `[first2, last2)` + * using three-way comparison and produces a result of the strongest applicable + * comparison category type. + * + * Implementation taken from https://en.cppreference.com/w/cpp/algorithm/lexicographical_compare_three_way + * + * The standard implementation is available with libstdc++ >= 10 and libc++ >= 17 + */ +#if __cpp_lib_three_way_comparison >= 201907L + +using std::lexicographical_compare_three_way; + +#else // __cpp_lib_three_way_comparison + +template <class I1, class I2, class Cmp = Std::compare_three_way> +constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2, Cmp comp = {}) + -> decltype(comp(*f1, *f2)) +{ + using ret_t = decltype(comp(*f1, *f2)); + static_assert(std::disjunction_v< + std::is_same<ret_t, std::strong_ordering>, + std::is_same<ret_t, std::weak_ordering>, + std::is_same<ret_t, std::partial_ordering>>, + "The return type must be a comparison category type."); + + bool exhaust1 = (f1 == l1); + bool exhaust2 = (f2 == l2); + for (; !exhaust1 && !exhaust2; exhaust1 = (++f1 == l1), exhaust2 = (++f2 == l2)) + if (auto c = comp(*f1, *f2); c != 0) + return c; + + return !exhaust1 ? std::strong_ordering::greater: + !exhaust2 ? std::strong_ordering::less: + std::strong_ordering::equal; +} + +#endif // __cpp_lib_three_way_comparison + +} // end namespace Dune::Std + +#endif // DUNE_COMMON_STD_ALGORITHM_HH diff --git a/dune/common/std/compare.hh b/dune/common/std/compare.hh new file mode 100644 index 0000000000000000000000000000000000000000..2032ee68a96067cc4411aff5cb5993d66324b92f --- /dev/null +++ b/dune/common/std/compare.hh @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file LICENSE.md in module root +// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_STD_COMPARE_HH +#define DUNE_COMMON_STD_COMPARE_HH + +#include <compare> +#include <concepts> +#include <type_traits> +#include <utility> + +/** + * \file This file provides some concepts introduced in the c++ standard library + * <compare> and <concepts> not yet available in all library implementation. + * The code is partially extracted from https://en.cppreference.com/w/cpp/utility/compare. + */ + +namespace Dune::Std { +namespace Impl { + +template <class T, class Cat> +concept comparesAs = + std::same_as<std::common_comparison_category_t<T, Cat>, Cat>; + +template <class T, class U> +concept weaklyEqualityComparableWith = + requires(const std::remove_reference_t<T>& t, + const std::remove_reference_t<U>& u) + { + { t == u } -> std::convertible_to<bool>; + { t != u } -> std::convertible_to<bool>; + { u == t } -> std::convertible_to<bool>; + { u != t } -> std::convertible_to<bool>; + }; + +template <class T, class U> +concept partiallyOrderedWith = + requires(const std::remove_reference_t<T>& t, + const std::remove_reference_t<U>& u) + { + { t < u } -> std::convertible_to<bool>; + { t > u } -> std::convertible_to<bool>; + { t <= u } -> std::convertible_to<bool>; + { t >= u } -> std::convertible_to<bool>; + { u < t } -> std::convertible_to<bool>; + { u > t } -> std::convertible_to<bool>; + { u <= t } -> std::convertible_to<bool>; + { u >= t } -> std::convertible_to<bool>; + }; + +template <class T, class U, class C = std::common_reference_t<const T&, const U&>> +concept comparisonCommonTypeWithImpl = + std::same_as<std::common_reference_t<const T&, const U&>, + std::common_reference_t<const U&, const T&>> && + requires + { + requires std::convertible_to<const T&, const C&> || + std::convertible_to<T, const C&>; + requires std::convertible_to<const U&, const C&> || + std::convertible_to<U, const C&>; + }; + +template <class T, class U> +concept comparisonCommonTypeWith = + comparisonCommonTypeWithImpl<std::remove_cvref_t<T>, std::remove_cvref_t<U>>; + +} // end namespace Impl + +/** + * \brief The concept `std::three_way_comparable` specifies that the three way + * comparison `operator <=>` on `T` yield results consistent with the comparison + * category implied by `Cat`. + * + * The standard implementation is available in libstdc++ >= 10 and libc++ >= 14. + */ +template <class T, class Cat = std::partial_ordering> +concept three_way_comparable = + Impl::weaklyEqualityComparableWith<T, T> && + Impl::partiallyOrderedWith<T, T> && + requires(const std::remove_reference_t<T>& a, + const std::remove_reference_t<T>& b) + { + { a <=> b } -> Impl::comparesAs<Cat>; + }; + + +/** + * \brief The concept `std::three_way_comparable_with` specifies that the three + * way comparison `operator <=>` on (possibly mixed) `T` and `U` operands yield + * results consistent with the comparison category implied by `Cat`. Comparing + * mixed operands yields results equivalent to comparing the operands converted + * to their common type. + * + * The standard implementation is available in libstdc++ >= 10 and libc++ >= 14. + */ +template <class T, class U, class Cat = std::partial_ordering> +concept three_way_comparable_with = + Std::three_way_comparable<T, Cat> && + Std::three_way_comparable<U, Cat> && + Impl::comparisonCommonTypeWith<T, U> && + Std::three_way_comparable< + std::common_reference_t< + const std::remove_reference_t<T>&, + const std::remove_reference_t<U>&>, Cat> && + Impl::weaklyEqualityComparableWith<T, U> && + Impl::partiallyOrderedWith<T, U> && + requires(const std::remove_reference_t<T>& t, + const std::remove_reference_t<U>& u) + { + { t <=> u } -> Impl::comparesAs<Cat>; + { u <=> t } -> Impl::comparesAs<Cat>; + }; + +//! A functor implementing the three-way comparison on the arguments +struct compare_three_way +{ + template <class T, class U> + constexpr auto operator() (T&& t, U&& u) const + { + return std::forward<T>(t) <=> std::forward<U>(u); + } +}; + +} // end namespace Dune::Std + +#endif // DUNE_COMMON_STD_COMPARE_HH diff --git a/dune/common/std/test/CMakeLists.txt b/dune/common/std/test/CMakeLists.txt index e653c875fd488e9fd6e574adee3532263e72540c..81f5d188f8eee66f190146f90885366263bdc0cc 100644 --- a/dune/common/std/test/CMakeLists.txt +++ b/dune/common/std/test/CMakeLists.txt @@ -7,6 +7,12 @@ link_libraries(Dune::Common) dune_add_test(SOURCES accessorstest.cc LABELS quick) +dune_add_test(SOURCES algorithmtest.cc + LABELS quick) + +dune_add_test(SOURCES comparetest.cc + LABELS quick) + dune_add_test(SOURCES extentstest.cc LABELS quick) diff --git a/dune/common/std/test/algorithmtest.cc b/dune/common/std/test/algorithmtest.cc new file mode 100644 index 0000000000000000000000000000000000000000..311792e55f3490c9b6967938757d858e9efcd0c9 --- /dev/null +++ b/dune/common/std/test/algorithmtest.cc @@ -0,0 +1,26 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root +// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception +#include <algorithm> +#include <array> + +#include <dune/common/std/algorithm.hh> +#include <dune/common/test/testsuite.hh> + +int main(int argc, char** argv) +{ + using namespace Dune; + TestSuite testSuite; + + std::array<double, 10> arr1{}; + std::array<double, 10> arr2{}; + + std::fill(arr1.begin(), arr1.end(), 1.0); + std::fill(arr2.begin(), arr2.end(), 2.0); + + auto cmp = Std::lexicographical_compare_three_way(arr1.begin(), arr1.end(), arr2.begin(), arr2.end()); + testSuite.check(cmp < 0); + + return testSuite.exit(); +} diff --git a/dune/common/std/test/comparetest.cc b/dune/common/std/test/comparetest.cc new file mode 100644 index 0000000000000000000000000000000000000000..166057cdfc874d9336203a6dfccd654f81ab2bf7 --- /dev/null +++ b/dune/common/std/test/comparetest.cc @@ -0,0 +1,17 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root +// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception +#include <dune/common/std/compare.hh> + +int main(int argc, char** argv) +{ + using namespace Dune; + + static_assert(Std::three_way_comparable<double>); + static_assert(Std::three_way_comparable_with<double,double>); + static_assert(Std::three_way_comparable_with<double,float>); + static_assert(Std::three_way_comparable_with<double,int>); + + static_assert(not Std::three_way_comparable_with<unsigned int,int>); +}