diff --git a/docs/Sorter-adapters.md b/docs/Sorter-adapters.md index 33d89709..c49dba4b 100644 --- a/docs/Sorter-adapters.md +++ b/docs/Sorter-adapters.md @@ -74,27 +74,27 @@ Adapting any *sorter* with `drop_merge_adapter` effectively makes it [*Rem*-adap #include ``` -The goal of this sorter adapter is to aggregate several sorters into one unique sorter. The new sorter will call the appropriate sorting algorithm based on the iterator category of the collection to sort. If several of the aggregated sorters have the same iterator category, the first to appear in the template parameter list will be chosen, unless some SFINAE condition prevents it from being used. As long as the iterator categories are different, the order of the sorters in the parameter pack does not matter. +The goal of this sorter adapter is to aggregate several *adapted sorters* into a unique *resulting sorter*, which dispatches calls to the appropriate *adapted sorter* based on its iterator category and that of the collection to sort. If several of the *adapted sorters* that would be picked have the same iterator category, the first to appear in the template parameter list is chosen, unless some SFINAE condition prevents it from being picked by overload resolution. As long as the iterator categories are different, the order of the sorters in the parameter pack does not matter. -For example, the following sorter should call a pattern-defeating quicksort to sort a random-access collection, a vergesort to sort a bidirectional collection and a bubble sort to sort a forward collection: +For example, the following sorter calls a pattern-defeating quicksort to sort random-access collections, an insertion sort to sort bidirectional collections, and a bubble sort to sort forward collections: ```cpp using general_purpose_sorter = hybrid_adapter< bubble_sorter, - verge_sorter, + insertion_sorter, pdq_sorter >; ``` -This adapter uses `cppsort::iterator_category` to check the iterator category of the sorters to aggregate. Therefore, if you write a sorter and want it to be usable with `hybrid_adapter`, you will need your sorter to provide an `iterator_category` type alias corresponding to one of the standard iterator tags. If you write specific sorters that only work with some specific types, you might want to SFINAE out the overloads of `operator()` when they are not valid instead of triggering a hard error. Doing so will allow to use them with fallback sorters in `hybrid_adapter` to handle the cases where the type to sort cannot be handled by your sorter. +This adapter uses [`cppsort::iterator_category`][iterator-category] to check the iterator category of the sorters to aggregate. Therefore, if you write a sorter and want it to be usable with `hybrid_adapter`, you need your sorter to provide an `iterator_category` type alias corresponding to one of the [standard iterator tags][iterator-tags]. If you write specific sorters that only work with some specific types, you might want to SFINAE out the overloads of `operator()` when they are not valid instead of triggering a hard error: doing so will allow to use them with fallback sorters in `hybrid_adapter` to handle the cases where the type to sort cannot be handled by your sorter. `hybrid_adapter` returns the result of the *adapted sorter* called if any. -If `hybrid_adapter` is nested in another `hybrid_adapter`, those are flattened: for example `hybrid_adapter, D>` is flattened to `hybrid_adapter`. This unwrapping exists so that the iterator categories of the sorters in the inner `hybrid_adapter` are seen by the outer one, and not only the fused iterator category of the inner `hybrid_adapter`. +If `hybrid_adapter` is nested in another `hybrid_adapter`, those are automatically flattened: for example `hybrid_adapter, D>` is flattened to `hybrid_adapter`. This unwrapping exists so that the iterator categories of the sorters in the inner `hybrid_adapter` are seen by the outer one, and not only the fused iterator category of the inner `hybrid_adapter`. -If `hybrid_adapter` is wrapped into [`stable_adapter`][stable-adapter], it wraps every *adapted sorter* into `stable_adapter`, forwarding it to better get the specific behaviour f some sorters or adapters when wrapped into it. +If `hybrid_adapter` is wrapped into [`stable_adapter`][stable-adapter], it wraps every *adapted sorter* into `stable_adapter`, forwarding it to better get the specific behaviour of some sorters or adapters when wrapped into it. -The *resulting sorter*'s `is_always_stable` is `std::true_type` if and only if every *adapted sorter*'s `is_always_stable` is `std::true_type`. `is_stable` is specialized so that it will return the stability of the called *adapted sorter* with the given parameters. The iterator category of the *resulting sorter* is the most permissive iterator category among the *adapted sorters*. +The *resulting sorter*'s `is_always_stable` is `std::true_type` if and only if every *adapted sorter*'s `is_always_stable` is `std::true_type`. `is_stable` is specialized so that it returns the stability of the called *adapted sorter* with the given parameters. The iterator category of the *resulting sorter* is the most permissive iterator category among the *adapted sorters*. ### `indirect_adapter` @@ -322,6 +322,8 @@ When wrapped into [`stable_adapter`][stable-adapter], it has a slightly differen [is-always-stable]: Sorter-traits.md#is_always_stable [is-stable]: Sorter-traits.md#is_stable [issue-104]: https://github.com/Morwenn/cpp-sort/issues/104 + [iterator-category]: Sorter-traits.md#iterator_category + [iterator-tags]: https://en.cppreference.com/w/cpp/iterator/iterator_tags [low-moves-sorter]: Fixed-size-sorters.md#low_moves_sorter [mountain-sort]: https://github.com/Morwenn/mountain-sort [probe-mono]: Measures-of-presortedness.md#mono diff --git a/include/cpp-sort/adapters/hybrid_adapter.h b/include/cpp-sort/adapters/hybrid_adapter.h index 491b45aa..aaeb7f68 100644 --- a/include/cpp-sort/adapters/hybrid_adapter.h +++ b/include/cpp-sort/adapters/hybrid_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022 Morwenn + * Copyright (c) 2015-2024 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_ADAPTERS_HYBRID_ADAPTER_H_ @@ -48,6 +48,9 @@ namespace cppsort template std::size_t iterator_category_value; + template<> + inline constexpr std::size_t iterator_category_value = 3; + template<> inline constexpr std::size_t iterator_category_value = 2; @@ -61,7 +64,7 @@ namespace cppsort template using choice_for_it = choice< (iterator_category_value< - iterator_category_t + iterator_concept_t > + 1) * N - 1 >; diff --git a/include/cpp-sort/detail/iterator_traits.h b/include/cpp-sort/detail/iterator_traits.h index fde4340e..24374188 100644 --- a/include/cpp-sort/detail/iterator_traits.h +++ b/include/cpp-sort/detail/iterator_traits.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2022 Morwenn + * Copyright (c) 2016-2024 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_ITERATOR_TRAITS_H_ @@ -24,6 +24,9 @@ namespace cppsort::detail template using iterator_category_t = typename std::iterator_traits::iterator_category; + template + using iterator_concept_t = typename mstd::detail::iter_concept; + // Additional common type to use instead of value_t template using rvalue_type_t = std::remove_cvref_t>; diff --git a/tests/adapters/hybrid_adapter_many_sorters.cpp b/tests/adapters/hybrid_adapter_many_sorters.cpp index e3e46302..e6a759f3 100644 --- a/tests/adapters/hybrid_adapter_many_sorters.cpp +++ b/tests/adapters/hybrid_adapter_many_sorters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022 Morwenn + * Copyright (c) 2018-2024 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -14,10 +14,23 @@ namespace enum class sorter_type { bidirectional, - random_access + random_access, + contiguous }; template + struct contiguous_sorter_impl + { + template + auto operator()(Iterator, Iterator) const + -> sorter_type + { + return sorter_type::contiguous; + } + + using iterator_category = std::contiguous_iterator_tag; + }; + struct random_access_sorter_impl { template @@ -43,8 +56,12 @@ namespace }; template + struct contiguous_sorter: + cppsort::sorter_facade> + {}; + struct random_access_sorter: - cppsort::sorter_facade> + cppsort::sorter_facade {}; struct bidirectional_sorter: @@ -61,17 +78,18 @@ TEST_CASE( "hybrid_adapter with many sorters", cppsort::hybrid_adapter< bidirectional_sorter, - random_access_sorter<0>, - random_access_sorter<1>, - random_access_sorter<2>, - random_access_sorter<3>, - random_access_sorter<4>, - random_access_sorter<5> + random_access_sorter, + contiguous_sorter<0>, + contiguous_sorter<1>, + contiguous_sorter<2>, + contiguous_sorter<3>, + contiguous_sorter<4>, + contiguous_sorter<5> > sorter; std::vector vec; auto res1 = sorter(vec); - CHECK( res1 == sorter_type::random_access ); + CHECK( res1 == sorter_type::contiguous ); std::list li; auto res2 = sorter(li);