From 249c3a0c9368faaf63efe02df43838f4e775c411 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 10 Apr 2019 23:31:55 +0200 Subject: [PATCH 01/33] Change conanfile.py to install with CMake [ci skip] --- conanfile.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/conanfile.py b/conanfile.py index 38ac305e..c6e3473a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,25 +1,24 @@ -from conans import ConanFile +from conans import CMake, ConanFile class CppSortConan(ConanFile): name = "cpp-sort" version = "1.4.0" - settings = "compiler" license = "https://github.com/Morwenn/cpp-sort/blob/master/license.txt" url = "https://github.com/Morwenn/cpp-sort" author = "Morwenn " description = "Additional sorting algorithms & related tools" - exports_sources = "include/*" + exports_sources = ("include/*", "CMakeLists.txt", "cmake/*") exports = "license.txt" no_copy_source = True - def configure(self): - if self.settings.compiler == "Visual Studio": - raise Exception("Visual Studio is not supported.") - def package(self): + cmake = CMake(self) + cmake.definitions["BUILD_TESTING"] = "OFF" + cmake.configure() + cmake.install() + self.copy("license*", dst="licenses", ignore_case=True, keep_path=False) - self.copy(pattern="*", src="include", dst="include") def package_id(self): self.info.header_only() From 44b0f8fed18a47f33075df382f653c72e882ae63 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 10 Apr 2019 23:41:15 +0200 Subject: [PATCH 02/33] More generators for Conan [ci skip] --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index c6e3473a..09899b18 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,6 +10,7 @@ class CppSortConan(ConanFile): description = "Additional sorting algorithms & related tools" exports_sources = ("include/*", "CMakeLists.txt", "cmake/*") exports = "license.txt" + generators = "cmake", "txt" no_copy_source = True def package(self): From d0c98c86f0e6fb2d1a8570c2c4605215eaeda2aa Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 22 Apr 2019 15:37:38 +0200 Subject: [PATCH 03/33] Bypass CMake architecture check in write_basic_package_version_file --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c263b772..100430e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,11 +48,16 @@ configure_package_config_file( ${CMAKE_INSTALL_LIBDIR}/cmake/cpp-sort ) +# Bypass automatic architeture check introduced by CMake, +# use the ARCH_INDEPENDENT option for this in the future +set(CPPSORT_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) +unset(CMAKE_SIZEOF_VOID_P) write_basic_package_version_file( ${CMAKE_BINARY_DIR}/cmake/cpp-sort-config-version.cmake COMPATIBILITY SameMajorVersion ) +set(CMAKE_SIZEOF_VOID_P ${CPPSORT_SIZEOF_VOID_P}) install( FILES From bfbcca89e2244c71eb74285c07dbd6977d27e6b7 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 28 Apr 2019 11:51:11 +0200 Subject: [PATCH 04/33] Minor cleanup --- include/cpp-sort/adapters/hybrid_adapter.h | 1 - include/cpp-sort/detail/split_sort.h | 2 ++ include/cpp-sort/sorters/split_sorter.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/cpp-sort/adapters/hybrid_adapter.h b/include/cpp-sort/adapters/hybrid_adapter.h index 32ed88af..9cb10f4b 100644 --- a/include/cpp-sort/adapters/hybrid_adapter.h +++ b/include/cpp-sort/adapters/hybrid_adapter.h @@ -178,7 +178,6 @@ namespace cppsort Sorters, sizeof...(Sorters) * detail::iterator_category_value> + sizeof...(Indices) - Indices - 1 - >... >; }; diff --git a/include/cpp-sort/detail/split_sort.h b/include/cpp-sort/detail/split_sort.h index 6331c01e..182aeeec 100644 --- a/include/cpp-sort/detail/split_sort.h +++ b/include/cpp-sort/detail/split_sort.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "inplace_merge.h" #include "iterator_traits.h" #include "pdqsort.h" @@ -65,6 +66,7 @@ namespace detail } else { // Everything is fine, add the new element to the subsequence ++middle; + using utility::iter_swap; iter_swap(middle, reader_it); } } diff --git a/include/cpp-sort/sorters/split_sorter.h b/include/cpp-sort/sorters/split_sorter.h index bfaf53cd..8b799895 100644 --- a/include/cpp-sort/sorters/split_sorter.h +++ b/include/cpp-sort/sorters/split_sorter.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include From 318c7b0560c698a0de1878be64cfbf5422e0e7e3 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 5 May 2019 16:59:54 +0200 Subject: [PATCH 05/33] Optimize out-of-place inplace_merge a bit --- include/cpp-sort/detail/inplace_merge.h | 28 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/include/cpp-sort/detail/inplace_merge.h b/include/cpp-sort/detail/inplace_merge.h index 6c15b787..b86ed68b 100644 --- a/include/cpp-sort/detail/inplace_merge.h +++ b/include/cpp-sort/detail/inplace_merge.h @@ -82,21 +82,37 @@ namespace detail ++result; } - for (; first1 != last1 ; ++result) { - if (first2 == last2) { - detail::move(first1, last1, result); - return; - } + if (first1 == last1) { + // first2 through last2 are already in the right spot + return; + } + + if (first2 == last2) { + detail::move(first1, last1, result); + return; + } + + while (true) { + CPPSORT_ASSUME(first1 != last1); + CPPSORT_ASSUME(first2 != last2); if (comp(proj(*first2), proj(*first1))) { *result = iter_move(first2); ++first2; + if (first2 == last2) { + detail::move(first1, last1, ++result); + return; + } } else { *result = iter_move(first1); ++first1; + if (first1 == last1) { + // first2 through last2 are already in the right spot + return; + } } + ++result; } - // first2 through last2 are already in the right spot. } //////////////////////////////////////////////////////////// From a607093c5ecab2e4e6f532c14bcf010ed5e7b2cc Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 5 May 2019 18:06:25 +0200 Subject: [PATCH 06/33] Rewrite merge_move to make it a bit more performant --- include/cpp-sort/detail/merge_move.h | 66 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/include/cpp-sort/detail/merge_move.h b/include/cpp-sort/detail/merge_move.h index 42f83d95..af7b34be 100644 --- a/include/cpp-sort/detail/merge_move.h +++ b/include/cpp-sort/detail/merge_move.h @@ -1,14 +1,26 @@ -// -*- C++ -*- -//===-------------------------- algorithm ---------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -// Modified in 2015-2017 by Morwenn for inclusion into cpp-sort -// -//===----------------------------------------------------------------------===// +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Morwenn + * + * 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. + */ #ifndef CPPSORT_DETAIL_MERGE_MOVE_H_ #define CPPSORT_DETAIL_MERGE_MOVE_H_ @@ -18,6 +30,7 @@ #include #include #include +#include "config.h" #include "move.h" namespace cppsort @@ -37,22 +50,33 @@ namespace detail auto&& proj1 = utility::as_function(projection1); auto&& proj2 = utility::as_function(projection2); - for (; first1 != last1; ++result) - { - if (first2 == last2) - return detail::move(first1, last1, result); - if (comp(proj2(*first2), proj1(*first1))) - { + if (first1 == last1) { + return detail::move(first2, last2, result); + } + + if (first2 == last2) { + return detail::move(first1, last1, result); + } + + while (true) { + CPPSORT_ASSUME(first1 != last1); + CPPSORT_ASSUME(first2 != last2); + + if (comp(proj2(*first2), proj1(*first1))) { *result = iter_move(first2); ++first2; - } - else - { + if (first2 == last2) { + return detail::move(first1, last1, ++result); + } + } else { *result = iter_move(first1); ++first1; + if (first1 == last1) { + return detail::move(first2, last2, ++result); + } } + ++result; } - return detail::move(std::move(first2), std::move(last2), std::move(result)); } }} From bef43154f8fb8367aee6e9d77afb202bd576af29 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 8 May 2019 01:18:06 +0200 Subject: [PATCH 07/33] Micro-optimize grail_sort --- include/cpp-sort/detail/grail_sort.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/cpp-sort/detail/grail_sort.h b/include/cpp-sort/detail/grail_sort.h index b6c0c9fd..7c023b13 100644 --- a/include/cpp-sort/detail/grail_sort.h +++ b/include/cpp-sort/detail/grail_sort.h @@ -189,9 +189,9 @@ namespace detail int len; if (p1 < q1) { len = std::distance(p1, q1); - while (p1 < q1) { + do { iter_swap(--q1, --q2); - } + } while (p1 < q1); } else { len = std::distance(p2, q2); atype = ftype; @@ -254,10 +254,7 @@ namespace detail } } if (out != p0) { - while (p0 != middle) { - *out = iter_move(p0); - ++out; ++p0; - } + detail::move(p0, middle, out); } } From 472e539c860173186bd6bc556a5c36d16e7f8bf4 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 8 May 2019 21:14:13 +0200 Subject: [PATCH 08/33] Make move[_backward] use std::move[_backward] when possible std::move & friends are optimized to fallback to std::memmove when possible, but are also optimized for some standard collection iterators such as std::deque iterators. While we can provide the optimization for pointers, we can't provide the other optimizations in a standard way, so we fallback to std::move[_backward] whenever iter_move support isn't required (best effort though) --- include/cpp-sort/detail/move.h | 151 +++++++++++++++++---------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/include/cpp-sort/detail/move.h b/include/cpp-sort/detail/move.h index 2aadda0d..da93d27b 100644 --- a/include/cpp-sort/detail/move.h +++ b/include/cpp-sort/detail/move.h @@ -1,125 +1,126 @@ -// -*- C++ -*- -//===-------------------------- algorithm ---------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -// // Modified in 2016-2017 by Morwenn for inclusion into cpp-sort -// -//===----------------------------------------------------------------------===// +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Morwenn + * + * 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. + */ #ifndef CPPSORT_DETAIL_MOVE_H_ #define CPPSORT_DETAIL_MOVE_H_ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include +#include #include #include #include +#include "type_traits.h" namespace cppsort { namespace detail { //////////////////////////////////////////////////////////// - // unwrap_iter + // Check whether iter_move is specialized + // + // The idea behind this check is that if an iterator has a + // dedicated iter_move found by ADL, then we ought to use it, + // otherwise we can fallback to std::move which is supposedly + // more optimized than what we would write by hand (notably + // for standard library iterators) + // - template - auto unwrap_iter(Iterator it) - -> Iterator + namespace hide_adl { - return it; - } + template + auto iter_move(Iterator) = delete; - template - auto unwrap_iter(std::move_iterator it) - -> std::enable_if_t< - std::is_trivially_copy_assignable::value, - T* - > - { - return it.base(); + struct dummy_callable + { + template + auto operator()(Iterator it) + -> decltype(iter_move(it)); + + template + auto operator()(const std::reverse_iterator& it) + -> decltype(iter_move(it.base())); // only there for type information + + template + auto operator()(const std::move_iterator& it) + -> decltype(iter_move(it.base())); + }; } //////////////////////////////////////////////////////////// // move template - auto move_impl(InputIterator first, InputIterator last, OutputIterator result) - -> OutputIterator - { - using utility::iter_move; - - for (; first != last; ++first, (void) ++result) { - *result = iter_move(first); - } - return result; - } - - template - auto move_impl(T* first, T* last, U* result) + auto move(InputIterator first, InputIterator last, OutputIterator result) -> std::enable_if_t< - std::is_same, U>::value && - std::is_trivially_copy_assignable::value, - U* + not is_invocable_v, + OutputIterator > { - const std::size_t n = static_cast(last - first); - if (n > 0) { - std::memmove(result, first, n * sizeof(U)); - } - return result + n; + return std::move(first, last, result); } template auto move(InputIterator first, InputIterator last, OutputIterator result) - -> OutputIterator + -> std::enable_if_t< + is_invocable_v, + OutputIterator + > { - return move_impl(unwrap_iter(first), unwrap_iter(last), unwrap_iter(result)); + for (; first != last; ++first, (void) ++result) { + using utility::iter_move; + *result = iter_move(first); + } + return result; } //////////////////////////////////////////////////////////// // move_backward template - auto move_backward_impl(InputIterator first, InputIterator last, OutputIterator result) - -> OutputIterator + auto move_backward(InputIterator first, InputIterator last, OutputIterator result) + -> std::enable_if_t< + not is_invocable_v, + OutputIterator + > { - using utility::iter_move; - - while (first != last) { - *--result = iter_move(--last); - } - return result; + return std::move_backward(first, last, result); } - template - auto move_backward_impl(T* first, T* last, U* result) + template + auto move_backward(InputIterator first, InputIterator last, OutputIterator result) -> std::enable_if_t< - std::is_same, U>::value && - std::is_trivially_copy_assignable::value, - U* + is_invocable_v, + OutputIterator > { - const std::size_t n = static_cast(last - first); - if (n > 0) { - result -= n; - std::memmove(result, first, n * sizeof(U)); + while (first != last) { + using utility::iter_move; + *--result = iter_move(--last); } return result; } - - template - auto move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, - BidirectionalIterator2 result) - -> BidirectionalIterator2 - { - return move_backward_impl(unwrap_iter(first), unwrap_iter(last), unwrap_iter(result)); - } }} #endif // CPPSORT_DETAIL_MOVE_H_ From e77f7efdda95f1929b3fda465922f22e54b9940f Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 18 May 2019 16:28:40 +0200 Subject: [PATCH 09/33] Fix -fno-sanitize-recover location --- testsuite/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 1c093f36..d5925c64 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -178,12 +178,15 @@ endif() # Optionally enable sanitizers if (UNIX AND SANITIZE) - target_compile_options(cpp-sort-testsuite PRIVATE -fsanitize=${SANITIZE}) + target_compile_options(cpp-sort-testsuite PRIVATE + -fsanitize=${SANITIZE} + -fno-sanitize-recover=all + ) set_property( TARGET cpp-sort-testsuite APPEND_STRING PROPERTY LINK_FLAGS - " -fsanitize=${SANITIZE} -fno-sanitize-recover=all" + " -fsanitize=${SANITIZE}" ) endif() From 9c4e5f26daa93b3167a5de950a629eab13dc42b2 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 9 Jun 2019 21:39:04 +0200 Subject: [PATCH 10/33] Support non-default-constructible empty function objects in three_way_compare --- include/cpp-sort/detail/three_way_compare.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/cpp-sort/detail/three_way_compare.h b/include/cpp-sort/detail/three_way_compare.h index 35f97d9c..91fe6d70 100644 --- a/include/cpp-sort/detail/three_way_compare.h +++ b/include/cpp-sort/detail/three_way_compare.h @@ -93,7 +93,10 @@ namespace detail } }; - template::type> + template< + typename Compare, + bool = std::is_empty::value && std::is_default_constructible::value + > struct three_way_compare: three_way_compare_base> { @@ -115,7 +118,7 @@ namespace detail }; template - struct three_way_compare: + struct three_way_compare: three_way_compare_base> { constexpr three_way_compare(Compare) {} @@ -131,7 +134,7 @@ namespace detail }; template<> - struct three_way_compare, std::true_type>: + struct three_way_compare, true>: three_way_compare_base>> { constexpr three_way_compare(std::less<>) {} @@ -158,7 +161,7 @@ namespace detail }; template<> - struct three_way_compare, std::true_type>: + struct three_way_compare, true>: three_way_compare_base>> { constexpr three_way_compare(std::greater<>) {} From ec91cf270de1edeb2f745cdb19bc156989d0a200 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 16 Jun 2019 23:02:46 +0200 Subject: [PATCH 11/33] Preliminary support for stateful sorters --- .../adapters/container_aware_adapter.h | 7 +- include/cpp-sort/adapters/counting_adapter.h | 34 ++- include/cpp-sort/adapters/hybrid_adapter.h | 262 ++++++++++------ include/cpp-sort/adapters/indirect_adapter.h | 27 +- .../cpp-sort/adapters/out_of_place_adapter.h | 20 +- include/cpp-sort/adapters/schwartz_adapter.h | 19 +- include/cpp-sort/adapters/self_sort_adapter.h | 26 +- .../cpp-sort/adapters/small_array_adapter.h | 13 +- include/cpp-sort/adapters/stable_adapter.h | 41 ++- include/cpp-sort/adapters/verge_adapter.h | 18 +- .../detail/container_aware/insertion_sort.h | 7 +- .../detail/container_aware/merge_sort.h | 7 +- .../detail/container_aware/selection_sort.h | 7 +- .../cpp-sort/detail/schwartz_small_array.h | 13 +- .../detail/stable_adapter_hybrid_adapter.h | 32 +- .../detail/stable_adapter_self_sort_adapter.h | 32 +- include/cpp-sort/sorter_facade.h | 284 ++++++++++-------- include/cpp-sort/utility/adapter_storage.h | 129 ++++++++ testsuite/CMakeLists.txt | 1 + testsuite/utility/adapter_storage.cpp | 138 +++++++++ 20 files changed, 801 insertions(+), 316 deletions(-) create mode 100644 include/cpp-sort/utility/adapter_storage.h create mode 100644 testsuite/utility/adapter_storage.cpp diff --git a/include/cpp-sort/adapters/container_aware_adapter.h b/include/cpp-sort/adapters/container_aware_adapter.h index cdfae140..0c1737d8 100644 --- a/include/cpp-sort/adapters/container_aware_adapter.h +++ b/include/cpp-sort/adapters/container_aware_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -354,7 +354,10 @@ namespace cppsort template struct container_aware_adapter: detail::container_aware_adapter_base, - sorter_facade_fptr> + detail::sorter_facade_fptr< + container_aware_adapter, + std::is_empty::value + > { container_aware_adapter() = default; diff --git a/include/cpp-sort/adapters/counting_adapter.h b/include/cpp-sort/adapters/counting_adapter.h index 36f10867..5687c49e 100644 --- a/include/cpp-sort/adapters/counting_adapter.h +++ b/include/cpp-sort/adapters/counting_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,7 @@ #include #include #include +#include #include "../detail/checkers.h" #include "../detail/comparison_counter.h" @@ -43,11 +44,18 @@ namespace cppsort namespace detail { - template + template struct counting_adapter_impl: - check_iterator_category, - check_is_always_stable + utility::adapter_storage, + check_iterator_category, + check_is_always_stable { + counting_adapter_impl() = default; + + constexpr explicit counting_adapter_impl(Sorter&& sorter): + utility::adapter_storage(std::move(sorter)) + {} + template< typename Iterable, typename Compare = std::less<>, @@ -60,7 +68,7 @@ namespace cppsort { CountType count(0); comparison_counter cmp(std::move(compare), count); - ComparisonSorter{}(std::forward(iterable), std::move(cmp)); + this->get()(std::forward(iterable), std::move(cmp)); return count; } @@ -76,7 +84,7 @@ namespace cppsort { CountType count(0); comparison_counter cmp(std::move(compare), count); - ComparisonSorter{}(std::move(first), std::move(last), std::move(cmp)); + this->get()(std::move(first), std::move(last), std::move(cmp)); return count; } @@ -94,7 +102,7 @@ namespace cppsort { CountType count(0); comparison_counter cmp(std::move(compare), count); - ComparisonSorter{}(std::forward(iterable), std::move(cmp), projection); + this->get()(std::forward(iterable), std::move(cmp), projection); return count; } @@ -112,24 +120,24 @@ namespace cppsort { CountType count(0); comparison_counter cmp(std::move(compare), count); - ComparisonSorter{}(std::move(first), std::move(last), - std::move(cmp), std::move(projection)); + this->get()(std::move(first), std::move(last), std::move(cmp), std::move(projection)); return count; } }; } - template + template struct counting_adapter: sorter_facade> { counting_adapter() = default; - // Automatic deduction guide - constexpr explicit counting_adapter(ComparisonSorter) noexcept {} + constexpr explicit counting_adapter(Sorter sorter): + sorter_facade>(std::move(sorter)) + {} }; //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/adapters/hybrid_adapter.h b/include/cpp-sort/adapters/hybrid_adapter.h index 9cb10f4b..3e53cd05 100644 --- a/include/cpp-sort/adapters/hybrid_adapter.h +++ b/include/cpp-sort/adapters/hybrid_adapter.h @@ -29,12 +29,16 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include #include #include +#include +#include "../detail/any_all.h" #include "../detail/checkers.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -51,6 +55,42 @@ namespace cppsort template<> struct choice<0> {}; + //////////////////////////////////////////////////////////// + // Mechanism used to unwrap nested hybrid_adapter + + template class Flattenable, class TypeList, class Accumulator> + struct flatten_fold; + + template< + template class Flattenable, + template class TsList, typename Front, typename... Rest, + template class AsList, typename... As + > + struct flatten_fold, AsList> + { + using type = typename flatten_fold, AsList>::type; + }; + + template< + template class Flattenable, + template class TsList, typename... InnerTs, typename... Rest, + template class AsList, typename... As + > + struct flatten_fold, Rest...>, AsList> + { + using type = typename flatten_fold, AsList>::type; + }; + + template< + template class Flattenable, + template class TsList, + typename Accumulator + > + struct flatten_fold, Accumulator> + { + using type = Accumulator; + }; + //////////////////////////////////////////////////////////// // Associate a priority to iterator categories, there is // probably a trick to automate that... @@ -73,44 +113,39 @@ namespace cppsort // Avoid just a bit of redundancy template using choice_for_it = choice< - (iterator_category_value> + 1) * N - 1 + (iterator_category_value< + iterator_category_t + > + 1) * N - 1 >; //////////////////////////////////////////////////////////// - // Import every operator() in one class - - template - struct sorters_merger: - Head, sorters_merger + // hybrid_adapter storage + // + // Unlike most adapters, hybrid_adapter actually wraps + // several sorters, and thus needs a dedicated storage with + // enhanced capabilities; it also does not provide any + // operator() because it wouldn't make sense since the + // storage itself doesn't know which sorter to call + + template + struct hybrid_adapter_storage_leaf: + utility::adapter_storage { - using Head::operator(); - using Head::_detail_stability; + hybrid_adapter_storage_leaf() = default; - using sorters_merger::operator(); - using sorters_merger::_detail_stability; - }; + constexpr hybrid_adapter_storage_leaf(Sorter&& sorter): + utility::adapter_storage(std::move(sorter)) + {}; - template - struct sorters_merger: - Head - { - using Head::operator(); - using Head::_detail_stability; - }; - - //////////////////////////////////////////////////////////// - // Add a dispatch to the operator() so that a sorter is - // preferred for its iterator category first, then for its - // position into the sorters + // Add a dispatch to the operator() so that a sorter is + // preferred for its iterator category first, then for its + // position into the sorters - template - struct selection_wrapper - { template auto operator()(choice, Args&&... args) const - -> decltype(Sorter{}(std::forward(args)...)) + -> decltype(this->get()(std::forward(args)...)) { - return Sorter{}(std::forward(args)...); + return this->get()(std::forward(args)...); } template @@ -121,107 +156,144 @@ namespace cppsort >; }; - //////////////////////////////////////////////////////////// - // Mechanism used to unwrap nested hybrid_adapter + template + struct hybrid_adapter_storage_impl: + Head, hybrid_adapter_storage_impl + { + hybrid_adapter_storage_impl() = default; - template class Flattenable, class TypeList, class Accumulator> - struct flatten_fold; + constexpr hybrid_adapter_storage_impl(Head&& head, Tail&&... tail): + Head(std::move(head)), + hybrid_adapter_storage_impl(std::move(tail)...) + {}; - template< - template class Flattenable, - template class TsList, typename Front, typename... Rest, - template class AsList, typename... As - > - struct flatten_fold, AsList> - { - using type = typename flatten_fold, AsList>::type; + using Head::operator(); + using Head::_detail_stability; + + using hybrid_adapter_storage_impl::operator(); + using hybrid_adapter_storage_impl::_detail_stability; }; - template< - template class Flattenable, - template class TsList, typename... InnerTs, typename... Rest, - template class AsList, typename... As - > - struct flatten_fold, Rest...>, AsList> + template + struct hybrid_adapter_storage_impl: + Head { - using type = typename flatten_fold, AsList>::type; + hybrid_adapter_storage_impl() = default; + + constexpr hybrid_adapter_storage_impl(Head&& head): + Head(std::move(head)) + {}; + + using Head::operator(); + using Head::_detail_stability; }; - template< - template class Flattenable, - template class TsList, - typename Accumulator - > - struct flatten_fold, Accumulator> + template + struct hybrid_adapter_storage; + + template + struct hybrid_adapter_storage, Sorters...>: + hybrid_adapter_storage_impl< + hybrid_adapter_storage_leaf< + sizeof...(Sorters) * iterator_category_value> + + sizeof...(Indices) - Indices - 1, + Sorters + >... + > { - using type = Accumulator; + using hybrid_adapter_storage_impl< + hybrid_adapter_storage_leaf< + sizeof...(Sorters) * iterator_category_value> + + sizeof...(Indices) - Indices - 1, + Sorters + >... + >::hybrid_adapter_storage_impl; + + //////////////////////////////////////////////////////////// + // Access a sorter by index & type + + template + auto get() && + -> decltype(auto) + { + return hybrid_adapter_storage_leaf< + sizeof...(Sorters) * iterator_category_value> + + sizeof...(Indices) - N - 1, + Sorter + >::get(); + } }; + //////////////////////////////////////////////////////////// + // Auto-unwrapping adapter implementation + template struct hybrid_adapter_impl: + detail::hybrid_adapter_storage, Sorters...>, detail::check_iterator_category, detail::check_is_always_stable, - sorter_facade_fptr> + detail::sorter_facade_fptr< + hybrid_adapter_impl, + detail::all(std::is_empty::value...) + > { private: - // Associate and index to every sorter depending on - // its position in the parameter pack - template - struct dispatch_sorter_impl; - - template - struct dispatch_sorter_impl> - { - using type = detail::sorters_merger< - detail::selection_wrapper< - Sorters, - sizeof...(Sorters) * detail::iterator_category_value> - + sizeof...(Indices) - Indices - 1 - >... - >; - }; - - // Dispatch-enabled sorter - using dispatch_sorter = typename dispatch_sorter_impl< - std::make_index_sequence - >::type; + using base_class = detail::hybrid_adapter_storage< + std::make_index_sequence, + Sorters... + >; public: + //////////////////////////////////////////////////////////// + // Construction + + hybrid_adapter_impl() = default; + + constexpr explicit hybrid_adapter_impl(Sorters&&... sorters): + base_class(std::move(sorters)...) + {} + + //////////////////////////////////////////////////////////// + // Call operator + template auto operator()(Iterable&& iterable, Args&&... args) const - -> decltype(dispatch_sorter{}( + -> decltype(base_class::operator()( detail::choice_for_it{}, std::forward(iterable), std::forward(args)... )) { - // Call the appropriate operator() - return dispatch_sorter{}( + return base_class::operator()( detail::choice_for_it{}, - std::forward(iterable), std::forward(args)... + std::forward(iterable), + std::forward(args)... ); } template auto operator()(Iterator first, Iterator last, Args&&... args) const - -> decltype(dispatch_sorter{}( + -> decltype(base_class::operator()( detail::choice_for_it{}, std::move(first), std::move(last), std::forward(args)... )) { - // Call the appropriate operator() - return dispatch_sorter{}( + return base_class::operator()( detail::choice_for_it{}, - std::move(first), std::move(last), std::forward(args)... + std::move(first), std::move(last), + std::forward(args)... ); } + //////////////////////////////////////////////////////////// + // Stability of a call + template static auto _detail_stability(Iterable&& iterable, Args&&... args) - -> decltype(dispatch_sorter::_detail_stability( + -> decltype(base_class::_detail_stability( detail::choice_for_it{}, std::forward(iterable), std::forward(args)... @@ -229,7 +301,7 @@ namespace cppsort template static auto _detail_stability(Iterator first, Iterator last, Args&&... args) - -> decltype(dispatch_sorter::_detail_stability( + -> decltype(base_class::_detail_stability( detail::choice_for_it{}, std::move(first), std::move(last), std::forward(args)... @@ -239,14 +311,24 @@ namespace cppsort //////////////////////////////////////////////////////////// // Adapter + template struct hybrid_adapter: - detail::flatten_fold, detail::hybrid_adapter_impl<>>::type + detail::flatten_fold< + hybrid_adapter, + hybrid_adapter, + detail::hybrid_adapter_impl<> + >::type { hybrid_adapter() = default; - // Automatic deduction guide - constexpr explicit hybrid_adapter(Sorters...) noexcept {} + constexpr explicit hybrid_adapter(Sorters... sorters): + detail::flatten_fold< + hybrid_adapter, + hybrid_adapter, + detail::hybrid_adapter_impl<> + >::type(std::move(sorters)...) + {} }; //////////////////////////////////////////////////////////// @@ -259,7 +341,7 @@ namespace cppsort } #ifdef CPPSORT_ADAPTERS_STABLE_ADAPTER_DONE_ -#include "../detail/stable_adapter_hybrid_adapter.h" +# include "../detail/stable_adapter_hybrid_adapter.h" #endif #define CPPSORT_ADAPTERS_HYBRID_ADAPTER_DONE_ diff --git a/include/cpp-sort/adapters/indirect_adapter.h b/include/cpp-sort/adapters/indirect_adapter.h index cb56cba7..29c3313b 100644 --- a/include/cpp-sort/adapters/indirect_adapter.h +++ b/include/cpp-sort/adapters/indirect_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -49,8 +50,15 @@ namespace cppsort { template struct indirect_adapter_impl: + utility::adapter_storage, check_is_always_stable { + indirect_adapter_impl() = default; + + constexpr explicit indirect_adapter_impl(Sorter&& sorter): + utility::adapter_storage(std::move(sorter)) + {} + template< typename RandomAccessIterator, typename Compare = std::less<>, @@ -78,8 +86,10 @@ namespace cppsort #ifndef __cpp_lib_uncaught_exceptions // Sort the iterators on pointed values - Sorter{}(std::begin(iterators), std::end(iterators), std::move(compare), - [&proj](RandomAccessIterator it) -> decltype(auto) { return proj(*it); }); + this->get()(std::begin(iterators), std::end(iterators), std::move(compare), + [&proj](RandomAccessIterator it) -> decltype(auto) { + return proj(*it); + }); #else // Work around the sorters that return void auto exit_function = make_scope_success([&] { @@ -125,8 +135,10 @@ namespace cppsort exit_function.release(); } - return Sorter{}(std::begin(iterators), std::end(iterators), std::move(compare), - [&proj](RandomAccessIterator it) -> decltype(auto) { return proj(*it); }); + return this->get()(std::begin(iterators), std::end(iterators), std::move(compare), + [&proj](RandomAccessIterator it) -> decltype(auto) { + return proj(*it); + }); #endif } @@ -143,8 +155,9 @@ namespace cppsort { indirect_adapter() = default; - // Automatic deduction guide - constexpr explicit indirect_adapter(Sorter) noexcept {} + constexpr explicit indirect_adapter(Sorter sorter): + sorter_facade>(std::move(sorter)) + {} }; //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/adapters/out_of_place_adapter.h b/include/cpp-sort/adapters/out_of_place_adapter.h index 1b7adfb0..2b713039 100644 --- a/include/cpp-sort/adapters/out_of_place_adapter.h +++ b/include/cpp-sort/adapters/out_of_place_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2018 Morwenn + * Copyright (c) 2018-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "../detail/checkers.h" #include "../detail/scope_exit.h" @@ -46,7 +47,7 @@ namespace cppsort namespace detail { template - auto sort_out_of_place(ForwardIterator first, Size size, Sorter sorter, Args&&... args) + auto sort_out_of_place(ForwardIterator first, Size size, const Sorter& sorter, Args&&... args) -> decltype(auto) { using utility::iter_move; @@ -88,17 +89,22 @@ namespace cppsort template struct out_of_place_adapter: + utility::adapter_storage, detail::check_iterator_category, detail::check_is_always_stable, - sorter_facade_fptr> + detail::sorter_facade_fptr< + out_of_place_adapter, + std::is_empty::value + > { //////////////////////////////////////////////////////////// // Construction out_of_place_adapter() = default; - // Automatic deduction guide - constexpr explicit out_of_place_adapter(Sorter) noexcept {} + constexpr explicit out_of_place_adapter(Sorter sorter): + utility::adapter_storage(std::move(sorter)) + {} //////////////////////////////////////////////////////////// // Wrap and call the sorter @@ -108,7 +114,7 @@ namespace cppsort -> decltype(auto) { auto size = std::distance(first, last); - return detail::sort_out_of_place(first, size, Sorter{}, std::forward(args)...); + return detail::sort_out_of_place(first, size, this->get(), std::forward(args)...); } template @@ -117,7 +123,7 @@ namespace cppsort { // Might be an optimization for forward/bidirectional iterables auto size = utility::size(iterable); - return detail::sort_out_of_place(std::begin(iterable), size, Sorter{}, std::forward(args)...); + return detail::sort_out_of_place(std::begin(iterable), size, this->get(), std::forward(args)...); } }; diff --git a/include/cpp-sort/adapters/schwartz_adapter.h b/include/cpp-sort/adapters/schwartz_adapter.h index 183467f0..294c3aa5 100644 --- a/include/cpp-sort/adapters/schwartz_adapter.h +++ b/include/cpp-sort/adapters/schwartz_adapter.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "../detail/associate_iterator.h" #include "../detail/checkers.h" @@ -51,9 +52,16 @@ namespace cppsort template struct schwartz_adapter_impl: + utility::adapter_storage, check_iterator_category, check_is_always_stable { + schwartz_adapter_impl() = default; + + constexpr explicit schwartz_adapter_impl(Sorter&& sorter): + utility::adapter_storage(std::move(sorter)) + {} + template< typename ForwardIterator, typename Compare, @@ -89,7 +97,7 @@ namespace cppsort } // Indirectly sort the original sequence - return Sorter{}( + return this->get()( make_associate_iterator(projected.get()), make_associate_iterator(projected.get() + size), std::move(compare), @@ -106,7 +114,7 @@ namespace cppsort > { // No projection to handle, forward everything to the adapted sorter - return Sorter{}(std::move(first), std::move(last), std::move(compare)); + return this->get()(std::move(first), std::move(last), std::move(compare)); } template @@ -115,7 +123,7 @@ namespace cppsort -> decltype(Sorter{}(std::move(first), std::move(last), std::move(compare), projection)) { // utility::identity does nothing, bypass schartz_adapter entirely - return Sorter{}(std::move(first), std::move(last), std::move(compare), projection); + return this->get()(std::move(first), std::move(last), std::move(compare), projection); } }; } @@ -126,8 +134,9 @@ namespace cppsort { schwartz_adapter() = default; - // Automatic deduction guide - constexpr explicit schwartz_adapter(Sorter) noexcept {} + constexpr explicit schwartz_adapter(Sorter sorter): + sorter_facade>(std::move(sorter)) + {} }; //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/adapters/self_sort_adapter.h b/include/cpp-sort/adapters/self_sort_adapter.h index 07b4394b..3bdfb1df 100644 --- a/include/cpp-sort/adapters/self_sort_adapter.h +++ b/include/cpp-sort/adapters/self_sort_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "../detail/checkers.h" #include "../detail/type_traits.h" @@ -78,16 +79,21 @@ namespace cppsort template struct self_sort_adapter: + utility::adapter_storage, detail::check_iterator_category, - sorter_facade_fptr> + detail::sorter_facade_fptr< + self_sort_adapter, + std::is_empty::value + > { //////////////////////////////////////////////////////////// // Construction self_sort_adapter() = default; - // Automatic deduction guide - constexpr explicit self_sort_adapter(Sorter) noexcept {} + constexpr explicit self_sort_adapter(Sorter sorter): + utility::adapter_storage(std::move(sorter)) + {} //////////////////////////////////////////////////////////// // Function call operator @@ -118,19 +124,19 @@ namespace cppsort -> std::enable_if_t< not detail::has_sort_method && not detail::has_stable_sort_method, - decltype(Sorter{}(std::forward(iterable), std::forward(args)...)) + decltype(this->get()(std::forward(iterable), std::forward(args)...)) > { - return Sorter{}(std::forward(iterable), std::forward(args)...); + return this->get()(std::forward(iterable), std::forward(args)...); } template auto operator()(Iterator first, Iterator last, Args&&... args) const - -> decltype(Sorter{}(std::move(first), std::move(last), - std::forward(args)...)) + -> decltype(this->get()(std::move(first), std::move(last), + std::forward(args)...)) { - return Sorter{}(std::move(first), std::move(last), - std::forward(args)...); + return this->get()(std::move(first), std::move(last), + std::forward(args)...); } //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/adapters/small_array_adapter.h b/include/cpp-sort/adapters/small_array_adapter.h index b6d4c606..00cd9aad 100644 --- a/include/cpp-sort/adapters/small_array_adapter.h +++ b/include/cpp-sort/adapters/small_array_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,7 @@ #include #include #include +#include "../detail/any_all.h" #include "../detail/is_in_pack.h" #include "../detail/type_traits.h" @@ -79,7 +80,10 @@ namespace cppsort > struct small_array_adapter>: fixed_sorter_traits, - sorter_facade_fptr>> + detail::sorter_facade_fptr< + small_array_adapter>, + detail::all(std::is_empty>::value...) + > { template< typename T, @@ -114,7 +118,10 @@ namespace cppsort template class FixedSizeSorter> struct small_array_adapter: fixed_sorter_traits, - sorter_facade_fptr> + detail::sorter_facade_fptr< + small_array_adapter, + true // TODO: how can we do better? + > { template< typename T, diff --git a/include/cpp-sort/adapters/stable_adapter.h b/include/cpp-sort/adapters/stable_adapter.h index f84b2d96..4bc63dce 100644 --- a/include/cpp-sort/adapters/stable_adapter.h +++ b/include/cpp-sort/adapters/stable_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include "../detail/associate_iterator.h" @@ -109,8 +110,15 @@ namespace cppsort template struct stable_adapter_impl: + utility::adapter_storage, check_iterator_category { + stable_adapter_impl() = default; + + constexpr explicit stable_adapter_impl(Sorter&& sorter): + utility::adapter_storage(std::move(sorter)) + {} + template< typename Iterator, typename Compare = std::less<>, @@ -146,7 +154,7 @@ namespace cppsort //////////////////////////////////////////////////////////// // Sort but takes the index into account to ensure stability - return Sorter{}( + return this->get()( make_associate_iterator(iterators.get()), make_associate_iterator(iterators.get() + size), make_stable_compare(std::move(compare), std::move(projection)) @@ -167,30 +175,35 @@ namespace cppsort { make_stable() = default; - // Automatic deduction guide - constexpr explicit make_stable(Sorter) noexcept {} + constexpr explicit make_stable(Sorter sorter): + sorter_facade>(std::move(sorter)) + {} }; // Actual sorter template struct stable_adapter: + utility::adapter_storage, detail::check_iterator_category, - sorter_facade_fptr> + detail::sorter_facade_fptr< + stable_adapter, + std::is_empty::value + > { stable_adapter() = default; - // Automatic deduction guide - constexpr explicit stable_adapter(Sorter) noexcept {} - + constexpr explicit stable_adapter(Sorter sorter): + utility::adapter_storage(std::move(sorter)) + {} template< typename... Args, typename = std::enable_if_t> > auto operator()(Args&&... args) const - -> decltype(Sorter{}(std::forward(args)...)) + -> decltype(this->get()(std::forward(args)...)) { - return Sorter{}(std::forward(args)...); + return this->get()(std::forward(args)...); } template< @@ -199,9 +212,9 @@ namespace cppsort typename = void > auto operator()(Args&&... args) const - -> decltype(make_stable{}(std::forward(args)...)) + -> decltype(make_stable(this->get())(std::forward(args)...)) { - return make_stable{}(std::forward(args)...); + return make_stable(this->get())(std::forward(args)...); } //////////////////////////////////////////////////////////// @@ -212,11 +225,11 @@ namespace cppsort } #ifdef CPPSORT_ADAPTERS_HYBRID_ADAPTER_DONE_ -#include "../detail/stable_adapter_hybrid_adapter.h" +# include "../detail/stable_adapter_hybrid_adapter.h" #endif #ifdef CPPSORT_ADAPTERS_SELF_SORT_ADAPTER_DONE_ -#include "../detail/stable_adapter_self_sort_adapter.h" +# include "../detail/stable_adapter_self_sort_adapter.h" #endif #define CPPSORT_ADAPTERS_STABLE_ADAPTER_DONE_ diff --git a/include/cpp-sort/adapters/verge_adapter.h b/include/cpp-sort/adapters/verge_adapter.h index f4ccd745..6ecfb7f8 100644 --- a/include/cpp-sort/adapters/verge_adapter.h +++ b/include/cpp-sort/adapters/verge_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2017-2018 Morwenn + * Copyright (c) 2017-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "../detail/vergesort.h" @@ -44,8 +45,15 @@ namespace cppsort namespace detail { template - struct verge_adapter_impl + struct verge_adapter_impl: + utility::adapter_storage { + verge_adapter_impl() = default; + + constexpr explicit verge_adapter_impl(FallbackSorter&& sorter): + utility::adapter_storage(std::move(sorter)) + {} + template< typename RandomAccessIterator, typename Compare = std::less<>, @@ -68,7 +76,7 @@ namespace cppsort vergesort(std::move(first), std::move(last), std::move(compare), std::move(projection), - FallbackSorter{}); + this->get()); } //////////////////////////////////////////////////////////// @@ -86,7 +94,9 @@ namespace cppsort verge_adapter() = default; // Automatic deduction guide - constexpr explicit verge_adapter(FallbackSorter) noexcept {} + constexpr explicit verge_adapter(FallbackSorter sorter): + sorter_facade>(std::move(sorter)) + {} }; } diff --git a/include/cpp-sort/detail/container_aware/insertion_sort.h b/include/cpp-sort/detail/container_aware/insertion_sort.h index 6d7ad083..0e8ea120 100644 --- a/include/cpp-sort/detail/container_aware/insertion_sort.h +++ b/include/cpp-sort/detail/container_aware/insertion_sort.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -116,7 +116,10 @@ namespace cppsort template<> struct container_aware_adapter: detail::container_aware_adapter_base, - sorter_facade_fptr> + detail::sorter_facade_fptr< + container_aware_adapter, + std::is_empty::value + > { container_aware_adapter() = default; constexpr explicit container_aware_adapter(insertion_sorter) noexcept {} diff --git a/include/cpp-sort/detail/container_aware/merge_sort.h b/include/cpp-sort/detail/container_aware/merge_sort.h index 5a51692e..4b626eaa 100644 --- a/include/cpp-sort/detail/container_aware/merge_sort.h +++ b/include/cpp-sort/detail/container_aware/merge_sort.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -89,7 +89,10 @@ namespace cppsort template<> struct container_aware_adapter: detail::container_aware_adapter_base, - sorter_facade_fptr> + detail::sorter_facade_fptr< + container_aware_adapter, + std::is_empty::value + > { using detail::container_aware_adapter_base::operator(); diff --git a/include/cpp-sort/detail/container_aware/selection_sort.h b/include/cpp-sort/detail/container_aware/selection_sort.h index c5febeac..f9568c24 100644 --- a/include/cpp-sort/detail/container_aware/selection_sort.h +++ b/include/cpp-sort/detail/container_aware/selection_sort.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -97,7 +97,10 @@ namespace cppsort template<> struct container_aware_adapter: detail::container_aware_adapter_base, - sorter_facade_fptr> + detail::sorter_facade_fptr< + container_aware_adapter, + std::is_empty::value + > { using detail::container_aware_adapter_base::operator(); diff --git a/include/cpp-sort/detail/schwartz_small_array.h b/include/cpp-sort/detail/schwartz_small_array.h index 407b3f05..f7ebd1b0 100644 --- a/include/cpp-sort/detail/schwartz_small_array.h +++ b/include/cpp-sort/detail/schwartz_small_array.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,7 @@ #include #include #include +#include "../detail/any_all.h" #include "../detail/is_in_pack.h" namespace cppsort @@ -45,7 +46,10 @@ namespace cppsort small_array_adapter> >: fixed_sorter_traits, - sorter_facade_fptr>>> + detail::sorter_facade_fptr< + schwartz_adapter>>, + detail::all(std::is_empty>::value...) + > { template< typename T, @@ -82,7 +86,10 @@ namespace cppsort template class FixedSizeSorter> struct schwartz_adapter>: fixed_sorter_traits, - sorter_facade_fptr>> + detail::sorter_facade_fptr< + schwartz_adapter>, + true // TODO: not sure how to specify that one + > { template< typename T, diff --git a/include/cpp-sort/detail/stable_adapter_hybrid_adapter.h b/include/cpp-sort/detail/stable_adapter_hybrid_adapter.h index 002970d7..b42ce0ac 100644 --- a/include/cpp-sort/detail/stable_adapter_hybrid_adapter.h +++ b/include/cpp-sort/detail/stable_adapter_hybrid_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2018 Morwenn + * Copyright (c) 2018-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,9 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include +#include namespace cppsort { @@ -35,18 +37,30 @@ namespace cppsort struct stable_adapter>: hybrid_adapter...> { - //////////////////////////////////////////////////////////// - // Construction + private: - stable_adapter() = default; + template + constexpr explicit stable_adapter(std::index_sequence, hybrid_adapter&& sorters): + hybrid_adapter...>( + (stable_adapter(std::move(sorters).template get()))... + ) + {} - // Automatic deduction guide - constexpr explicit stable_adapter(hybrid_adapter) noexcept {} + public: - //////////////////////////////////////////////////////////// - // Sorter traits + //////////////////////////////////////////////////////////// + // Construction - using is_always_stable = std::true_type; + stable_adapter() = default; + + constexpr explicit stable_adapter(hybrid_adapter sorters): + stable_adapter(std::make_index_sequence{}, std::move(sorters)) + {} + + //////////////////////////////////////////////////////////// + // Sorter traits + + using is_always_stable = std::true_type; }; } diff --git a/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h b/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h index 4a73ceee..d1404e1e 100644 --- a/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h +++ b/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "checkers.h" @@ -40,16 +41,21 @@ namespace cppsort { template struct stable_adapter>: + utility::adapter_storage, detail::check_iterator_category, - sorter_facade_fptr>> + detail::sorter_facade_fptr< + stable_adapter>, + std::is_empty::value + > { //////////////////////////////////////////////////////////// // Construction stable_adapter() = default; - // Automatic deduction guide - constexpr explicit stable_adapter(self_sort_adapter) noexcept {} + constexpr explicit stable_adapter(self_sort_adapter sorter): + utility::adapter_storage(std::move(sorter.get())) + {} //////////////////////////////////////////////////////////// // Generic cases @@ -68,17 +74,19 @@ namespace cppsort auto operator()(Iterable&& iterable, Args&&... args) const -> std::enable_if_t< not detail::has_stable_sort_method, - decltype(stable_adapter{}(std::forward(iterable), std::forward(args)...)) + decltype(stable_adapter(this->get())(std::forward(iterable), + std::forward(args)...)) > { - return stable_adapter{}(std::forward(iterable), std::forward(args)...); + return stable_adapter(this->get())(std::forward(iterable), + std::forward(args)...); } template auto operator()(Iterator first, Iterator last, Args&&... args) const - -> decltype(stable_adapter{}(first, last, std::forward(args)...)) + -> decltype(stable_adapter(this->get())(first, last, std::forward(args)...)) { - return stable_adapter{}(first, last, std::forward(args)...); + return stable_adapter(this->get())(first, last, std::forward(args)...); } //////////////////////////////////////////////////////////// @@ -89,7 +97,7 @@ namespace cppsort auto operator()(std::forward_list& iterable) const -> void { - return iterable.sort(); + iterable.sort(); } template< @@ -100,14 +108,14 @@ namespace cppsort auto operator()(std::forward_list& iterable, Compare compare) const -> void { - return iterable.sort(utility::as_function(compare)); + iterable.sort(utility::as_function(compare)); } template auto operator()(std::list& iterable) const -> void { - return iterable.sort(); + iterable.sort(); } template< @@ -118,7 +126,7 @@ namespace cppsort auto operator()(std::list& iterable, Compare compare) const -> void { - return iterable.sort(utility::as_function(compare)); + iterable.sort(utility::as_function(compare)); } //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/sorter_facade.h b/include/cpp-sort/sorter_facade.h index 9ec6993f..96a03c76 100644 --- a/include/cpp-sort/sorter_facade.h +++ b/include/cpp-sort/sorter_facade.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -39,135 +39,146 @@ namespace cppsort { - // Class that generates the required function pointers conversion - // operator depending on what the underlying sorter can handle - - template - class sorter_facade_fptr + namespace detail { - protected: - - // Function pointer types, a type alias is required - // for the function pointer conversion operator syntax - // to be valid - - template - using fptr_t = detail::invoke_result_t(*)(Iterable&); - - template - using fptr_rvalue_t = detail::invoke_result_t(*)(Iterable&&); - - template - using fptr_func_t = detail::invoke_result_t(*)(Iterable&, Func); - - template - using fptr_rvalue_func_t = detail::invoke_result_t(*)(Iterable&&, Func); - - template - using fptr_func2_t - = detail::invoke_result_t(*)(Iterable&, Func1, Func2); - - template - using fptr_rvalue_func2_t - = detail::invoke_result_t(*)(Iterable&&, Func1, Func2); - - template - using fptr_it_t = detail::invoke_result_t(*)(Iterator, Iterator); - - template - using fptr_func_it_t - = detail::invoke_result_t(*)(Iterator, Iterator, Func); - - template - using fptr_func2_it_t - = detail::invoke_result_t(*)(Iterator, Iterator, Func1, Func2); - - public: - - //////////////////////////////////////////////////////////// - // Conversion to function pointers - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_t() const - { - return [](Iterable& iterable) { - return Sorter{}(iterable); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_rvalue_t() const - { - return [](Iterable&& iterable) { - return Sorter{}(std::move(iterable)); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_func_t() const - { - return [](Iterable& iterable, Func func) { - return Sorter{}(iterable, func); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_rvalue_func_t() const - { - return [](Iterable&& iterable, Func func) { - return Sorter{}(std::move(iterable), func); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_func2_t() const - { - return [](Iterable& iterable, Func1 func1, Func2 func2) { - return Sorter{}(iterable, func1, func2); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_rvalue_func2_t() const - { - return [](Iterable&& iterable, Func1 func1, Func2 func2) { - return Sorter{}(std::move(iterable), func1, func2); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_it_t() const - { - return [](Iterator first, Iterator last) { - return Sorter{}(first, last); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_func_it_t() const - { - return [](Iterator first, Iterator last, Func func) { - return Sorter{}(first, last, func); - }; - } - - template - CPPSORT_CONSTEXPR_AFTER_CXX14 - operator fptr_func2_it_t() const - { - return [](Iterator first, Iterator last, Func1 func1, Func2 func2) { - return Sorter{}(first, last, func1, func2); - }; - } - }; + // Helper class to allow to convert a sorter_facade into a variety + // of function pointers, but only if the wrapped sorter is empty: + // conversion to function pointer does not make sense when state + // is involved + + template + class sorter_facade_fptr; + + template + class sorter_facade_fptr + { + protected: + + // Function pointer types, a type alias is required + // for the function pointer conversion operator syntax + // to be valid + + template + using fptr_t = detail::invoke_result_t(*)(Iterable&); + + template + using fptr_rvalue_t = detail::invoke_result_t(*)(Iterable&&); + + template + using fptr_func_t = detail::invoke_result_t(*)(Iterable&, Func); + + template + using fptr_rvalue_func_t = detail::invoke_result_t(*)(Iterable&&, Func); + + template + using fptr_func2_t + = detail::invoke_result_t(*)(Iterable&, Func1, Func2); + + template + using fptr_rvalue_func2_t + = detail::invoke_result_t(*)(Iterable&&, Func1, Func2); + + template + using fptr_it_t = detail::invoke_result_t(*)(Iterator, Iterator); + + template + using fptr_func_it_t + = detail::invoke_result_t(*)(Iterator, Iterator, Func); + + template + using fptr_func2_it_t + = detail::invoke_result_t(*)(Iterator, Iterator, Func1, Func2); + + public: + + //////////////////////////////////////////////////////////// + // Conversion to function pointers + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_t() const + { + return [](Iterable& iterable) { + return Sorter{}(iterable); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_rvalue_t() const + { + return [](Iterable&& iterable) { + return Sorter{}(std::move(iterable)); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_func_t() const + { + return [](Iterable& iterable, Func func) { + return Sorter{}(iterable, func); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_rvalue_func_t() const + { + return [](Iterable&& iterable, Func func) { + return Sorter{}(std::move(iterable), func); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_func2_t() const + { + return [](Iterable& iterable, Func1 func1, Func2 func2) { + return Sorter{}(iterable, func1, func2); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_rvalue_func2_t() const + { + return [](Iterable&& iterable, Func1 func1, Func2 func2) { + return Sorter{}(std::move(iterable), func1, func2); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_it_t() const + { + return [](Iterator first, Iterator last) { + return Sorter{}(first, last); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_func_it_t() const + { + return [](Iterator first, Iterator last, Func func) { + return Sorter{}(first, last, func); + }; + } + + template + CPPSORT_CONSTEXPR_AFTER_CXX14 + operator fptr_func2_it_t() const + { + return [](Iterator first, Iterator last, Func1 func1, Func2 func2) { + return Sorter{}(first, last, func1, func2); + }; + } + }; + + template + class sorter_facade_fptr {}; + } // This class takes an incomplete sorter, analyses it and creates // all the methods needed to complete it: additional overloads to @@ -176,10 +187,21 @@ namespace cppsort template struct sorter_facade: Sorter, - sorter_facade_fptr< - sorter_facade + detail::sorter_facade_fptr< + sorter_facade, + std::is_empty::value > { + //////////////////////////////////////////////////////////// + // Constructors + + sorter_facade() = default; + + template + constexpr sorter_facade(Args&&... args): + Sorter(std::forward(args)...) + {} + //////////////////////////////////////////////////////////// // Non-comparison overloads diff --git a/include/cpp-sort/utility/adapter_storage.h b/include/cpp-sort/utility/adapter_storage.h new file mode 100644 index 00000000..5f953886 --- /dev/null +++ b/include/cpp-sort/utility/adapter_storage.h @@ -0,0 +1,129 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Morwenn + * + * 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. + */ +#ifndef CPPSORT_UTILITY_ADAPTER_STORAGE_H_ +#define CPPSORT_UTILITY_ADAPTER_STORAGE_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +namespace cppsort +{ +namespace utility +{ + // + // Storage for adapters that adapt only one sorter at a + // time: it will contain nothing if the sorter is empty + // and default-constructible, and a copy of the sorter + // passed at construction time otherwise + // + // It can be called like the sorter it wraps with operator(), + // which should be hidden by the adapter's own operator() + // despite inheritance + // + + template< + typename Sorter, + bool = std::is_empty::value && std::is_default_constructible::value + > + struct adapter_storage; + + template + struct adapter_storage + { + adapter_storage() = default; + explicit constexpr adapter_storage(Sorter) noexcept {} + + template + constexpr auto operator()(Args&&... args) const + noexcept(noexcept(Sorter{}(std::forward(args)...))) + -> decltype(Sorter{}(std::forward(args)...)) + { + return Sorter{}(std::forward(args)...); + } + + auto get() const + -> Sorter + { + return Sorter{}; + } + }; + + template + struct adapter_storage + { + Sorter sorter; + + adapter_storage() = default; + + explicit constexpr adapter_storage(Sorter sorter) + noexcept(std::is_nothrow_copy_constructible::value): + sorter(sorter) + {} + + template + constexpr auto operator()(Args&&... args) const + noexcept(noexcept(sorter(std::forward(args)...))) + -> decltype(sorter(std::forward(args)...)) + { + return sorter(std::forward(args)...); + } + + template + constexpr auto operator()(Args&&... args) + noexcept(noexcept(sorter(std::forward(args)...))) + -> decltype(sorter(std::forward(args)...)) + { + return sorter(std::forward(args)...); + } + + auto get() & + -> Sorter& + { + return static_cast(sorter); + } + + auto get() const& + -> const Sorter& + { + return static_cast(sorter); + } + + auto get() && + -> Sorter&& + { + return static_cast(sorter); + } + + auto get() const&& + -> const Sorter&& + { + return static_cast(sorter); + } + }; +}} + +#endif // CPPSORT_UTILITY_ADAPTER_STORAGE_H_ diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index d5925c64..f9d67e3f 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -105,6 +105,7 @@ set( set( UTILITY_TESTS + utility/adapter_storage.cpp utility/as_projection.cpp utility/as_projection_iterable.cpp utility/branchless_traits.cpp diff --git a/testsuite/utility/adapter_storage.cpp b/testsuite/utility/adapter_storage.cpp new file mode 100644 index 00000000..67a90ccf --- /dev/null +++ b/testsuite/utility/adapter_storage.cpp @@ -0,0 +1,138 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Morwenn + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + //////////////////////////////////////////////////////////// + // Dummy adapter + + template + struct dummy_adapter_impl: + cppsort::utility::adapter_storage + { + dummy_adapter_impl() = default; + + constexpr dummy_adapter_impl(Sorter&& sorter): + cppsort::utility::adapter_storage(std::move(sorter)) + {} + + template< + typename ForwardIterator, + typename Compare = std::less<>, + typename Projection = cppsort::utility::identity + > + auto operator()(ForwardIterator first, ForwardIterator last, + Compare compare={}, Projection projection={}) const + -> std::enable_if_t> + { + this->get()(first, last, compare, projection); + } + }; + + template + struct dummy_adapter: + cppsort::sorter_facade> + { + dummy_adapter() = default; + + constexpr dummy_adapter(Sorter sorter): + cppsort::sorter_facade>(std::move(sorter)) + {} + }; + + //////////////////////////////////////////////////////////// + // Immutable & non-empty sorter + + struct non_empty_sorter_impl + { + int dummy1=0, dummy2=0; + + non_empty_sorter_impl() = default; + constexpr non_empty_sorter_impl(int a, int b): + dummy1(a), dummy2(b) + {} + + template< + typename Iterator, + typename Compare = std::less<>, + typename = std::enable_if_t< + not cppsort::is_projection_iterator_v + > + > + auto operator()(Iterator first, Iterator last, Compare compare={}) const + -> void + { + std::sort(std::move(first), std::move(last), std::move(compare)); + } + + using iterator_category = std::random_access_iterator_tag; + using is_always_stable = std::false_type; + }; + + struct non_empty_sorter: + cppsort::sorter_facade + { + non_empty_sorter() = default; + non_empty_sorter(int a, int b): + cppsort::sorter_facade(a, b) + {} + }; +} + +TEST_CASE( "test correct adapter_storage behavior", "[adapter_storage]" ) +{ + std::vector arr = { 2, 8, 7, 4, 3, 6, 0, 1, 2, 8, 6, 9 }; + + SECTION( "with an immutable empty sorter" ) + { + cppsort::selection_sorter original_sorter{}; + auto adapted_sorter = dummy_adapter(original_sorter); + + void(*my_sort)(std::vector&) = adapted_sorter; + my_sort(arr); + CHECK( std::is_sorted(std::begin(arr), std::end(arr)) ); + } + + SECTION( "with an immutable non-empty sorter" ) + { + non_empty_sorter original_sorter(5, 8); + auto adapted_sorter = dummy_adapter(original_sorter); + + adapted_sorter(arr); + CHECK( std::is_sorted(std::begin(arr), std::end(arr)) ); + } +} From f4c0e070835fbf43b289507024688e722b2e5b3e Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 16 Jun 2019 23:24:53 +0200 Subject: [PATCH 12/33] Try to fix Clang build --- include/cpp-sort/adapters/hybrid_adapter.h | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/include/cpp-sort/adapters/hybrid_adapter.h b/include/cpp-sort/adapters/hybrid_adapter.h index 3e53cd05..8ba117d7 100644 --- a/include/cpp-sort/adapters/hybrid_adapter.h +++ b/include/cpp-sort/adapters/hybrid_adapter.h @@ -251,7 +251,7 @@ namespace cppsort hybrid_adapter_impl() = default; - constexpr explicit hybrid_adapter_impl(Sorters&&... sorters): + constexpr explicit hybrid_adapter_impl(Sorters... sorters): base_class(std::move(sorters)...) {} @@ -319,17 +319,7 @@ namespace cppsort hybrid_adapter, detail::hybrid_adapter_impl<> >::type - { - hybrid_adapter() = default; - - constexpr explicit hybrid_adapter(Sorters... sorters): - detail::flatten_fold< - hybrid_adapter, - hybrid_adapter, - detail::hybrid_adapter_impl<> - >::type(std::move(sorters)...) - {} - }; + {}; //////////////////////////////////////////////////////////// // is_stable specializations From e5bc95a5c562aa08ef1315d71168163ef8a7f5ff Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 17 Jun 2019 00:54:10 +0200 Subject: [PATCH 13/33] Try to fix Travis cache on Windows VM --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9ee46147..890970eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -210,8 +210,10 @@ script: else travis_wait ctest -C ${BUILD_TYPE} --output-on-failure; fi + - cd .. after_success: + - cd build - if [[ $ENABLE_COVERAGE = true ]]; then make gcov; make lcov; @@ -219,6 +221,7 @@ after_success: fi after_failure: + - cd build - if [[ $USE_VALGRIND = true ]]; then find ./Testing/Temporary -type f -name "MemoryChecker.*.log" -size +1300c | xargs cat; fi From f57060745ed66702398bb7eda4767331710d083a Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 22 Jun 2019 17:15:01 +0200 Subject: [PATCH 14/33] Stateful sorters support for container_aware_adapter --- .../adapters/container_aware_adapter.h | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/include/cpp-sort/adapters/container_aware_adapter.h b/include/cpp-sort/adapters/container_aware_adapter.h index 0c1737d8..91296d25 100644 --- a/include/cpp-sort/adapters/container_aware_adapter.h +++ b/include/cpp-sort/adapters/container_aware_adapter.h @@ -33,6 +33,7 @@ #include #include #include +#include #include "../detail/projection_compare.h" #include "../detail/type_traits.h" @@ -108,8 +109,15 @@ namespace cppsort {}; template - struct container_aware_adapter_base + struct container_aware_adapter_base: + utility::adapter_storage { + container_aware_adapter_base() = default; + + constexpr explicit container_aware_adapter_base(Sorter&& sorter): + utility::adapter_storage(std::move(sorter)) + {} + template< bool Stability = false, typename Iterable @@ -120,11 +128,11 @@ namespace cppsort conditional_t< Stability, std::false_type, - decltype(detail::adl_despair{}(Sorter{}, iterable)) + decltype(detail::adl_despair{}(this->get(), iterable)) > > { - return detail::adl_despair{}(Sorter{}, iterable); + return detail::adl_despair{}(this->get(), iterable); } template< @@ -137,11 +145,11 @@ namespace cppsort conditional_t< Stability, cppsort::is_stable, - decltype(cppsort::sort(Sorter{}, iterable)) + decltype(cppsort::sort(this->get(), iterable)) > > { - return cppsort::sort(Sorter{}, iterable); + return cppsort::sort(this->get(), iterable); } template< @@ -155,11 +163,11 @@ namespace cppsort conditional_t< Stability, std::false_type, - decltype(detail::adl_despair{}(Sorter{}, iterable, std::move(compare))) + decltype(detail::adl_despair{}(this->get(), iterable, std::move(compare))) > > { - return detail::adl_despair{}(Sorter{}, iterable, std::move(compare)); + return detail::adl_despair{}(this->get(), iterable, std::move(compare)); } template< @@ -174,11 +182,11 @@ namespace cppsort conditional_t< Stability, cppsort::is_stable, - decltype(cppsort::sort(Sorter{}, iterable, std::move(compare))) + decltype(cppsort::sort(this->get(), iterable, std::move(compare))) > > { - return cppsort::sort(Sorter{}, iterable, std::move(compare)); + return cppsort::sort(this->get(), iterable, std::move(compare)); } template< @@ -193,11 +201,11 @@ namespace cppsort conditional_t< Stability, std::false_type, - decltype(detail::adl_despair{}(Sorter{}, iterable, std::move(projection))) + decltype(detail::adl_despair{}(this->get(), iterable, std::move(projection))) > > { - return detail::adl_despair{}(Sorter{}, iterable, std::move(projection)); + return detail::adl_despair{}(this->get(), iterable, std::move(projection)); } template< @@ -212,12 +220,12 @@ namespace cppsort conditional_t< Stability, std::false_type, - decltype(detail::adl_despair{}(Sorter{}, iterable, + decltype(detail::adl_despair{}(this->get(), iterable, std::less<>{}, std::move(projection))) > > { - return detail::adl_despair{}(Sorter{}, iterable, + return detail::adl_despair{}(this->get(), iterable, std::less<>{}, std::move(projection)); } @@ -238,13 +246,13 @@ namespace cppsort conditional_t< Stability, std::false_type, - decltype(detail::adl_despair{}(Sorter{}, iterable, + decltype(detail::adl_despair{}(this->get(), iterable, detail::make_projection_compare(std::less<>{}, std::move(projection)))) > > { - return detail::adl_despair{}(Sorter{}, iterable, + return detail::adl_despair{}(this->get(), iterable, detail::make_projection_compare(std::less<>{}, std::move(projection))); } @@ -267,11 +275,11 @@ namespace cppsort conditional_t< Stability, cppsort::is_stable, - decltype(cppsort::sort(Sorter{}, iterable, std::move(projection))) + decltype(cppsort::sort(this->get(), iterable, std::move(projection))) > > { - return cppsort::sort(Sorter{}, iterable, std::move(projection)); + return cppsort::sort(this->get(), iterable, std::move(projection)); } template< @@ -286,12 +294,12 @@ namespace cppsort conditional_t< Stability, std::false_type, - decltype(detail::adl_despair{}(Sorter{}, iterable, + decltype(detail::adl_despair{}(this->get(), iterable, std::move(compare), std::move(projection))) > > { - return detail::adl_despair{}(Sorter{}, iterable, + return detail::adl_despair{}(this->get(), iterable, std::move(compare), std::move(projection)); } @@ -312,13 +320,13 @@ namespace cppsort conditional_t< Stability, std::false_type, - decltype(detail::adl_despair{}(Sorter{}, iterable, + decltype(detail::adl_despair{}(this->get(), iterable, detail::make_projection_compare(std::move(compare), std::move(projection)))) > > { - return detail::adl_despair{}(Sorter{}, iterable, + return detail::adl_despair{}(this->get(), iterable, detail::make_projection_compare(std::move(compare), std::move(projection))); } @@ -340,12 +348,12 @@ namespace cppsort conditional_t< Stability, cppsort::is_stable, - decltype(cppsort::sort(Sorter{}, iterable, + decltype(cppsort::sort(this->get(), iterable, std::move(compare), std::move(projection))) > > { - return cppsort::sort(Sorter{}, iterable, + return cppsort::sort(this->get(), iterable, std::move(compare), std::move(projection)); } }; @@ -361,8 +369,9 @@ namespace cppsort { container_aware_adapter() = default; - // Automatic deduction guide - constexpr explicit container_aware_adapter(Sorter) noexcept {} + constexpr explicit container_aware_adapter(Sorter sorter): + detail::container_aware_adapter_base(std::move(sorter)) + {} }; //////////////////////////////////////////////////////////// From ff090a5fb0ae8fa854bc640824d3427474cbbb59 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 22 Jun 2019 19:53:30 +0200 Subject: [PATCH 15/33] Fix hybrid_adapter construction --- include/cpp-sort/adapters/hybrid_adapter.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/cpp-sort/adapters/hybrid_adapter.h b/include/cpp-sort/adapters/hybrid_adapter.h index 8ba117d7..b492f037 100644 --- a/include/cpp-sort/adapters/hybrid_adapter.h +++ b/include/cpp-sort/adapters/hybrid_adapter.h @@ -319,7 +319,18 @@ namespace cppsort hybrid_adapter, detail::hybrid_adapter_impl<> >::type - {}; + { + hybrid_adapter() = default; + + template + hybrid_adapter(Args&&... args): + detail::flatten_fold< + hybrid_adapter, + hybrid_adapter, + detail::hybrid_adapter_impl<> + >::type(std::forward(args)...) + {} + }; //////////////////////////////////////////////////////////// // is_stable specializations From 2a45f355ab06fecfd3a66cf7e54516e41c3eec54 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 22 Jun 2019 20:12:48 +0200 Subject: [PATCH 16/33] Try to fix hybrid_adapter on Clang --- include/cpp-sort/adapters/hybrid_adapter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cpp-sort/adapters/hybrid_adapter.h b/include/cpp-sort/adapters/hybrid_adapter.h index b492f037..0fc4802f 100644 --- a/include/cpp-sort/adapters/hybrid_adapter.h +++ b/include/cpp-sort/adapters/hybrid_adapter.h @@ -325,7 +325,7 @@ namespace cppsort template hybrid_adapter(Args&&... args): detail::flatten_fold< - hybrid_adapter, + ::cppsort::hybrid_adapter, hybrid_adapter, detail::hybrid_adapter_impl<> >::type(std::forward(args)...) From af2687314b6d624dd7437091ac88e5a422febe01 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 22 Jun 2019 21:41:42 +0200 Subject: [PATCH 17/33] Add tests for stateful sorters --- testsuite/CMakeLists.txt | 1 + .../every_adapter_stateful_sorter.cpp | 220 ++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 testsuite/adapters/every_adapter_stateful_sorter.cpp diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index f9d67e3f..0f4954a1 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -21,6 +21,7 @@ set( adapters/counting_adapter.cpp adapters/every_adapter_fptr.cpp adapters/every_adapter_internal_compare.cpp + adapters/every_adapter_stateful_sorter.cpp adapters/hybrid_adapter_is_stable.cpp adapters/hybrid_adapter_many_sorters.cpp adapters/hybrid_adapter_nested.cpp diff --git a/testsuite/adapters/every_adapter_stateful_sorter.cpp b/testsuite/adapters/every_adapter_stateful_sorter.cpp new file mode 100644 index 00000000..264bba9c --- /dev/null +++ b/testsuite/adapters/every_adapter_stateful_sorter.cpp @@ -0,0 +1,220 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Morwenn + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "../distributions.h" + +namespace +{ + template + struct stateful_sorter_impl + { + int dummy = 0; + + stateful_sorter_impl() = default; + + constexpr stateful_sorter_impl(int a): + dummy(a) + {} + + template< + typename Iterator, + typename Compare = std::less<>, + typename = std::enable_if_t< + not cppsort::is_projection_iterator_v + > + > + auto operator()(Iterator first, Iterator last, Compare compare={}) const + -> int + { + cppsort::quick_merge_sort(std::move(first), std::move(last), std::move(compare)); + if (std::is_same::value) { + return 1; + } else if (std::is_same::value) { + return 2; + } else { + return 3; + } + } + + using iterator_category = IteratorCategory; + using is_always_stable = std::false_type; + }; + + template + struct stateful_sorter: + cppsort::sorter_facade> + { + stateful_sorter() = default; + stateful_sorter(int a): + cppsort::sorter_facade>(a) + {} + }; +} + +TEST_CASE( "test stateful sorters with every adapter", + "[adapters][adapter_storage]" ) +{ + auto distribution = dist::shuffled{}; + + // Collections to sort + std::vector collection; collection.reserve(15); + distribution(std::back_inserter(collection), 15, 0); + std::list li; + distribution(std::back_inserter(li), 15, 0); + std::forward_list fli; + distribution(std::front_inserter(fli), 15, 0); + + SECTION( "container_aware_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::container_aware_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + + sort_it(li, std::greater<>{}); + CHECK( std::is_sorted(std::begin(li), std::end(li), std::greater<>{}) ); + } + + SECTION( "counting_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::counting_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + } + + SECTION( "hybrid_adapter" ) + { + stateful_sorter sorter1(41); + stateful_sorter sorter2(42); + stateful_sorter sorter3(43); + cppsort::hybrid_adapter< + stateful_sorter, + stateful_sorter, + stateful_sorter + > sort_it(sorter1, sorter2, sorter3); + + int res1 = sort_it(fli, std::greater<>{}); + CHECK( res1 == 1 ); + CHECK( std::is_sorted(std::begin(fli), std::end(fli), std::greater<>{}) ); + + int res2 = sort_it(li, std::greater<>{}); + CHECK( res2 == 2 ); + CHECK( std::is_sorted(std::begin(li), std::end(li), std::greater<>{}) ); + + int res3 = sort_it(collection, std::greater<>{}); + CHECK( res3 == 3 ); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + } + + SECTION( "indirect_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::indirect_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + } + + SECTION( "out_of_place_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::out_of_place_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + + sort_it(li, std::greater<>{}); + CHECK( std::is_sorted(std::begin(li), std::end(li), std::greater<>{}) ); + + sort_it(fli, std::greater<>{}); + CHECK( std::is_sorted(std::begin(fli), std::end(fli), std::greater<>{}) ); + } + + SECTION( "schwartz_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::schwartz_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + } + + SECTION( "self_sort_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::self_sort_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + + sort_it(li, std::greater<>{}); + CHECK( std::is_sorted(std::begin(li), std::end(li), std::greater<>{}) ); + } + + SECTION( "stable_adapter" ) + { + stateful_sorter<> sorter(42); + auto sort_it = cppsort::stable_adapter< + cppsort::self_sort_adapter> + >(cppsort::self_sort_adapter>(sorter)); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + + sort_it(li, std::greater<>{}); + CHECK( std::is_sorted(std::begin(li), std::end(li), std::greater<>{}) ); + } + + SECTION( "stable_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::stable_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + } + + SECTION( "verge_adapter" ) + { + stateful_sorter<> sorter(42); + cppsort::verge_adapter> sort_it(sorter); + + sort_it(collection, std::greater<>{}); + CHECK( std::is_sorted(std::begin(collection), std::end(collection), std::greater<>{}) ); + } +} From ad35b065d33ed7a95446c0fcaa13cc712666275f Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 22 Jun 2019 22:49:02 +0200 Subject: [PATCH 18/33] Simplify stable_adapter --- .../detail/stable_adapter_self_sort_adapter.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h b/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h index d1404e1e..3d8fcce0 100644 --- a/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h +++ b/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h @@ -41,7 +41,7 @@ namespace cppsort { template struct stable_adapter>: - utility::adapter_storage, + utility::adapter_storage>, detail::check_iterator_category, detail::sorter_facade_fptr< stable_adapter>, @@ -54,7 +54,9 @@ namespace cppsort stable_adapter() = default; constexpr explicit stable_adapter(self_sort_adapter sorter): - utility::adapter_storage(std::move(sorter.get())) + utility::adapter_storage>( + stable_adapter(std::move(sorter.get())) + ) {} //////////////////////////////////////////////////////////// @@ -74,19 +76,17 @@ namespace cppsort auto operator()(Iterable&& iterable, Args&&... args) const -> std::enable_if_t< not detail::has_stable_sort_method, - decltype(stable_adapter(this->get())(std::forward(iterable), - std::forward(args)...)) + decltype(this->get()(std::forward(iterable), std::forward(args)...)) > { - return stable_adapter(this->get())(std::forward(iterable), - std::forward(args)...); + return this->get()(std::forward(iterable), std::forward(args)...); } template auto operator()(Iterator first, Iterator last, Args&&... args) const - -> decltype(stable_adapter(this->get())(first, last, std::forward(args)...)) + -> decltype(this->get()(first, last, std::forward(args)...)) { - return stable_adapter(this->get())(first, last, std::forward(args)...); + return this->get()(first, last, std::forward(args)...); } //////////////////////////////////////////////////////////// From 51b9c742d7ec02c15709d5122c229b6e774df6e4 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 24 Jun 2019 21:49:25 +0200 Subject: [PATCH 19/33] Start cleaning grail_sort code a bit --- include/cpp-sort/detail/grail_sort.h | 369 ++++++++++++------------ include/cpp-sort/sorters/grail_sorter.h | 6 +- 2 files changed, 188 insertions(+), 187 deletions(-) diff --git a/include/cpp-sort/detail/grail_sort.h b/include/cpp-sort/detail/grail_sort.h index 7c023b13..347ac3f6 100644 --- a/include/cpp-sort/detail/grail_sort.h +++ b/include/cpp-sort/detail/grail_sort.h @@ -4,7 +4,7 @@ * (c) 2013 by Andrey Astrelin * Modified in 2015-2019 by Morwenn for inclusion into cpp-sort * - * Stable sorting that works in O(N*log(N)) worst time + * Stable sorting that works in O(N*log(N)) time * and uses O(1) extra memory * */ @@ -34,11 +34,13 @@ namespace cppsort { namespace detail +{ +namespace grail { // cost: 2*len+nk^2/2 template - auto grail_FindKeys(RandomAccessIterator first, RandomAccessIterator last, - int nkeys, Compare compare, Projection projection) + auto find_keys(RandomAccessIterator first, RandomAccessIterator last, + int nkeys, Compare compare, Projection projection) -> int { auto&& proj = utility::as_function(projection); @@ -61,9 +63,9 @@ namespace detail // cost: min(L1,L2)^2+max(L1,L2) template - auto grail_MergeWithoutBuffer(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, - Compare compare, Projection projection) + auto merge_without_buffer(RandomAccessIterator first, RandomAccessIterator middle, + RandomAccessIterator last, + Compare compare, Projection projection) -> void { auto&& proj = utility::as_function(projection); @@ -101,9 +103,9 @@ namespace detail // arr[M..-1] - buffer, arr[0,L1-1]++arr[L1,L1+L2-1] -> arr[M,M+L1+L2-1] template - auto grail_MergeLeft(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, RandomAccessIterator M, - Compare compare, Projection projection) + auto merge_left(RandomAccessIterator first, RandomAccessIterator middle, + RandomAccessIterator last, RandomAccessIterator M, + Compare compare, Projection projection) -> void { using utility::iter_swap; @@ -128,9 +130,9 @@ namespace detail } template - auto grail_MergeRight(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, RandomAccessIterator M, - Compare compare, Projection projection) + auto merge_right(RandomAccessIterator first, RandomAccessIterator middle, + RandomAccessIterator last, RandomAccessIterator M, + Compare compare, Projection projection) -> void { using utility::iter_swap; @@ -160,9 +162,9 @@ namespace detail } template - auto grail_SmartMergeWithBuffer(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, int lkeys, - int atype, Compare compare, Projection projection) + auto smart_merge_with_buffer(RandomAccessIterator first, RandomAccessIterator middle, + RandomAccessIterator last, int lkeys, + int atype, Compare compare, Projection projection) -> std::pair { using utility::iter_swap; @@ -200,9 +202,9 @@ namespace detail } template - auto grail_SmartMergeWithoutBuffer(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, int atype, - Compare compare, Projection projection) + auto smart_merge_without_buffer(RandomAccessIterator first, RandomAccessIterator middle, + RandomAccessIterator last, int atype, + Compare compare, Projection projection) -> std::pair { auto&& proj = utility::as_function(projection); @@ -235,9 +237,9 @@ namespace detail // Sort With Extra Buffer template - auto grail_MergeLeftWithXBuf(ForwardIterator first, ForwardIterator middle, - ForwardIterator last, ForwardIterator out, - Compare compare, Projection projection) + auto merge_left_with_extra_buffer(ForwardIterator first, ForwardIterator middle, + ForwardIterator last, ForwardIterator out, + Compare compare, Projection projection) -> void { using utility::iter_move; @@ -259,9 +261,9 @@ namespace detail } template - auto grail_SmartMergeWithXBuf(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, int atype, int lkeys, - Compare compare, Projection projection) + auto smart_merge_with_extra_buffer(RandomAccessIterator first, RandomAccessIterator middle, + RandomAccessIterator last, int atype, int lkeys, + Compare compare, Projection projection) -> std::pair { using utility::iter_move; @@ -304,37 +306,36 @@ namespace detail // nblock2 are regular blocks from stream A. llast is length of last (irregular) block from stream B, that should go before nblock2 blocks. // llast=0 requires nblock2=0 (no irregular blocks). llast>0, nblock2=0 is possible. template - auto grail_MergeBuffersLeftWithXBuf(RandomAccessIterator keys, RandomAccessIterator midkey, - RandomAccessIterator arr, int nblock, int lblock, int nblock2, - int llast, Compare compare, Projection projection) + auto merge_buffers_left_with_extra_buffer(RandomAccessIterator keys, RandomAccessIterator midkey, + RandomAccessIterator arr, int nblock, int lblock, int nblock2, + int llast, Compare compare, Projection projection) -> void { auto&& proj = utility::as_function(projection); if (nblock == 0) { - RandomAccessIterator l = arr + nblock2 * lblock; - grail_MergeLeftWithXBuf(arr, l, l+llast, arr-lblock, compare, projection); + auto l = arr + nblock2 * lblock; + merge_left_with_extra_buffer(arr, l, l+llast, arr-lblock, compare, projection); return; } int lrest = lblock; int frest = compare(proj(*keys), proj(*midkey)) < 0 ? 0 : 1; - RandomAccessIterator prest; - RandomAccessIterator pidx = arr + lblock; + auto pidx = arr + lblock; for (int cidx = 1 ; cidx < nblock ; (void) ++cidx, pidx += lblock) { - prest = pidx - lrest; + auto prest = pidx - lrest; int fnext = compare(proj(keys[cidx]), proj(*midkey)) < 0 ? 0 : 1; if (fnext == frest) { detail::move(prest, prest + lrest, prest - lblock); prest = pidx; lrest = lblock; } else { - std::tie(lrest, frest) = grail_SmartMergeWithXBuf(prest, prest+lrest, prest+(lrest+lblock), - frest, lblock, - compare, projection); + std::tie(lrest, frest) = smart_merge_with_extra_buffer( + prest, prest + lrest, prest + (lrest + lblock), + frest, lblock, compare, projection); } } - prest = pidx - lrest; + auto prest = pidx - lrest; if (llast) { if (frest) { detail::move(prest, prest + lrest, prest - lblock); @@ -343,8 +344,8 @@ namespace detail } else { lrest += lblock * nblock2; } - grail_MergeLeftWithXBuf(prest, prest+lrest, prest+(lrest+llast), - prest-lblock, std::move(compare), std::move(projection)); + merge_left_with_extra_buffer(prest, prest + lrest, prest + (lrest + llast), prest - lblock, + std::move(compare), std::move(projection)); } else { detail::move(prest, prest + lrest, prest - lblock); } @@ -352,14 +353,90 @@ namespace detail /***** End Sort With Extra Buffer *****/ + // arr - starting array. arr[-lblock..-1] - buffer (if havebuf). + // lblock - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded + // keys - arrays of keys, in same order as blocks. key0, nblock2=0 is possible. + template + auto merge_buffers_left(RandomAccessIterator keys, RandomAccessIterator midkey, + RandomAccessIterator arr, int nblock, int lblock, bool havebuf, + int nblock2, int llast, Compare compare, Projection projection) + -> void + { + auto&& proj = utility::as_function(projection); + auto&& midkey_proj = proj(*midkey); + + if (nblock == 0) { + auto l = arr + nblock2 * lblock; + if (havebuf) { + merge_left(arr, l, l+llast, arr-lblock, std::move(compare), std::move(projection)); + } else { + merge_without_buffer(arr, l, l + llast, std::move(compare), std::move(projection)); + } + return; + } + + int lrest = lblock; + int frest = compare(proj(*keys), midkey_proj) < 0 ? 0 : 1; + auto pidx = arr + lblock; + for (int cidx = 1 ; cidx < nblock ; (void) ++cidx, pidx += lblock) { + auto prest = pidx - lrest; + int fnext = compare(proj(keys[cidx]), midkey_proj) < 0 ? 0 : 1; + if(fnext == frest) { + if (havebuf) { + detail::swap_ranges(prest-lblock, prest-(lblock-lrest), prest); + } + prest = pidx; + lrest = lblock; + } else { + if (havebuf) { + std::tie(lrest, frest) = smart_merge_with_buffer( + prest, prest + lrest, prest + (lrest + lblock), + lblock, frest, compare, projection); + } else { + std::tie(lrest, frest) = smart_merge_without_buffer( + prest, prest + lrest, prest + (lrest + lblock), + frest, compare, projection); + } + + } + } + + auto prest = pidx - lrest; + if (llast) { + if (frest) { + if (havebuf) { + detail::swap_ranges(prest-lblock, prest-(lblock-lrest), prest); + } + prest = pidx; + lrest = lblock * nblock2; + } else { + lrest += lblock * nblock2; + } + if (havebuf) { + merge_left(prest, prest+lrest, prest+(lrest+llast), prest-lblock, + std::move(compare), std::move(projection)); + } else { + merge_without_buffer(prest, prest + lrest, prest + (lrest + llast), + std::move(compare), std::move(projection)); + } + } else { + if (havebuf) { + detail::swap_ranges(prest, prest+lrest, prest-lblock); + } + } + } + // build blocks of length K // input: [-K,-1] elements are buffer // output: first K elements are buffer, blocks 2*K and last subblock sorted template - auto grail_BuildBlocks(RandomAccessIterator first, RandomAccessIterator last, - int K, BufferIterator extbuf, int LExtBuf, - Compare compare, Projection projection) + auto build_blocks(RandomAccessIterator first, RandomAccessIterator last, + int K, BufferIterator extbuf, int LExtBuf, + Compare compare, Projection projection) -> void { using utility::iter_move; @@ -389,15 +466,15 @@ namespace detail first -= 2; last -= 2; for (h = 2 ; h < kbuf ; h *= 2) { - RandomAccessIterator p0 = first; - RandomAccessIterator p1 = last - 2 * h; + auto p0 = first; + auto p1 = last - 2 * h; while (p0 <= p1) { - grail_MergeLeftWithXBuf(p0, p0+h, p0+(h+h), p0-h, compare, projection); + merge_left_with_extra_buffer(p0, p0+h, p0+(h+h), p0-h, compare, projection); p0 += 2 * h; } int rest = std::distance(p0, last); if (rest > h) { - grail_MergeLeftWithXBuf(p0, p0+h, last, p0-h, compare, projection); + merge_left_with_extra_buffer(p0, p0+h, last, p0-h, compare, projection); } else { for (; p0 < last ; ++p0) { p0[-h] = iter_move(p0); @@ -413,8 +490,8 @@ namespace detail if (compare(proj(first[m-1]), proj(first[m])) > 0) { u = 1; } - iter_swap(first+(m-3), first+(m-1+u)); - iter_swap(first+(m-2), first+(m-u)); + iter_swap(first + (m-3), first + (m-1+u)); + iter_swap(first + (m-2), first + (m-u)); } if (size % 2) { iter_swap(last - 1, last - 3); @@ -424,15 +501,15 @@ namespace detail h = 2; } for (; h < K ; h *= 2) { - RandomAccessIterator p0 = first; - RandomAccessIterator p1 = last - 2 * h; + auto p0 = first; + auto p1 = last - 2 * h; while (p0 <= p1) { - grail_MergeLeft(p0, p0+h, p0+(h+h), p0-h, compare, projection); + merge_left(p0, p0+h, p0+(h+h), p0-h, compare, projection); p0 += 2 * h; } int rest = std::distance(p0, last); if (rest > h) { - grail_MergeLeft(p0, p0+h, last, p0-h, compare, projection); + merge_left(p0, p0+h, last, p0-h, compare, projection); } else { detail::rotate(p0-h, p0, last); } @@ -440,121 +517,15 @@ namespace detail last -= h; } int restk = size % (2 * K); - RandomAccessIterator p = last - restk; + auto p = last - restk; if (restk <= K) { detail::rotate(p, last, last+K); } else { - grail_MergeRight(p, p+K, last, last+K, compare, projection); + merge_right(p, p+K, last, last+K, compare, projection); } while (p > first) { p -= 2 * K; - grail_MergeRight(p, p+K, p+(K+K), p+(K+K+K), compare, projection); - } - } - - // arr - starting array. arr[-lblock..-1] - buffer (if havebuf). - // lblock - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded - // keys - arrays of keys, in same order as blocks. key0, nblock2=0 is possible. - template - auto grail_MergeBuffersLeft(RandomAccessIterator keys, RandomAccessIterator midkey, - RandomAccessIterator arr, int nblock, int lblock, bool havebuf, - int nblock2, int llast, Compare compare, Projection projection) - -> void - { - auto&& proj = utility::as_function(projection); - auto&& midkey_proj = proj(*midkey); - - if (nblock == 0) { - RandomAccessIterator l = arr + nblock2 * lblock; - if (havebuf) { - grail_MergeLeft(arr, l, l+llast, arr-lblock, - std::move(compare), std::move(projection)); - } else { - grail_MergeWithoutBuffer(arr, l, l+llast, - std::move(compare), std::move(projection)); - } - return; - } - - int lrest = lblock; - int frest = compare(proj(*keys), midkey_proj) < 0 ? 0 : 1; - RandomAccessIterator prest; - RandomAccessIterator pidx = arr + lblock; - for (int cidx = 1 ; cidx < nblock ; (void) ++cidx, pidx += lblock) { - prest = pidx - lrest; - int fnext = compare(proj(keys[cidx]), midkey_proj) < 0 ? 0 : 1; - if(fnext == frest) { - if (havebuf) { - detail::swap_ranges(prest-lblock, prest-(lblock-lrest), prest); - } - prest = pidx; - lrest = lblock; - } else { - if (havebuf) { - std::tie(lrest, frest) = grail_SmartMergeWithBuffer(prest, prest+lrest, prest+(lrest+lblock), - lblock, frest, - compare, projection); - } else { - std::tie(lrest, frest) = grail_SmartMergeWithoutBuffer(prest, prest+lrest, prest+(lrest+lblock), - frest, compare, projection); - } - - } - } - - prest = pidx - lrest; - if (llast) { - if (frest) { - if (havebuf) { - detail::swap_ranges(prest-lblock, prest-(lblock-lrest), prest); - } - prest = pidx; - lrest = lblock * nblock2; - } else { - lrest += lblock * nblock2; - } - if (havebuf) { - grail_MergeLeft(prest, prest+lrest, prest+(lrest+llast), prest-lblock, - std::move(compare), std::move(projection)); - } else { - grail_MergeWithoutBuffer(prest, prest+lrest, prest+(lrest+llast), - std::move(compare), std::move(projection)); - } - } else { - if (havebuf) { - detail::swap_ranges(prest, prest+lrest, prest-lblock); - } - } - } - - template - auto grail_LazyStableSort(RandomAccessIterator first, RandomAccessIterator last, - Compare compare, Projection projection) - -> void - { - using utility::iter_swap; - auto&& proj = utility::as_function(projection); - - for (auto it = std::next(first) ; it < last ; it += 2) { - if (compare(proj(*std::prev(it)), proj(*it)) > 0) { - iter_swap(std::prev(it), it); - } - } - - auto size = std::distance(first, last); - for (int h = 2 ; h < size ; h *= 2) { - RandomAccessIterator p0 = first; - RandomAccessIterator p1 = last - 2 * h; - while (p0 <= p1) { - grail_MergeWithoutBuffer(p0, p0+h, p0+(h+h), compare, projection); - p0 += 2 * h; - } - int rest = std::distance(p0, last); - if (rest > h) { - grail_MergeWithoutBuffer(p0, p0+h, last, compare, projection); - } + merge_right(p, p+K, p+(K+K), p+(K+K+K), compare, projection); } } @@ -562,9 +533,9 @@ namespace detail // LL and nkeys are powers of 2. (2*LL/lblock) keys are guarantied template - auto grail_CombineBlocks(RandomAccessIterator keys, RandomAccessIterator arr, int len, int LL, - int lblock, bool havebuf, BufferIterator xbuf, bool usexbuf, - Compare compare, Projection projection) + auto combine_blocks(RandomAccessIterator keys, RandomAccessIterator arr, int len, int LL, + int lblock, bool havebuf, BufferIterator xbuf, bool usexbuf, + Compare compare, Projection projection) -> void { using utility::iter_move; @@ -582,7 +553,7 @@ namespace detail } for (int b = 0 ; b <= M ; ++b) { if (b == M && lrest == 0) break; - RandomAccessIterator arr1 = arr + b * 2 * LL; + auto arr1 = arr + b * 2 * LL; int NBlk = (b == M ? lrest : 2 * LL) / lblock; insertion_sort(keys, keys + (NBlk + (b == M ? 1 : 0)), compare.base(), projection); @@ -615,9 +586,11 @@ namespace detail } } if (usexbuf) { - grail_MergeBuffersLeftWithXBuf(keys,keys+midkey,arr1,NBlk-nbl2,lblock,nbl2,llast,compare,projection); + merge_buffers_left_with_extra_buffer(keys, keys + midkey, arr1, NBlk - nbl2, lblock, nbl2, llast, + compare, projection); } else { - grail_MergeBuffersLeft(keys,keys+midkey,arr1,NBlk-nbl2,lblock,havebuf,nbl2,llast,compare,projection); + merge_buffers_left(keys, keys + midkey, arr1, NBlk - nbl2, lblock, havebuf, nbl2, llast, + compare, projection); } } if (usexbuf) { @@ -632,16 +605,45 @@ namespace detail } } + template + auto lazy_stable_sort(RandomAccessIterator first, RandomAccessIterator last, + Compare compare, Projection projection) + -> void + { + using utility::iter_swap; + auto&& proj = utility::as_function(projection); + + for (auto it = std::next(first) ; it < last ; it += 2) { + if (compare(proj(*std::prev(it)), proj(*it)) > 0) { + iter_swap(std::prev(it), it); + } + } + + auto size = std::distance(first, last); + for (int h = 2 ; h < size ; h *= 2) { + auto p0 = first; + auto p1 = last - 2 * h; + while (p0 <= p1) { + merge_without_buffer(p0, p0 + h, p0 + (h + h), compare, projection); + p0 += 2 * h; + } + int rest = std::distance(p0, last); + if (rest > h) { + merge_without_buffer(p0, p0 + h, last, compare, projection); + } + } + } + template - auto grail_commonSort(RandomAccessIterator first, RandomAccessIterator last, - BufferIterator extbuf, int LExtBuf, - Compare compare, Projection projection) + auto common_sort(RandomAccessIterator first, RandomAccessIterator last, + BufferIterator extbuf, int LExtBuf, + Compare compare, Projection projection) -> void { auto size = std::distance(first, last); if (size < 16) { - insertion_sort(first, last, compare.base(), projection); + insertion_sort(first, last, compare.base(), std::move(projection)); return; } @@ -650,11 +652,11 @@ namespace detail lblock *= 2; } int nkeys = (size - 1) / lblock + 1; - int findkeys = grail_FindKeys(first, last, nkeys + lblock, compare, projection); + int findkeys = find_keys(first, last, nkeys + lblock, compare, projection); bool havebuf = true; if (findkeys < nkeys + lblock) { if (findkeys < 4) { - grail_LazyStableSort(first, last, compare, projection); + lazy_stable_sort(first, last, std::move(compare), std::move(projection)); return; } nkeys = lblock; @@ -664,13 +666,13 @@ namespace detail havebuf = false; lblock = 0; } - RandomAccessIterator ptr = first + (lblock + nkeys); + auto ptr = first + (lblock + nkeys); int cbuf = havebuf ? lblock : nkeys; if (havebuf) { - grail_BuildBlocks(ptr, last, cbuf, extbuf, LExtBuf, compare, projection); + build_blocks(ptr, last, cbuf, extbuf, LExtBuf, compare, projection); } else { using T = value_type_t; - grail_BuildBlocks(ptr, last, cbuf, static_cast(nullptr), 0, compare, projection); + build_blocks(ptr, last, cbuf, static_cast(nullptr), 0, compare, projection); } // 2*cbuf are built @@ -692,13 +694,12 @@ namespace detail } } - grail_CombineBlocks(first, ptr, std::distance(ptr, last), cbuf, lb, - chavebuf, extbuf, chavebuf && lb <= LExtBuf, - compare, projection); + combine_blocks(first, ptr, std::distance(ptr, last), cbuf, lb, + chavebuf, extbuf, chavebuf && lb <= LExtBuf, + compare, projection); } insertion_sort(first, ptr, compare.base(), projection); - grail_MergeWithoutBuffer(first, ptr, last, - std::move(compare), std::move(projection)); + merge_without_buffer(first, ptr, last, std::move(compare), std::move(projection)); } template buffer(size); using compare_t = std::remove_reference_t; - grail_commonSort(std::move(first), std::move(last), buffer.begin(), buffer.size(), - three_way_compare(utility::as_function(compare)), - std::move(projection)); + common_sort(std::move(first), std::move(last), buffer.begin(), buffer.size(), + three_way_compare(utility::as_function(compare)), + std::move(projection)); } -}} +}}} #endif // CPPSORT_DETAIL_GRAIL_SORT_H_ diff --git a/include/cpp-sort/sorters/grail_sorter.h b/include/cpp-sort/sorters/grail_sorter.h index abdb75e1..e2592181 100644 --- a/include/cpp-sort/sorters/grail_sorter.h +++ b/include/cpp-sort/sorters/grail_sorter.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -68,8 +68,8 @@ namespace cppsort "grail_sorter requires at least random-access iterators" ); - grail_sort(std::move(first), std::move(last), - std::move(compare), std::move(projection)); + grail::grail_sort(std::move(first), std::move(last), + std::move(compare), std::move(projection)); } //////////////////////////////////////////////////////////// From 1bf761ace321f020ec8ccc2bf3b1da72c6760440 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Thu, 4 Jul 2019 00:17:14 +0200 Subject: [PATCH 20/33] More grail_sort cleanup (still WIP) --- include/cpp-sort/detail/grail_sort.h | 129 ++++++++++++++------------- 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/include/cpp-sort/detail/grail_sort.h b/include/cpp-sort/detail/grail_sort.h index 347ac3f6..0228f97c 100644 --- a/include/cpp-sort/detail/grail_sort.h +++ b/include/cpp-sort/detail/grail_sort.h @@ -37,31 +37,31 @@ namespace detail { namespace grail { - // cost: 2*len+nk^2/2 + // cost: 2 * len + nk^2 / 2 template auto find_keys(RandomAccessIterator first, RandomAccessIterator last, - int nkeys, Compare compare, Projection projection) - -> int + difference_type_t key_count, + Compare compare, Projection projection) + -> difference_type_t { auto&& proj = utility::as_function(projection); - int h = 1; - RandomAccessIterator h0 = first; - RandomAccessIterator u = std::next(first); - while (u != last && h < nkeys) { - int r = lower_bound(h0, h0 + h, proj(*u), compare.base(), projection) - h0; - if (r == h || compare(proj(*u), proj(h0[r])) != 0) { - h0 = detail::rotate(h0, h0 + h, u); + difference_type_t dist = 1; + auto h0 = first; + for (auto u = std::next(first) ; u != last ; ++u) { + if (dist == key_count) break; + auto r = lower_bound(h0, h0 + dist, proj(*u), compare.base(), projection) - h0; + if (r == dist || compare(proj(*u), proj(h0[r])) != 0) { + h0 = detail::rotate(h0, h0 + dist, u); detail::rotate(h0 + r, u, std::next(u)); - ++h; + ++dist; } - ++u; } - detail::rotate(first, h0, h0 + h); - return h; + detail::rotate(first, h0, h0 + dist); + return dist; } - // cost: min(L1,L2)^2+max(L1,L2) + // cost: min(L1, L2)^2 + max(L1, L2) template auto merge_without_buffer(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, @@ -72,10 +72,11 @@ namespace grail if (std::distance(first, middle) < std::distance(middle, last)) { while (first != middle) { - auto h = lower_bound(middle, last, proj(*first), compare.base(), projection); - if (h != middle) { - detail::rotate(first, middle, h); - auto delta = std::distance(middle, h); + // Binary search left + auto it = lower_bound(middle, last, proj(*first), compare.base(), projection); + if (it != middle) { + detail::rotate(first, middle, it); + auto delta = std::distance(middle, it); first += delta; middle += delta; } @@ -86,10 +87,11 @@ namespace grail } } else { while (middle != last) { - auto h = upper_bound(first, middle, proj(*std::prev(last)), compare.base(), projection); - if (h != middle) { - detail::rotate(h, middle, last); - auto delta = std::distance(h, middle); + // Binary search right + auto it = upper_bound(first, middle, proj(*std::prev(last)), compare.base(), projection); + if (it != middle) { + detail::rotate(it, middle, last); + auto delta = std::distance(it, middle); middle -= delta; last -= delta; } @@ -111,21 +113,21 @@ namespace grail using utility::iter_swap; auto&& proj = utility::as_function(projection); - RandomAccessIterator p0 = first; - RandomAccessIterator p1 = middle; + auto left_it = first; + auto right_it = middle; - while (p1 < last) { - if (p0 == middle || compare(proj(*p0), proj(*p1)) > 0) { - iter_swap(M, p1); - ++p1; + while (right_it != last) { + if (left_it == middle || compare(proj(*left_it), proj(*right_it)) > 0) { + iter_swap(M, right_it); + ++right_it; } else { - iter_swap(M, p0); - ++p0; + iter_swap(M, left_it); + ++left_it; } ++M; } - if (M != p0) { - detail::swap_ranges(M, M + std::distance(p0, middle), p0); + if (M != left_it) { + detail::swap_ranges(M, M + std::distance(left_it, middle), left_it); } } @@ -138,9 +140,9 @@ namespace grail using utility::iter_swap; auto&& proj = utility::as_function(projection); - RandomAccessIterator p0 = std::prev(M); - RandomAccessIterator p1 = std::prev(middle); - RandomAccessIterator p2 = std::prev(last); + auto p0 = std::prev(M), + p1 = std::prev(middle), + p2 = std::prev(last); while (p1 >= first) { if (p2 < middle || compare(proj(*p1), proj(*p2)) > 0) { @@ -152,33 +154,34 @@ namespace grail } --p0; } - if (p2 != p0) { - while (p2 >= middle) { + + if (p2 != p0 && p2 >= middle) { + do { iter_swap(p0, p2); --p0; --p2; - } + } while (p2 != middle); } } template - auto smart_merge_with_buffer(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, int lkeys, - int atype, Compare compare, Projection projection) - -> std::pair + auto smart_merge_with_buffer(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, + difference_type_t block_len, int left_over_frag, + Compare compare, Projection projection) + -> std::pair, int> { using utility::iter_swap; auto&& proj = utility::as_function(projection); - RandomAccessIterator p0 = first - lkeys, - p1 = first, - p2 = middle, - q1 = p2, - q2 = last; + auto p0 = first - block_len, + p1 = first, + p2 = middle, + q1 = p2, + q2 = last; + int frag_type = 1 - left_over_frag; // 1 if inverted - int ftype = 1 - atype; // 1 if inverted - while (p1 < q1 && p2 < q2) { - if (compare(proj(*p1), proj(*p2)) - ftype < 0) { + while (p1 != q1 && p2 != q2) { + if (compare(proj(*p1), proj(*p2)) - frag_type < 0) { iter_swap(p0, p1); ++p1; } else { @@ -188,7 +191,7 @@ namespace grail ++p0; } - int len; + difference_type_t len; if (p1 < q1) { len = std::distance(p1, q1); do { @@ -196,42 +199,42 @@ namespace grail } while (p1 < q1); } else { len = std::distance(p2, q2); - atype = ftype; + left_over_frag = frag_type; } - return { len, atype }; + return { len, left_over_frag }; } template auto smart_merge_without_buffer(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, int atype, + RandomAccessIterator last, int left_over_frag, Compare compare, Projection projection) -> std::pair { auto&& proj = utility::as_function(projection); if (middle == last) { - return { std::distance(first, middle), atype }; + return { std::distance(first, middle), left_over_frag }; } - int ftype = 1 - atype; - if (first != middle && compare(proj(*std::prev(middle)), proj(*middle)) - ftype >= 0) { + int frag_type = 1 - left_over_frag; + if (first != middle && compare(proj(*std::prev(middle)), proj(*middle)) - frag_type >= 0) { while (first != middle) { - int h = ftype ? (lower_bound(middle, last, proj(*first), compare.base(), projection) - middle) - : (upper_bound(middle, last, proj(*first), compare.base(), projection) - middle); + auto h = frag_type ? (lower_bound(middle, last, proj(*first), compare.base(), projection) - middle) + : (upper_bound(middle, last, proj(*first), compare.base(), projection) - middle); if (h != 0) { detail::rotate(first, middle, middle + h); first += h; middle += h; } if (middle == last) { - return { std::distance(first, middle), atype }; + return { std::distance(first, middle), left_over_frag }; } do { ++first; - } while (first != middle && compare(proj(*first), proj(*middle)) - ftype < 0); + } while (first != middle && compare(proj(*first), proj(*middle)) - frag_type < 0); } } - return { std::distance(middle, last), ftype }; + return { std::distance(middle, last), frag_type }; } // Sort With Extra Buffer From dd6032bdfb0fdb2c92b71eef6cfd6315dd3f78ec Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 7 Jul 2019 22:05:31 +0200 Subject: [PATCH 21/33] More cleanup for grail_sort merge functions --- include/cpp-sort/detail/grail_sort.h | 110 +++++++++++++-------------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/include/cpp-sort/detail/grail_sort.h b/include/cpp-sort/detail/grail_sort.h index 0228f97c..a8468946 100644 --- a/include/cpp-sort/detail/grail_sort.h +++ b/include/cpp-sort/detail/grail_sort.h @@ -173,32 +173,31 @@ namespace grail using utility::iter_swap; auto&& proj = utility::as_function(projection); - auto p0 = first - block_len, - p1 = first, - p2 = middle, - q1 = p2, - q2 = last; + auto out_it = first - block_len, + left_it = first, + right_it = middle; int frag_type = 1 - left_over_frag; // 1 if inverted - while (p1 != q1 && p2 != q2) { - if (compare(proj(*p1), proj(*p2)) - frag_type < 0) { - iter_swap(p0, p1); - ++p1; + while (left_it != middle && right_it != last) { + if (compare(proj(*left_it), proj(*right_it)) - frag_type < 0) { + iter_swap(out_it, left_it); + ++left_it; } else { - iter_swap(p0, p2); - ++p2; + iter_swap(out_it, right_it); + ++right_it; } - ++p0; + ++out_it; } + //auto p1 = left_it, q1 = middle, p2 = right_it, q2 = last; difference_type_t len; - if (p1 < q1) { - len = std::distance(p1, q1); + if (left_it < middle) { + len = std::distance(left_it, middle); do { - iter_swap(--q1, --q2); - } while (p1 < q1); + iter_swap(--middle, --last); + } while (left_it != middle); } else { - len = std::distance(p2, q2); + len = std::distance(right_it, last); left_over_frag = frag_type; } return { len, left_over_frag }; @@ -219,12 +218,12 @@ namespace grail int frag_type = 1 - left_over_frag; if (first != middle && compare(proj(*std::prev(middle)), proj(*middle)) - frag_type >= 0) { while (first != middle) { - auto h = frag_type ? (lower_bound(middle, last, proj(*first), compare.base(), projection) - middle) - : (upper_bound(middle, last, proj(*first), compare.base(), projection) - middle); - if (h != 0) { - detail::rotate(first, middle, middle + h); - first += h; - middle += h; + auto len = frag_type ? (lower_bound(middle, last, proj(*first), compare.base(), projection) - middle) + : (upper_bound(middle, last, proj(*first), compare.base(), projection) - middle); + if (len != 0) { + detail::rotate(first, middle, middle + len); + first += len; + middle += len; } if (middle == last) { return { std::distance(first, middle), left_over_frag }; @@ -248,59 +247,56 @@ namespace grail using utility::iter_move; auto&& proj = utility::as_function(projection); - auto p0 = first, p1 = middle; - while (p1 != last) { - if (p0 == middle || compare(proj(*p0), proj(*p1)) > 0) { - *out = iter_move(p1); - ++out; ++p1; + auto it = middle; + while (it != last) { + if (first == middle || compare(proj(*first), proj(*it)) > 0) { + *out = iter_move(it); + ++out; + ++it; } else { - *out = iter_move(p0); - ++out; ++p0; + *out = iter_move(first); + ++out; + ++first; } } - if (out != p0) { - detail::move(p0, middle, out); + if (out != first) { + detail::move(first, middle, out); } } template auto smart_merge_with_extra_buffer(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, int atype, int lkeys, + RandomAccessIterator last, + int left_over_frag, difference_type_t block_len, Compare compare, Projection projection) -> std::pair { using utility::iter_move; auto&& proj = utility::as_function(projection); - RandomAccessIterator p0 = first - lkeys, - p1 = first, - p2 = middle, - q1 = p2, - q2 = last; - - int ftype = 1 - atype; // 1 if inverted - while (p1 < q1 && p2 < q2) { - if (compare(proj(*p1), proj(*p2)) - ftype < 0) { - *p0 = iter_move(p1); - ++p1; + auto out = first - block_len; + auto it = middle; + + int frag_type = 1 - left_over_frag; // 1 if inverted + while (first < middle && it < last) { + if (compare(proj(*first), proj(*it)) - frag_type < 0) { + *out = iter_move(first); + ++first; } else { - *p0 = iter_move(p2); - ++p2; + *out = iter_move(it); + ++it; } - ++p0; + ++out; } - int len; - if (p1 < q1) { - len = std::distance(p1, q1); - while (p1 < q1) { - *--q2 = iter_move(--q1); - } - } else { - len = std::distance(p2, q2); - atype = ftype; + if (first < middle) { + auto left_over_len = std::distance(first, middle); + do { + *--last = iter_move(--middle); + } while (first != middle); + return { left_over_len, left_over_frag }; } - return { len, atype }; + return { std::distance(it, last), frag_type }; } // arr - starting array. arr[-lblock..-1] - buffer (if havebuf). From b5156b1f381a74df637016ccb29877e1f9d47c1b Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 7 Jul 2019 23:28:34 +0200 Subject: [PATCH 22/33] Add Conan badge to README [ci skip] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cf86f586..86b6f6a8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![Latest Release](https://img.shields.io/badge/release-cpp--sort%2F1.4.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases) +[![Conan Package](https://img.shields.io/badge/conan-1.4.0-blue.svg)](https://bintray.com/morwenn/cpp-sort/cpp-sort%3Amorwenn) [![Build Status](https://travis-ci.org/Morwenn/cpp-sort.svg?branch=master)](https://travis-ci.org/Morwenn/cpp-sort) [![License](http://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org) [![Code Coverage](https://codecov.io/gh/Morwenn/cpp-sort/branch/master/graph/badge.svg)](https://codecov.io/gh/Morwenn/cpp-sort) From 5714594fde84ac541aca83585af228ee9922a560 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 19 Jul 2019 23:41:22 +0200 Subject: [PATCH 23/33] Revamp temporary_buffer Get rid of std::unique_ptr, take advantage of sized deallocation when available. --- include/cpp-sort/detail/memory.h | 57 ++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/include/cpp-sort/detail/memory.h b/include/cpp-sort/detail/memory.h index 82f5e311..edd9afb2 100644 --- a/include/cpp-sort/detail/memory.h +++ b/include/cpp-sort/detail/memory.h @@ -17,7 +17,6 @@ //////////////////////////////////////////////////////////// #include #include -#include #include #include #include "type_traits.h" @@ -88,7 +87,8 @@ namespace detail >; //////////////////////////////////////////////////////////// - // Reimplement get_temporary_buffer because C++20 + // Reimplement the functions get_temporary_buffer and + // return_temporary_buffer because of their removal in C++20 template auto get_temporary_buffer(ptrdiff_t count) noexcept @@ -115,8 +115,20 @@ namespace detail return res; } + template + auto return_temporary_buffer(T* ptr, std::size_t count) noexcept + -> void + { +#ifdef __cpp_sized_deallocation + ::operator delete(ptr, count * sizeof(T)); +#else + (void)count; + ::operator delete(ptr); +#endif + } + //////////////////////////////////////////////////////////// - // Wrapper are std::unique_ptr for temporary buffers + // Thin wrapper around get/return_temporary_buffer template class temporary_buffer @@ -133,33 +145,51 @@ namespace detail // Construction & destruction temporary_buffer() = default; - temporary_buffer(temporary_buffer&&) = default; temporary_buffer(const temporary_buffer&) = delete; + temporary_buffer(temporary_buffer&& other) noexcept: + buffer(other.buffer), + buffer_size(other.buffer_size) + { + other.buffer = nullptr; + other.buffer_size = 0; + } + constexpr temporary_buffer(std::nullptr_t) noexcept {} - explicit temporary_buffer(std::ptrdiff_t count) + explicit temporary_buffer(std::ptrdiff_t count) noexcept { auto tmp = get_temporary_buffer(count); - buffer.reset(tmp.first); + buffer = tmp.first; buffer_size = tmp.second; } - ~temporary_buffer() = default; + ~temporary_buffer() noexcept + { + return_temporary_buffer(buffer, buffer_size); + } //////////////////////////////////////////////////////////// // Assignment operator - temporary_buffer& operator=(temporary_buffer&&) = default; temporary_buffer& operator=(const temporary_buffer&) = delete; + auto operator=(temporary_buffer&& other) noexcept + -> temporary_buffer& + { + using std::swap; + swap(buffer, other.buffer); + swap(buffer_size, other.buffer_size); + return *this; + } + //////////////////////////////////////////////////////////// // Data access auto data() const noexcept -> pointer { - return buffer.get(); + return buffer; } auto size() const noexcept @@ -171,7 +201,7 @@ namespace detail //////////////////////////////////////////////////////////// // Modifiers - auto try_grow(std::ptrdiff_t count) + auto try_grow(std::ptrdiff_t count) noexcept -> bool { if (count <= buffer_size) { @@ -180,18 +210,19 @@ namespace detail auto tmp = get_temporary_buffer(count); if (tmp.second <= buffer_size) { // If the allocated buffer isn't bigger, keep the old one - ::operator delete(tmp.first, std::nothrow); + return_temporary_buffer(tmp.first, tmp.second); return false; } // If the allocated buffer is big enough, replace the previous one - buffer.reset(tmp.first); + return_temporary_buffer(buffer, buffer_size); + buffer = tmp.first; buffer_size = tmp.second; return true; } private: - std::unique_ptr buffer = nullptr; + T* buffer = nullptr; std::ptrdiff_t buffer_size = 0; }; }} From 5fab7183db2d5fb305d6aac73c6c16c135176eb9 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 20 Jul 2019 14:31:16 +0200 Subject: [PATCH 24/33] More optional support for size deallocation --- .../cpp-sort/adapters/out_of_place_adapter.h | 3 ++- include/cpp-sort/adapters/schwartz_adapter.h | 3 ++- include/cpp-sort/adapters/stable_adapter.h | 3 ++- include/cpp-sort/detail/memory.h | 20 ++++++++++++++++++- .../cpp-sort/detail/merge_insertion_sort.h | 5 +++-- include/cpp-sort/detail/timsort.h | 4 +++- 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/include/cpp-sort/adapters/out_of_place_adapter.h b/include/cpp-sort/adapters/out_of_place_adapter.h index 2b713039..d4f5caaa 100644 --- a/include/cpp-sort/adapters/out_of_place_adapter.h +++ b/include/cpp-sort/adapters/out_of_place_adapter.h @@ -55,7 +55,8 @@ namespace cppsort // Copy the collection into contiguous memory buffer std::unique_ptr buffer( - static_cast(::operator new(size * sizeof(rvalue_reference))) + static_cast(::operator new(size * sizeof(rvalue_reference))), + operator_deleter(size * sizeof(rvalue_reference)) ); destruct_n d(0); std::unique_ptr&> h2(buffer.get(), d); diff --git a/include/cpp-sort/adapters/schwartz_adapter.h b/include/cpp-sort/adapters/schwartz_adapter.h index 294c3aa5..f3da0060 100644 --- a/include/cpp-sort/adapters/schwartz_adapter.h +++ b/include/cpp-sort/adapters/schwartz_adapter.h @@ -86,7 +86,8 @@ namespace cppsort // Collection of projected elements auto size = std::distance(first, last); std::unique_ptr projected( - static_cast(::operator new(size * sizeof(value_t))) + static_cast(::operator new(size * sizeof(value_t))), + operator_deleter(size * sizeof(value_t)) ); destruct_n d(0); std::unique_ptr&> h2(projected.get(), d); diff --git a/include/cpp-sort/adapters/stable_adapter.h b/include/cpp-sort/adapters/stable_adapter.h index 4bc63dce..21e13f1a 100644 --- a/include/cpp-sort/adapters/stable_adapter.h +++ b/include/cpp-sort/adapters/stable_adapter.h @@ -139,7 +139,8 @@ namespace cppsort auto size = std::distance(first, last); std::unique_ptr iterators( - static_cast(::operator new(size * sizeof(value_t))) + static_cast(::operator new(size * sizeof(value_t))), + operator_deleter(size * sizeof(value_t)) ); destruct_n d(0); std::unique_ptr&> h2(iterators.get(), d); diff --git a/include/cpp-sort/detail/memory.h b/include/cpp-sort/detail/memory.h index edd9afb2..39f11675 100644 --- a/include/cpp-sort/detail/memory.h +++ b/include/cpp-sort/detail/memory.h @@ -6,7 +6,7 @@ // This file is dual licensed under the MIT and the University of Illinois Open // Source Licenses. See LICENSE.TXT for details. // -// Modified in 2016-2018 by Morwenn for inclusion into cpp-sort +// Modified in 2016-2019 by Morwenn for inclusion into cpp-sort // //===----------------------------------------------------------------------===// #ifndef CPPSORT_DETAIL_MEMORY_H_ @@ -30,11 +30,29 @@ namespace detail struct operator_deleter { + operator_deleter() = default; + +#ifdef __cpp_sized_deallocation + std::size_t size = 0; + + constexpr explicit operator_deleter(std::size_t size) noexcept: + size(size) + {} + + inline auto operator()(void* pointer) const noexcept + -> void + { + ::operator delete(pointer, size); + } +#else + constexpr explicit operator_deleter(std::size_t) noexcept {} + inline auto operator()(void* pointer) const noexcept -> void { ::operator delete(pointer); } +#endif }; //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/detail/merge_insertion_sort.h b/include/cpp-sort/detail/merge_insertion_sort.h index 8fc230cd..59d298f4 100644 --- a/include/cpp-sort/detail/merge_insertion_sort.h +++ b/include/cpp-sort/detail/merge_insertion_sort.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -431,7 +431,8 @@ namespace detail using rvalue_reference = remove_cvref_t>; std::unique_ptr cache( - static_cast(::operator new(full_size * sizeof(rvalue_reference))) + static_cast(::operator new(full_size * sizeof(rvalue_reference))), + operator_deleter(full_size * sizeof(rvalue_reference)) ); destruct_n d(0); std::unique_ptr&> h2(cache.get(), d); diff --git a/include/cpp-sort/detail/timsort.h b/include/cpp-sort/detail/timsort.h index 82da7841..b18d9f1c 100644 --- a/include/cpp-sort/detail/timsort.h +++ b/include/cpp-sort/detail/timsort.h @@ -407,8 +407,10 @@ namespace detail // Resize the merge buffer if the old one isn't big enough if (buffer_size < new_size) { // Release memory first, then allocate again to prevent - // easily avoidable out-of-memory errors + // easily avoidable out-of-memory errors and make sized + // deallocation work properly buffer.reset(nullptr); + buffer.get_deleter() = operator_deleter(new_size * sizeof(rvalue_reference)); buffer.reset(static_cast( ::operator new(new_size * sizeof(rvalue_reference)) )); From 158f7219e6e78f086aa59f219d5b026d7d4ee897 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 20 Jul 2019 18:29:49 +0200 Subject: [PATCH 25/33] Add is_transparent to partial/weak/total_less/greater --- .../cpp-sort/comparators/partial_greater.h | 4 +- include/cpp-sort/comparators/partial_less.h | 4 +- include/cpp-sort/comparators/total_greater.h | 4 +- include/cpp-sort/comparators/total_less.h | 4 +- include/cpp-sort/comparators/weak_greater.h | 4 +- include/cpp-sort/comparators/weak_less.h | 4 +- testsuite/CMakeLists.txt | 1 + .../comparators/transparent_comparators.cpp | 69 +++++++++++++++++++ 8 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 testsuite/comparators/transparent_comparators.cpp diff --git a/include/cpp-sort/comparators/partial_greater.h b/include/cpp-sort/comparators/partial_greater.h index f2ac1971..3e10a3b2 100644 --- a/include/cpp-sort/comparators/partial_greater.h +++ b/include/cpp-sort/comparators/partial_greater.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -70,6 +70,8 @@ namespace cppsort { return partial_greater(std::forward(lhs), std::forward(rhs)); } + + using is_transparent = void; }; } diff --git a/include/cpp-sort/comparators/partial_less.h b/include/cpp-sort/comparators/partial_less.h index c42f3415..99cf696d 100644 --- a/include/cpp-sort/comparators/partial_less.h +++ b/include/cpp-sort/comparators/partial_less.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -70,6 +70,8 @@ namespace cppsort { return partial_less(std::forward(lhs), std::forward(rhs)); } + + using is_transparent = void; }; } diff --git a/include/cpp-sort/comparators/total_greater.h b/include/cpp-sort/comparators/total_greater.h index f026fe57..78677031 100644 --- a/include/cpp-sort/comparators/total_greater.h +++ b/include/cpp-sort/comparators/total_greater.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -79,6 +79,8 @@ namespace cppsort { return total_greater(std::forward(lhs), std::forward(rhs)); } + + using is_transparent = void; }; } diff --git a/include/cpp-sort/comparators/total_less.h b/include/cpp-sort/comparators/total_less.h index c76e4144..2e9d20c9 100644 --- a/include/cpp-sort/comparators/total_less.h +++ b/include/cpp-sort/comparators/total_less.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -79,6 +79,8 @@ namespace cppsort { return total_less(std::forward(lhs), std::forward(rhs)); } + + using is_transparent = void; }; } diff --git a/include/cpp-sort/comparators/weak_greater.h b/include/cpp-sort/comparators/weak_greater.h index d1e415cf..fd349fd2 100644 --- a/include/cpp-sort/comparators/weak_greater.h +++ b/include/cpp-sort/comparators/weak_greater.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -78,6 +78,8 @@ namespace cppsort { return weak_greater(std::forward(lhs), std::forward(rhs)); } + + using is_transparent = void; }; } diff --git a/include/cpp-sort/comparators/weak_less.h b/include/cpp-sort/comparators/weak_less.h index 2bbcd20a..6e3fac58 100644 --- a/include/cpp-sort/comparators/weak_less.h +++ b/include/cpp-sort/comparators/weak_less.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -78,6 +78,8 @@ namespace cppsort { return weak_less(std::forward(lhs), std::forward(rhs)); } + + using is_transparent = void; }; } diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 0f4954a1..d656d5fc 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -48,6 +48,7 @@ set( comparators/case_insensitive_less.cpp comparators/natural_less.cpp comparators/total_less.cpp + comparators/transparent_comparators.cpp ) set( diff --git a/testsuite/comparators/transparent_comparators.cpp b/testsuite/comparators/transparent_comparators.cpp new file mode 100644 index 00000000..ce0ee922 --- /dev/null +++ b/testsuite/comparators/transparent_comparators.cpp @@ -0,0 +1,69 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Morwenn + * + * 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 + +namespace +{ + // Hacky struct to make sure that it is not directly constructible + // from a string but is comparable with one + struct string_wrapper + { + string_wrapper(int value): + yay(std::to_string(value)) + {} + + std::string yay; + }; + + auto total_less(const string_wrapper& lhs, const string_wrapper& rhs) + -> bool + { + return lhs.yay < rhs.yay; + } + + auto total_less(const string_wrapper& lhs, const std::string& rhs) + -> bool + { + return lhs.yay < rhs; + } + + auto total_less(const std::string& lhs, const string_wrapper& rhs) + -> bool + { + return lhs < rhs.yay; + } +} + +TEST_CASE( "Check that comparators work as transparent comparators" ) +{ + using namespace std::string_literals; + + std::set> strings = { 1, 2, 3, 4 }; + + CHECK( strings.find("1"s) != strings.end() ); + CHECK( strings.find("8"s) == strings.end() ); +} From f7d822cb4db7b0267bc8812e0027b89bd7299449 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 26 Jul 2019 00:17:23 +0200 Subject: [PATCH 26/33] Better get_temporary_buffer --- include/cpp-sort/detail/memory.h | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/include/cpp-sort/detail/memory.h b/include/cpp-sort/detail/memory.h index 39f11675..420b6085 100644 --- a/include/cpp-sort/detail/memory.h +++ b/include/cpp-sort/detail/memory.h @@ -108,21 +108,31 @@ namespace detail // Reimplement the functions get_temporary_buffer and // return_temporary_buffer because of their removal in C++20 + /* + * @brief std::get_temporary_buffer on hormones + * @param count Desired number of objects + * @param min_count Number of objects small enough to give up + * + * This functions tries to allocate enough storage for at least + * \a count objects. Failing that it will try to allocate storage + * for fewer objects and will give up if it can't allocate more + * than \a min_size objects. + */ template - auto get_temporary_buffer(ptrdiff_t count) noexcept + auto get_temporary_buffer(std::ptrdiff_t count, std::ptrdiff_t min_count) noexcept -> std::pair { std::pair res(nullptr, 0); // Don't allocate more than possible - const ptrdiff_t max = std::numeric_limits::max() / sizeof(T); + constexpr ptrdiff_t max = std::numeric_limits::max() / sizeof(T); if (count > max) { count = max; } // Try to gradually allocate less memory until we get a valid buffer // or until the amount of memory to allocate reaches 0 - while (count > 0) { + while (count > min_count) { res.first = static_cast(::operator new(count * sizeof(T), std::nothrow)); if (res.first) { res.second = count; @@ -177,7 +187,7 @@ namespace detail explicit temporary_buffer(std::ptrdiff_t count) noexcept { - auto tmp = get_temporary_buffer(count); + auto tmp = get_temporary_buffer(count, 0); buffer = tmp.first; buffer_size = tmp.second; } @@ -222,13 +232,9 @@ namespace detail auto try_grow(std::ptrdiff_t count) noexcept -> bool { - if (count <= buffer_size) { - return false; - } - auto tmp = get_temporary_buffer(count); - if (tmp.second <= buffer_size) { - // If the allocated buffer isn't bigger, keep the old one - return_temporary_buffer(tmp.first, tmp.second); + auto tmp = get_temporary_buffer(count, buffer_size); + if (not tmp.first) { + // If it failed to allocate a bigger buffer, keep the old one return false; } // If the allocated buffer is big enough, replace the previous one From a42d90518c13eb0c8392ff563a5122d81f5a0cbc Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 26 Jul 2019 23:59:47 +0200 Subject: [PATCH 27/33] More better case_inisensitive_less and natural_less --- .../comparators/case_insensitive_less.h | 6 +- include/cpp-sort/comparators/natural_less.h | 14 +-- .../comparators/transparent_comparators.cpp | 93 +++++++++++++++++-- 3 files changed, 97 insertions(+), 16 deletions(-) diff --git a/include/cpp-sort/comparators/case_insensitive_less.h b/include/cpp-sort/comparators/case_insensitive_less.h index 85886d83..7f29bafb 100644 --- a/include/cpp-sort/comparators/case_insensitive_less.h +++ b/include/cpp-sort/comparators/case_insensitive_less.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -128,6 +128,8 @@ namespace cppsort { return adl_barrier::refined_case_insensitive_less_locale_fn(loc); } + + using is_transparent = void; }; struct case_insensitive_less_fn @@ -155,6 +157,8 @@ namespace cppsort { return case_insensitive_less_locale_fn(loc); } + + using is_transparent = void; }; namespace adl_barrier diff --git a/include/cpp-sort/comparators/natural_less.h b/include/cpp-sort/comparators/natural_less.h index 13d62fe2..1acf7a78 100644 --- a/include/cpp-sort/comparators/natural_less.h +++ b/include/cpp-sort/comparators/natural_less.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016 Morwenn + * Copyright (c) 2016-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -39,9 +39,9 @@ namespace cppsort //////////////////////////////////////////////////////////// // Natural order for char sequences - template - auto natural_less_impl(ForwardIterator begin1, ForwardIterator end1, - ForwardIterator begin2, ForwardIterator end2) + template + auto natural_less_impl(ForwardIterator1 begin1, ForwardIterator1 end1, + ForwardIterator2 begin2, ForwardIterator2 end2) -> bool { while (begin1 != end1 && begin2 != end2) { @@ -105,8 +105,8 @@ namespace cppsort return begin1 == end1 && begin2 != end2; } - template - auto natural_less(const T& lhs, const T& rhs) + template + auto natural_less(const T& lhs, const U& rhs) -> bool { return natural_less_impl(std::begin(lhs), std::end(lhs), @@ -125,6 +125,8 @@ namespace cppsort { return natural_less(std::forward(lhs), std::forward(rhs)); } + + using is_transparent = void; }; } diff --git a/testsuite/comparators/transparent_comparators.cpp b/testsuite/comparators/transparent_comparators.cpp index ce0ee922..06fb12bd 100644 --- a/testsuite/comparators/transparent_comparators.cpp +++ b/testsuite/comparators/transparent_comparators.cpp @@ -21,9 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include #include +#include #include +#include +#include #include namespace @@ -32,11 +36,37 @@ namespace // from a string but is comparable with one struct string_wrapper { - string_wrapper(int value): - yay(std::to_string(value)) - {} + std::vector yay; - std::string yay; + string_wrapper(int value) + { + auto str = std::to_string(value); + std::copy(str.begin(), str.end(), std::back_inserter(yay)); + } + + auto begin() + -> decltype(yay.begin()) + { + return yay.begin(); + } + + auto begin() const + -> decltype(yay.begin()) + { + return yay.begin(); + } + + auto end() + -> decltype(yay.end()) + { + return yay.end(); + } + + auto end() const + -> decltype(yay.end()) + { + return yay.end(); + } }; auto total_less(const string_wrapper& lhs, const string_wrapper& rhs) @@ -48,13 +78,39 @@ namespace auto total_less(const string_wrapper& lhs, const std::string& rhs) -> bool { - return lhs.yay < rhs; + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } auto total_less(const std::string& lhs, const string_wrapper& rhs) -> bool { - return lhs < rhs.yay; + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + auto case_insensitive_less(const string_wrapper& lhs, const std::string& rhs) + -> bool + { + // Doesn't make sense, but... + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + auto case_insensitive_less(const std::string& lhs, const string_wrapper& rhs) + -> bool + { + // Doesn't make sense, but... + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + auto natural_less(const string_wrapper& lhs, const std::string& rhs) + -> bool + { + return cppsort::detail::natural_less(lhs, rhs); + } + + auto natural_less(const std::string& lhs, const string_wrapper& rhs) + -> bool + { + return cppsort::detail::natural_less(lhs, rhs); } } @@ -62,8 +118,27 @@ TEST_CASE( "Check that comparators work as transparent comparators" ) { using namespace std::string_literals; - std::set> strings = { 1, 2, 3, 4 }; + SECTION( "total_less" ) + { + std::set> strings = { 1, 2, 3, 4 }; + + CHECK( strings.find("1"s) != strings.end() ); + CHECK( strings.find("8"s) == strings.end() ); + } - CHECK( strings.find("1"s) != strings.end() ); - CHECK( strings.find("8"s) == strings.end() ); + SECTION( "case_insensitive_less" ) + { + std::set> strings = { 1, 2, 3, 4 }; + + CHECK( strings.find("1"s) != strings.end() ); + CHECK( strings.find("8"s) == strings.end() ); + } + + SECTION( "natural_less" ) + { + std::set> strings = { 1, 2, 3, 4 }; + + CHECK( strings.find("1"s) != strings.end() ); + CHECK( strings.find("8"s) == strings.end() ); + } } From eba0d2884bd8d22f00a6434df8f2304cf8792e42 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 27 Jul 2019 09:59:20 +0200 Subject: [PATCH 28/33] Give accessible type names to comparators --- include/cpp-sort/comparators/case_insensitive_less.h | 2 ++ include/cpp-sort/comparators/natural_less.h | 2 ++ include/cpp-sort/comparators/partial_greater.h | 4 +++- include/cpp-sort/comparators/partial_less.h | 4 +++- include/cpp-sort/comparators/total_greater.h | 4 +++- include/cpp-sort/comparators/total_less.h | 4 +++- include/cpp-sort/comparators/weak_greater.h | 4 +++- include/cpp-sort/comparators/weak_less.h | 4 +++- testsuite/comparators/transparent_comparators.cpp | 7 ++++--- 9 files changed, 26 insertions(+), 9 deletions(-) diff --git a/include/cpp-sort/comparators/case_insensitive_less.h b/include/cpp-sort/comparators/case_insensitive_less.h index 7f29bafb..f0198938 100644 --- a/include/cpp-sort/comparators/case_insensitive_less.h +++ b/include/cpp-sort/comparators/case_insensitive_less.h @@ -288,6 +288,8 @@ namespace cppsort } } + using case_insensitive_less_t = detail::case_insensitive_less_fn; + namespace { constexpr auto&& case_insensitive_less = utility::static_const< diff --git a/include/cpp-sort/comparators/natural_less.h b/include/cpp-sort/comparators/natural_less.h index 1acf7a78..3012f897 100644 --- a/include/cpp-sort/comparators/natural_less.h +++ b/include/cpp-sort/comparators/natural_less.h @@ -130,6 +130,8 @@ namespace cppsort }; } + using natural_less_t = detail::natural_less_fn; + namespace { constexpr auto&& natural_less = utility::static_const< diff --git a/include/cpp-sort/comparators/partial_greater.h b/include/cpp-sort/comparators/partial_greater.h index 3e10a3b2..15b02155 100644 --- a/include/cpp-sort/comparators/partial_greater.h +++ b/include/cpp-sort/comparators/partial_greater.h @@ -75,6 +75,8 @@ namespace cppsort }; } + using partial_greater_t = detail::partial_greater_fn; + namespace { constexpr auto&& partial_greater = utility::static_const< @@ -87,7 +89,7 @@ namespace cppsort namespace utility { template - struct is_probably_branchless_comparison: + struct is_probably_branchless_comparison: std::is_arithmetic {}; } diff --git a/include/cpp-sort/comparators/partial_less.h b/include/cpp-sort/comparators/partial_less.h index 99cf696d..05cb4754 100644 --- a/include/cpp-sort/comparators/partial_less.h +++ b/include/cpp-sort/comparators/partial_less.h @@ -75,6 +75,8 @@ namespace cppsort }; } + using partial_less_t = detail::partial_less_fn; + namespace { constexpr auto&& partial_less = utility::static_const< @@ -87,7 +89,7 @@ namespace cppsort namespace utility { template - struct is_probably_branchless_comparison: + struct is_probably_branchless_comparison: std::is_arithmetic {}; } diff --git a/include/cpp-sort/comparators/total_greater.h b/include/cpp-sort/comparators/total_greater.h index 78677031..73986b0f 100644 --- a/include/cpp-sort/comparators/total_greater.h +++ b/include/cpp-sort/comparators/total_greater.h @@ -84,6 +84,8 @@ namespace cppsort }; } + using total_greater_t = detail::total_greater_fn; + namespace { constexpr auto&& total_greater = utility::static_const< @@ -96,7 +98,7 @@ namespace cppsort namespace utility { template - struct is_probably_branchless_comparison: + struct is_probably_branchless_comparison: std::is_integral {}; } diff --git a/include/cpp-sort/comparators/total_less.h b/include/cpp-sort/comparators/total_less.h index 2e9d20c9..fa1fb186 100644 --- a/include/cpp-sort/comparators/total_less.h +++ b/include/cpp-sort/comparators/total_less.h @@ -84,6 +84,8 @@ namespace cppsort }; } + using total_less_t = detail::total_less_fn; + namespace { constexpr auto&& total_less = utility::static_const< @@ -96,7 +98,7 @@ namespace cppsort namespace utility { template - struct is_probably_branchless_comparison: + struct is_probably_branchless_comparison: std::is_integral {}; } diff --git a/include/cpp-sort/comparators/weak_greater.h b/include/cpp-sort/comparators/weak_greater.h index fd349fd2..dfc94abc 100644 --- a/include/cpp-sort/comparators/weak_greater.h +++ b/include/cpp-sort/comparators/weak_greater.h @@ -83,6 +83,8 @@ namespace cppsort }; } + using weak_greater_t = detail::weak_greater_fn; + namespace { constexpr auto&& weak_greater = utility::static_const< @@ -95,7 +97,7 @@ namespace cppsort namespace utility { template - struct is_probably_branchless_comparison: + struct is_probably_branchless_comparison: std::is_integral {}; } diff --git a/include/cpp-sort/comparators/weak_less.h b/include/cpp-sort/comparators/weak_less.h index 6e3fac58..6ed3a154 100644 --- a/include/cpp-sort/comparators/weak_less.h +++ b/include/cpp-sort/comparators/weak_less.h @@ -83,6 +83,8 @@ namespace cppsort }; } + using weak_less_t = detail::weak_less_fn; + namespace { constexpr auto&& weak_less = utility::static_const< @@ -95,7 +97,7 @@ namespace cppsort namespace utility { template - struct is_probably_branchless_comparison: + struct is_probably_branchless_comparison: std::is_integral {}; } diff --git a/testsuite/comparators/transparent_comparators.cpp b/testsuite/comparators/transparent_comparators.cpp index 06fb12bd..a41dface 100644 --- a/testsuite/comparators/transparent_comparators.cpp +++ b/testsuite/comparators/transparent_comparators.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include #include #include #include @@ -120,7 +121,7 @@ TEST_CASE( "Check that comparators work as transparent comparators" ) SECTION( "total_less" ) { - std::set> strings = { 1, 2, 3, 4 }; + std::set strings = { 1, 2, 3, 4 }; CHECK( strings.find("1"s) != strings.end() ); CHECK( strings.find("8"s) == strings.end() ); @@ -128,7 +129,7 @@ TEST_CASE( "Check that comparators work as transparent comparators" ) SECTION( "case_insensitive_less" ) { - std::set> strings = { 1, 2, 3, 4 }; + std::set strings = { 1, 2, 3, 4 }; CHECK( strings.find("1"s) != strings.end() ); CHECK( strings.find("8"s) == strings.end() ); @@ -136,7 +137,7 @@ TEST_CASE( "Check that comparators work as transparent comparators" ) SECTION( "natural_less" ) { - std::set> strings = { 1, 2, 3, 4 }; + std::set strings = { 1, 2, 3, 4 }; CHECK( strings.find("1"s) != strings.end() ); CHECK( strings.find("8"s) == strings.end() ); From 919d61523653bf4ae59a5c8df0e44205c6c6b116 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 28 Jul 2019 17:26:00 +0200 Subject: [PATCH 29/33] Timsort: don't allocate in mergeLo/mergeHi when either run has a size of 1 --- include/cpp-sort/detail/timsort.h | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/include/cpp-sort/detail/timsort.h b/include/cpp-sort/detail/timsort.h index b18d9f1c..68d6b54d 100644 --- a/include/cpp-sort/detail/timsort.h +++ b/include/cpp-sort/detail/timsort.h @@ -430,6 +430,15 @@ namespace detail auto&& comp = utility::as_function(compare); auto&& proj = utility::as_function(projection); + if (len1 == 1) { + detail::rotate_left(base1, base2 + len2); + return; + } + if (len2 == 1) { + detail::rotate_right(base1, base2 + len2); + return; + } + resize_buffer(len1); destruct_n d(0); std::unique_ptr&> h2(buffer.get(), d); @@ -440,21 +449,13 @@ namespace detail } auto cursor1 = buffer.get(); - iterator cursor2 = base2; - iterator dest = base1; + auto cursor2 = base2; + auto dest = base1; *dest = iter_move(cursor2); ++dest; ++cursor2; - if (--len2 == 0) { - detail::move(cursor1, cursor1 + len1, dest); - return; - } - if (len1 == 1) { - detail::move(cursor2, cursor2 + len2, dest); - dest[len2] = iter_move(cursor1); - return; - } + --len2; int minGallop(minGallop_); @@ -577,6 +578,15 @@ namespace detail auto&& comp = utility::as_function(compare); auto&& proj = utility::as_function(projection); + if (len1 == 1) { + detail::rotate_left(base1, base2 + len2); + return; + } + if (len2 == 1) { + detail::rotate_right(base1, base2 + len2); + return; + } + resize_buffer(len2); destruct_n d(0); std::unique_ptr&> h2(buffer.get(), d); @@ -586,24 +596,14 @@ namespace detail ::new(ptr) rvalue_reference(iter_move(it)); } - iterator cursor1 = base1 + (len1 - 1); - auto cursor2 = buffer.get() + (len2 - 1); - iterator dest = base2 + (len2 - 1); + auto cursor1 = base1 + (len1 - 1); + auto cursor2 = buffer.get() + (len2 - 1); + auto dest = base2 + (len2 - 1); *dest = iter_move(cursor1); --dest; --cursor1; - if (--len1 == 0) { - detail::move(buffer.get(), buffer.get() + len2, dest - (len2 - 1)); - return; - } - if (len2 == 1) { - dest -= len1; - cursor1 -= len1; - detail::move_backward(cursor1 + 1, cursor1 + (1 + len1), dest + (1 + len1)); - *dest = iter_move(cursor2); - return; - } + --len1; int minGallop( minGallop_ ); From 84f521ed12fab4d1a78ab2b8a9217c9e775dda7f Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 28 Jul 2019 18:16:24 +0200 Subject: [PATCH 30/33] Make Timsort spacing more consistent with the rest of the library [ci skip] --- include/cpp-sort/detail/timsort.h | 67 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/include/cpp-sort/detail/timsort.h b/include/cpp-sort/detail/timsort.h index 68d6b54d..fc28ac2d 100644 --- a/include/cpp-sort/detail/timsort.h +++ b/include/cpp-sort/detail/timsort.h @@ -47,6 +47,7 @@ #include "memory.h" #include "move.h" #include "reverse.h" +#include "rotate.h" #include "type_traits.h" #include "upper_bound.h" @@ -108,12 +109,12 @@ namespace detail TimSort ts{}; difference_type const minRun = minRunLength(nRemaining); - iterator cur = lo; + iterator cur = lo; do { difference_type runLen = countRunAndMakeAscending(cur, hi, compare, projection); if (runLen < minRun) { - difference_type const force = std::min(nRemaining, minRun); + difference_type const force = std::min(nRemaining, minRun); binarySort(cur, cur + force, cur + runLen, compare, projection); runLen = force; } @@ -121,7 +122,7 @@ namespace detail ts.pushRun(cur, runLen); ts.mergeCollapse(compare, projection); - cur += runLen; + cur += runLen; nRemaining -= runLen; } while (nRemaining != 0); @@ -143,7 +144,7 @@ namespace detail if (start == lo) { ++start; } - for ( ; start < hi; ++start ) { + for (; start < hi; ++start) { assert(lo <= start); auto pivot = iter_move(start); @@ -164,7 +165,7 @@ namespace detail auto&& proj = utility::as_function(projection); iterator runHi = std::next(lo); - if ( runHi == hi ) { + if (runHi == hi) { return 1; } @@ -204,7 +205,7 @@ namespace detail auto mergeCollapse(Compare compare, Projection projection) -> void { - while ( pending_.size() > 1 ) { + while (pending_.size() > 1) { difference_type n = pending_.size() - 2; if ((n > 0 && pending_[n - 1].len <= pending_[n].len + pending_[n + 1].len) @@ -226,7 +227,7 @@ namespace detail auto mergeForceCollapse(Compare compare, Projection projection) -> void { - while ( pending_.size() > 1 ) { + while (pending_.size() > 1) { difference_type n = pending_.size() - 2; if (n > 0 && pending_[n - 1].len < pending_[n + 1].len) { @@ -245,9 +246,9 @@ namespace detail assert( i == stackSize - 2 || i == stackSize - 3 ); iterator base1 = pending_[i].base; - difference_type len1 = pending_[i].len; + difference_type len1 = pending_[i].len; iterator base2 = pending_[i + 1].base; - difference_type len2 = pending_[i + 1].len; + difference_type len2 = pending_[i + 1].len; assert( len1 > 0 ); assert( len2 > 0 ); @@ -265,7 +266,7 @@ namespace detail assert( k >= 0 ); base1 += k; - len1 -= k; + len1 -= k; if (len1 == 0) { return; @@ -305,7 +306,7 @@ namespace detail difference_type const maxOfs = len - hint; while (ofs < maxOfs && comp(proj(base[hint + ofs]), key_proj)) { lastOfs = ofs; - ofs = (ofs << 1) + 1; + ofs = (ofs << 1) + 1; if (ofs <= 0) { // int overflow ofs = maxOfs; @@ -316,13 +317,13 @@ namespace detail } lastOfs += hint; - ofs += hint; + ofs += hint; } else { difference_type const maxOfs = hint + 1; while (ofs < maxOfs && not comp(proj(base[hint - ofs]), key_proj)) { lastOfs = ofs; - ofs = (ofs << 1) + 1; + ofs = (ofs << 1) + 1; if (ofs <= 0) { ofs = maxOfs; @@ -333,8 +334,8 @@ namespace detail } difference_type const tmp = lastOfs; - lastOfs = hint - ofs; - ofs = hint - tmp; + lastOfs = hint - ofs; + ofs = hint - tmp; } assert( -1 <= lastOfs ); assert( lastOfs < ofs ); @@ -363,7 +364,7 @@ namespace detail difference_type const maxOfs = hint + 1; while (ofs < maxOfs && comp(key_proj, proj(base[hint - ofs]))) { lastOfs = ofs; - ofs = (ofs << 1) + 1; + ofs = (ofs << 1) + 1; if (ofs <= 0) { ofs = maxOfs; @@ -374,14 +375,14 @@ namespace detail } difference_type const tmp = lastOfs; - lastOfs = hint - ofs; - ofs = hint - tmp; + lastOfs = hint - ofs; + ofs = hint - tmp; } else { difference_type const maxOfs = len - hint; while (ofs < maxOfs && not comp(key_proj, proj(base[hint + ofs]))) { lastOfs = ofs; - ofs = (ofs << 1) + 1; + ofs = (ofs << 1) + 1; if (ofs <= 0) { // int overflow ofs = maxOfs; @@ -392,7 +393,7 @@ namespace detail } lastOfs += hint; - ofs += hint; + ofs += hint; } assert( -1 <= lastOfs ); assert( lastOfs < ofs ); @@ -491,7 +492,7 @@ namespace detail break; } } - } while ( (count1 | count2) < minGallop ); + } while ((count1 | count2) < minGallop); if (break_outer) { break; } @@ -503,9 +504,9 @@ namespace detail count1 = gallopRight(*cursor2, cursor1, len1, 0, compare, projection); if (count1 != 0) { detail::move_backward(cursor1, cursor1 + count1, dest + count1); - dest += count1; + dest += count1; cursor1 += count1; - len1 -= count1; + len1 -= count1; if (len1 <= 1) { break_outer = true; @@ -523,9 +524,9 @@ namespace detail count2 = gallopLeft(*cursor1, cursor2, len2, 0, compare, projection); if (count2 != 0) { detail::move(cursor2, cursor2 + count2, dest); - dest += count2; + dest += count2; cursor2 += count2; - len2 -= count2; + len2 -= count2; if (len2 == 0) { break_outer = true; break; @@ -540,7 +541,7 @@ namespace detail } --minGallop; - } while ( (count1 >= min_gallop) | (count2 >= min_gallop) ); + } while ((count1 >= min_gallop) | (count2 >= min_gallop)); if (break_outer) { break; } @@ -639,7 +640,7 @@ namespace detail break; } } - } while ( (count1 | count2) < minGallop ); + } while ((count1 | count2) < minGallop); if (break_outer) { break; } @@ -650,9 +651,9 @@ namespace detail count1 = len1 - gallopRight(*cursor2, base1, len1, len1 - 1, compare, projection); if (count1 != 0) { - dest -= count1; + dest -= count1; cursor1 -= count1; - len1 -= count1; + len1 -= count1; detail::move_backward(cursor1 + 1, cursor1 + (1 + count1), dest + (1 + count1)); if (len1 == 0) { @@ -670,9 +671,9 @@ namespace detail count2 = len2 - gallopLeft(*cursor1, buffer.get(), len2, len2 - 1, compare, projection); if (count2 != 0) { - dest -= count2; + dest -= count2; cursor2 -= count2; - len2 -= count2; + len2 -= count2; detail::move(cursor2 + 1, cursor2 + (1 + count2), dest + 1); if (len2 <= 1) { break_outer = true; @@ -688,7 +689,7 @@ namespace detail } minGallop--; - } while ( (count1 >= min_gallop) | (count2 >= min_gallop) ); + } while ((count1 >= min_gallop) | (count2 >= min_gallop)); if (break_outer) { break; } @@ -703,7 +704,7 @@ namespace detail if (len2 == 1) { assert( len1 > 0 ); - dest -= len1; + dest -= len1; detail::move_backward(cursor1 + (1 - len1), cursor1 + 1, dest + (1 + len1)); *dest = iter_move(cursor2); } From fe99e6b0fb09afae5b4e549acaec9def88d41928 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 16 Aug 2019 18:41:55 +0200 Subject: [PATCH 31/33] Fix seemingly codegen issue with MinGW-w64 (issue #151) --- include/cpp-sort/detail/quick_merge_sort.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/cpp-sort/detail/quick_merge_sort.h b/include/cpp-sort/detail/quick_merge_sort.h index aade8f6b..9cac9466 100644 --- a/include/cpp-sort/detail/quick_merge_sort.h +++ b/include/cpp-sort/detail/quick_merge_sort.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2018 Morwenn + * Copyright (c) 2018-2019 Morwenn * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,12 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include #include #include #include "config.h" +#include "iterator_traits.h" #include "nth_element.h" #include "quicksort.h" #include "swap_ranges.h" @@ -156,7 +158,12 @@ namespace detail auto pivot = detail::nth_element(first, last, size_left, size, compare, projection); internal_mergesort(first, pivot, size_left, pivot, compare, projection); - first = pivot; + if (std::is_base_of>::value) { + // Avoid weird codegen bug with MinGW-w64 (see GitHub issue #151) + std::advance(first, size_left); + } else { + first = pivot; + } size -= size_left; } small_sort(first, last, size, std::move(compare), std::move(projection)); From 08fb330d5c80e1c05f2aefcfffbbbbddf11b4bd3 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 16 Aug 2019 19:49:57 +0200 Subject: [PATCH 32/33] More tweaks for mtime_cache --- .travis.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 890970eb..147578de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -210,10 +210,14 @@ script: else travis_wait ctest -C ${BUILD_TYPE} --output-on-failure; fi - - cd .. + - if [[ $TRAVIS_OS_NAME = "windows" ]]; then + cd ..; + fi after_success: - - cd build + - if [[ $TRAVIS_OS_NAME = "windows" ]]; then + cd build; + fi - if [[ $ENABLE_COVERAGE = true ]]; then make gcov; make lcov; @@ -221,7 +225,9 @@ after_success: fi after_failure: - - cd build + - if [[ $TRAVIS_OS_NAME = "windows" ]]; then + cd build; + fi - if [[ $USE_VALGRIND = true ]]; then find ./Testing/Temporary -type f -name "MemoryChecker.*.log" -size +1300c | xargs cat; fi From 5760b4d86948715cbd9f52203741d33aa40125d1 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 16 Aug 2019 21:21:25 +0200 Subject: [PATCH 33/33] Preparing release 1.5.0 --- .travis.yml | 2 +- CMakeLists.txt | 2 +- README.md | 4 ++-- conanfile.py | 2 +- include/cpp-sort/version.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 147578de..0c3c760f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -173,7 +173,7 @@ matrix: after_success: - conan remote add bintray https://api.bintray.com/conan/morwenn/cpp-sort - conan user -r bintray -p ${CONAN_PASSWORD} morwenn - - conan upload --all -r bintray cpp-sort/1.4.0@morwenn/stable + - conan upload --all -r bintray cpp-sort/1.5.0@morwenn/stable before_install: - if [[ $TRAVIS_OS_NAME = "linux" && $CXX = "clang++" ]]; then diff --git a/CMakeLists.txt b/CMakeLists.txt index 100430e8..ca2c3915 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8.0) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -project(cpp-sort VERSION 1.4.0 LANGUAGES CXX) +project(cpp-sort VERSION 1.5.0 LANGUAGES CXX) include(CMakePackageConfigHelpers) include(GNUInstallDirs) diff --git a/README.md b/README.md index 86b6f6a8..928d8223 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Latest Release](https://img.shields.io/badge/release-cpp--sort%2F1.4.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases) -[![Conan Package](https://img.shields.io/badge/conan-1.4.0-blue.svg)](https://bintray.com/morwenn/cpp-sort/cpp-sort%3Amorwenn) +[![Latest Release](https://img.shields.io/badge/release-cpp--sort%2F1.5.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases) +[![Conan Package](https://img.shields.io/badge/conan-1.5.0-blue.svg)](https://bintray.com/morwenn/cpp-sort/cpp-sort%3Amorwenn) [![Build Status](https://travis-ci.org/Morwenn/cpp-sort.svg?branch=master)](https://travis-ci.org/Morwenn/cpp-sort) [![License](http://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org) [![Code Coverage](https://codecov.io/gh/Morwenn/cpp-sort/branch/master/graph/badge.svg)](https://codecov.io/gh/Morwenn/cpp-sort) diff --git a/conanfile.py b/conanfile.py index 09899b18..3a344c87 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,7 +3,7 @@ class CppSortConan(ConanFile): name = "cpp-sort" - version = "1.4.0" + version = "1.5.0" license = "https://github.com/Morwenn/cpp-sort/blob/master/license.txt" url = "https://github.com/Morwenn/cpp-sort" author = "Morwenn " diff --git a/include/cpp-sort/version.h b/include/cpp-sort/version.h index c0ca6f11..2e04a7ec 100644 --- a/include/cpp-sort/version.h +++ b/include/cpp-sort/version.h @@ -27,7 +27,7 @@ // Semantic versioning macros #define CPPSORT_VERSION_MAJOR 1 -#define CPPSORT_VERSION_MINOR 4 +#define CPPSORT_VERSION_MINOR 5 #define CPPSORT_VERSION_PATCH 0 #endif // CPPSORT_VERSION_H_