diff options
author | pechatnov <pechatnov@yandex-team.ru> | 2022-02-10 16:48:57 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:48:57 +0300 |
commit | 8e9b2f8bbf4a2320f539eef5b85555f42c065425 (patch) | |
tree | 189a13fe5128c85492e45518171a532ffa90ba03 /library/cpp/iterator | |
parent | 92040fb3ad117c48c87d591bf9fe916ffda61233 (diff) | |
download | ydb-8e9b2f8bbf4a2320f539eef5b85555f42c065425.tar.gz |
Restoring authorship annotation for <pechatnov@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'library/cpp/iterator')
-rw-r--r-- | library/cpp/iterator/README.md | 200 | ||||
-rw-r--r-- | library/cpp/iterator/cartesian_product.cpp | 2 | ||||
-rw-r--r-- | library/cpp/iterator/cartesian_product.h | 224 | ||||
-rw-r--r-- | library/cpp/iterator/concatenate.cpp | 2 | ||||
-rw-r--r-- | library/cpp/iterator/concatenate.h | 262 | ||||
-rw-r--r-- | library/cpp/iterator/enumerate.cpp | 2 | ||||
-rw-r--r-- | library/cpp/iterator/enumerate.h | 158 | ||||
-rw-r--r-- | library/cpp/iterator/filtering.cpp | 2 | ||||
-rw-r--r-- | library/cpp/iterator/filtering.h | 74 | ||||
-rw-r--r-- | library/cpp/iterator/functools.cpp | 2 | ||||
-rw-r--r-- | library/cpp/iterator/functools.h | 114 | ||||
-rw-r--r-- | library/cpp/iterator/mapped.cpp | 2 | ||||
-rw-r--r-- | library/cpp/iterator/mapped.h | 128 | ||||
-rw-r--r-- | library/cpp/iterator/ut/functools_ut.cpp | 972 | ||||
-rw-r--r-- | library/cpp/iterator/ut/ya.make | 2 | ||||
-rw-r--r-- | library/cpp/iterator/ya.make | 20 | ||||
-rw-r--r-- | library/cpp/iterator/zip.cpp | 2 | ||||
-rw-r--r-- | library/cpp/iterator/zip.h | 230 |
18 files changed, 1199 insertions, 1199 deletions
diff --git a/library/cpp/iterator/README.md b/library/cpp/iterator/README.md index cd92a284c9..77f0c29b92 100644 --- a/library/cpp/iterator/README.md +++ b/library/cpp/iterator/README.md @@ -1,100 +1,100 @@ -# Functools library - -Эта библиотека предоставляет функции `Enumerate`, `Zip`, `Map`, `Filter`, `Concatenate` и `CartesianProduct`. - -`Enumerate`, `Zip`, `Map`, `Filter` повторяют логику одноименных функций из python. - -Важный момент: - * Итераторы данных view почти во всех случаях (кроме Map, там зависит от маппера) возвращают `std::tuple` **по значению** (при этом шаблонные параметры всегда, когда это уместно, ссылки). - <br> Так что нет никакого смысла делать `for (const auto& [i, x] : Enumerate(c))`. - <br> Лучше всегда `for (auto [i, x] : Enumerate(c))` (`x` в этом случае будет ссылкой и работать с ним можно как со ссылкой) или `for (const auto [i, x] : Enumerate(c))`. - -Предоставляемые гарантии: - * Работа для всех контейнеров, для которых работает range-based for (для `Enumerate`, `Zip`, `Concatenate`, `CartesianProduct`). - Для `Map` и `Filter` есть требование на то, что первый и последний итераторы контейнера имеют один тип - * В том числе работает для обычных массивов (`int a[] = {1, 2, 3}; Enumerate(a)`). - * Поддержка rvalue для контейнеров, предикатов и функций-мапперов (`Filter([](auto x){...}, TVector{1, 2, 3})`). - В этом случае объекты сохраняются внутри view. - * Проброс элементов контейнеров по неконстантной ссылке - * `TView::iterator` - можно полагаться, что этот тип есть и корректен - * `TIterator::iterator_category` - можно полагаться, что этот тип есть и определен. - - -На что гарантий нет: - * Любые изменения контейнеров, меняющие размер или инвалидирующие их итераторы, инвалидируют созданные view - * Не принимает списки инициализации (`Enumerate({1, 2, 3})`), так как неизвестен желаемый тип контейнера. - * В классах реализации оставлены публичные члены вида `.Field_`, чтобы не загромождать реализацию - Тем не менее эти поля не гарантированы, могут стать приватными или исчезнуть - * Для всех итераторов определены вложенные типы: `value_type`, `pointer`, `reference`. - Тем не менее не рекомендуется их использовать в связи с их неоднозначностью. - `value_type` может быть как обычным типом, так и ссылкой. Может быть `std::tuple<T1, T2>`, - а может `std::tuple<T1&, const T2&>`. - Если возникает необходимость в этих типах, то возможно, стоит упростить код и вообще не использовать эти view. - Если очень хочется можно использовать `delctype(*container.begin())`. - - -Производительность: - * Бенчмарки времени компиляции и скорости выполнения, а так же сравнение с range-v3 и другими существующими реализациями - доступны в [репозитории где ведется разработка](https://github.com/yuri-pechatnov/cpp_functools/tree/master "functools"). - - - -Q: Оверхед? -A: По выполнению: на Enumerate, Zip, Map - нулевой. Где-то x1.5 на Filter, и x3 на Concatenate и CartesianProduct. Но если в теле цикла происходит хоть что-то существенное, то это пренебрежимо мало. - По компиляции: сложно рассчитать как оно скажется в реальном большом проекте, но приблизительно не более x1.5 на один цикл. - -Q: А зачем свой велосипед? -A: ((https://pechatnov.at.yandex-team.ru/67 Ответ в этом посте)). - -Q: А почему вот здесь плохо написано, надо же по-другому? -A: Код несколько раз переписывался и согласовывался ((https://st.yandex-team.ru/IGNIETFERRO-973 более полугода)). А допиливать его внутреннюю реализацию после коммита никто не мешает и дальше. - - -Сигнатуры и эквиваленты: - - -```cpp -//! In all comments variables ending with '_' -//! are considered as invisible for {...} block. - -//! Usage: for (auto [i, x] : Enumerate(container)) {...} -//! Equivalent: { std::size_t i_ = 0; for (auto& x : container) { const std::size_t i = i_; {...}; ++i_; }} -template <typename TContainerOrRef> -auto Enumerate(TContainerOrRef&& container); - -//! Usage: for (auto x : Map(mapperFunc, container)) {...} -//! Equivalent: for (auto iter_ = std::begin(container); iter_ != std::end(container); ++iter_) { -//! auto x = mapperFunc(*iter_); {...}; } -template <typename TMapper, typename TContainerOrRef> -auto Map(TMapper&& mapper, TContainerOrRef&& container); - -//! Usage: for (auto x : Filter(predicate, container)) {...} -//! Equivalent: for (auto x : container) { if (predicate(x)) {...}} -template <typename TPredicate, typename TContainerOrRef> -auto Filter(TPredicate&& predicate, TContainerOrRef&& container); - -//! Usage: for (auto [ai, bi] : Zip(a, b)) {...} -//! Equivalent: { auto ia_ = std::begin(a); auto ib_ = std::begin(b); -//! for (; ia_ != std::end(a) && ib_ != std::end(b); ++ia_, ++ib_) { -//! auto&& ai = *ia_; auto&& bi = *ib_; {...} -//! }} -template <typename... TContainers> -auto Zip(TContainers&&... containers); - -//! Usage: for (auto x : Reversed(container)) {...} -//! Equivalent: for (auto iter_ = std::rbegin(container); iter_ != std::rend(container); ++iter_) { -//! auto x = *iter_; {...}} -template <typename TContainerOrRef> -auto Reversed(TContainerOrRef&& container); - -//! Usage: for (auto x : Concatenate(a, b)) {...} -//! Equivalent: { for (auto x : a) {...} for (auto x : b) {...} } -//! (if there is no static variables in {...}) -template <typename TFirstContainer, typename... TContainers> -auto Concatenate(TFirstContainer&& container, TContainers&&... containers); - -//! Usage: for (auto [ai, bi] : CartesianProduct(a, b)) {...} -//! Equivalent: for (auto& ai : a) { for (auto& bi : b) {...} } -template <typename... TContainers> -auto CartesianProduct(TContainers&&... containers); -``` +# Functools library + +Эта библиотека предоставляет функции `Enumerate`, `Zip`, `Map`, `Filter`, `Concatenate` и `CartesianProduct`. + +`Enumerate`, `Zip`, `Map`, `Filter` повторяют логику одноименных функций из python. + +Важный момент: + * Итераторы данных view почти во всех случаях (кроме Map, там зависит от маппера) возвращают `std::tuple` **по значению** (при этом шаблонные параметры всегда, когда это уместно, ссылки). + <br> Так что нет никакого смысла делать `for (const auto& [i, x] : Enumerate(c))`. + <br> Лучше всегда `for (auto [i, x] : Enumerate(c))` (`x` в этом случае будет ссылкой и работать с ним можно как со ссылкой) или `for (const auto [i, x] : Enumerate(c))`. + +Предоставляемые гарантии: + * Работа для всех контейнеров, для которых работает range-based for (для `Enumerate`, `Zip`, `Concatenate`, `CartesianProduct`). + Для `Map` и `Filter` есть требование на то, что первый и последний итераторы контейнера имеют один тип + * В том числе работает для обычных массивов (`int a[] = {1, 2, 3}; Enumerate(a)`). + * Поддержка rvalue для контейнеров, предикатов и функций-мапперов (`Filter([](auto x){...}, TVector{1, 2, 3})`). + В этом случае объекты сохраняются внутри view. + * Проброс элементов контейнеров по неконстантной ссылке + * `TView::iterator` - можно полагаться, что этот тип есть и корректен + * `TIterator::iterator_category` - можно полагаться, что этот тип есть и определен. + + +На что гарантий нет: + * Любые изменения контейнеров, меняющие размер или инвалидирующие их итераторы, инвалидируют созданные view + * Не принимает списки инициализации (`Enumerate({1, 2, 3})`), так как неизвестен желаемый тип контейнера. + * В классах реализации оставлены публичные члены вида `.Field_`, чтобы не загромождать реализацию + Тем не менее эти поля не гарантированы, могут стать приватными или исчезнуть + * Для всех итераторов определены вложенные типы: `value_type`, `pointer`, `reference`. + Тем не менее не рекомендуется их использовать в связи с их неоднозначностью. + `value_type` может быть как обычным типом, так и ссылкой. Может быть `std::tuple<T1, T2>`, + а может `std::tuple<T1&, const T2&>`. + Если возникает необходимость в этих типах, то возможно, стоит упростить код и вообще не использовать эти view. + Если очень хочется можно использовать `delctype(*container.begin())`. + + +Производительность: + * Бенчмарки времени компиляции и скорости выполнения, а так же сравнение с range-v3 и другими существующими реализациями + доступны в [репозитории где ведется разработка](https://github.com/yuri-pechatnov/cpp_functools/tree/master "functools"). + + + +Q: Оверхед? +A: По выполнению: на Enumerate, Zip, Map - нулевой. Где-то x1.5 на Filter, и x3 на Concatenate и CartesianProduct. Но если в теле цикла происходит хоть что-то существенное, то это пренебрежимо мало. + По компиляции: сложно рассчитать как оно скажется в реальном большом проекте, но приблизительно не более x1.5 на один цикл. + +Q: А зачем свой велосипед? +A: ((https://pechatnov.at.yandex-team.ru/67 Ответ в этом посте)). + +Q: А почему вот здесь плохо написано, надо же по-другому? +A: Код несколько раз переписывался и согласовывался ((https://st.yandex-team.ru/IGNIETFERRO-973 более полугода)). А допиливать его внутреннюю реализацию после коммита никто не мешает и дальше. + + +Сигнатуры и эквиваленты: + + +```cpp +//! In all comments variables ending with '_' +//! are considered as invisible for {...} block. + +//! Usage: for (auto [i, x] : Enumerate(container)) {...} +//! Equivalent: { std::size_t i_ = 0; for (auto& x : container) { const std::size_t i = i_; {...}; ++i_; }} +template <typename TContainerOrRef> +auto Enumerate(TContainerOrRef&& container); + +//! Usage: for (auto x : Map(mapperFunc, container)) {...} +//! Equivalent: for (auto iter_ = std::begin(container); iter_ != std::end(container); ++iter_) { +//! auto x = mapperFunc(*iter_); {...}; } +template <typename TMapper, typename TContainerOrRef> +auto Map(TMapper&& mapper, TContainerOrRef&& container); + +//! Usage: for (auto x : Filter(predicate, container)) {...} +//! Equivalent: for (auto x : container) { if (predicate(x)) {...}} +template <typename TPredicate, typename TContainerOrRef> +auto Filter(TPredicate&& predicate, TContainerOrRef&& container); + +//! Usage: for (auto [ai, bi] : Zip(a, b)) {...} +//! Equivalent: { auto ia_ = std::begin(a); auto ib_ = std::begin(b); +//! for (; ia_ != std::end(a) && ib_ != std::end(b); ++ia_, ++ib_) { +//! auto&& ai = *ia_; auto&& bi = *ib_; {...} +//! }} +template <typename... TContainers> +auto Zip(TContainers&&... containers); + +//! Usage: for (auto x : Reversed(container)) {...} +//! Equivalent: for (auto iter_ = std::rbegin(container); iter_ != std::rend(container); ++iter_) { +//! auto x = *iter_; {...}} +template <typename TContainerOrRef> +auto Reversed(TContainerOrRef&& container); + +//! Usage: for (auto x : Concatenate(a, b)) {...} +//! Equivalent: { for (auto x : a) {...} for (auto x : b) {...} } +//! (if there is no static variables in {...}) +template <typename TFirstContainer, typename... TContainers> +auto Concatenate(TFirstContainer&& container, TContainers&&... containers); + +//! Usage: for (auto [ai, bi] : CartesianProduct(a, b)) {...} +//! Equivalent: for (auto& ai : a) { for (auto& bi : b) {...} } +template <typename... TContainers> +auto CartesianProduct(TContainers&&... containers); +``` diff --git a/library/cpp/iterator/cartesian_product.cpp b/library/cpp/iterator/cartesian_product.cpp index c3ebfdcc33..40c5391134 100644 --- a/library/cpp/iterator/cartesian_product.cpp +++ b/library/cpp/iterator/cartesian_product.cpp @@ -1 +1 @@ -#include "cartesian_product.h" +#include "cartesian_product.h" diff --git a/library/cpp/iterator/cartesian_product.h b/library/cpp/iterator/cartesian_product.h index 3ef70339a2..afeced9995 100644 --- a/library/cpp/iterator/cartesian_product.h +++ b/library/cpp/iterator/cartesian_product.h @@ -1,115 +1,115 @@ -#pragma once - -#include <util/generic/store_policy.h> - -#include <tuple> - - -namespace NPrivate { - - template <typename... TContainers> - struct TCartesianMultiplier { - template <std::size_t... I> - struct TCartesianMultiplierWithIndex { - private: - using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>; - using TValue = std::tuple<decltype(*std::begin(std::declval<TContainers&>()))...>; - using TIteratorState = std::tuple<int, decltype(std::begin(std::declval<TContainers&>()))...>; - using TSentinelState = std::tuple<int, decltype(std::end(std::declval<TContainers&>()))...>; - - struct TIterator; - struct TSentinelCandidate { - TSentinelState Iterators_; - THolders* HoldersPtr_; - }; - using TSentinel = std::conditional_t<std::is_same_v<TIteratorState, TSentinelState>, - TIterator, TSentinelCandidate>; - - struct TIterator { - private: - //! Return value is true when iteration is not finished - template <std::size_t position = sizeof...(TContainers)> - void IncrementIteratorsTuple() { - auto& currentIterator = std::get<position>(Iterators_); - ++currentIterator; - - if (currentIterator != std::end(*std::get<position - 1>(*HoldersPtr_).Ptr())) { - return; - } else { - currentIterator = std::begin(*std::get<position - 1>(*HoldersPtr_).Ptr()); - if constexpr (position != 1) { - IncrementIteratorsTuple<position - 1>(); - } else { - std::get<0>(Iterators_) = 1; - } - } - } - public: - using difference_type = std::ptrdiff_t; - using value_type = TValue; - using pointer = TValue*; - using reference = TValue&; - using iterator_category = std::input_iterator_tag; - - TValue operator*() { - return {*std::get<I + 1>(Iterators_)...}; - } - TValue operator*() const { - return {*std::get<I + 1>(Iterators_)...}; - } - void operator++() { - IncrementIteratorsTuple(); - } - bool operator!=(const TSentinel& other) const { - // not finished iterator VS sentinel (most frequent case) - if (std::get<0>(Iterators_) != std::get<0>(other.Iterators_)) { - return true; - } - // do not compare sentinels and finished iterators - if (std::get<0>(other.Iterators_)) { - return false; - } - // compare not finished iterators - return ((std::get<I + 1>(Iterators_) != std::get<I + 1>(other.Iterators_)) || ...); - } - bool operator==(const TSentinel& other) const { - return !(*this != other); - } - - TIteratorState Iterators_; - THolders* HoldersPtr_; - }; - public: - using iterator = TIterator; - using const_iterator = TIterator; +#pragma once + +#include <util/generic/store_policy.h> + +#include <tuple> + + +namespace NPrivate { + + template <typename... TContainers> + struct TCartesianMultiplier { + template <std::size_t... I> + struct TCartesianMultiplierWithIndex { + private: + using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>; + using TValue = std::tuple<decltype(*std::begin(std::declval<TContainers&>()))...>; + using TIteratorState = std::tuple<int, decltype(std::begin(std::declval<TContainers&>()))...>; + using TSentinelState = std::tuple<int, decltype(std::end(std::declval<TContainers&>()))...>; + + struct TIterator; + struct TSentinelCandidate { + TSentinelState Iterators_; + THolders* HoldersPtr_; + }; + using TSentinel = std::conditional_t<std::is_same_v<TIteratorState, TSentinelState>, + TIterator, TSentinelCandidate>; + + struct TIterator { + private: + //! Return value is true when iteration is not finished + template <std::size_t position = sizeof...(TContainers)> + void IncrementIteratorsTuple() { + auto& currentIterator = std::get<position>(Iterators_); + ++currentIterator; + + if (currentIterator != std::end(*std::get<position - 1>(*HoldersPtr_).Ptr())) { + return; + } else { + currentIterator = std::begin(*std::get<position - 1>(*HoldersPtr_).Ptr()); + if constexpr (position != 1) { + IncrementIteratorsTuple<position - 1>(); + } else { + std::get<0>(Iterators_) = 1; + } + } + } + public: + using difference_type = std::ptrdiff_t; + using value_type = TValue; + using pointer = TValue*; + using reference = TValue&; + using iterator_category = std::input_iterator_tag; + + TValue operator*() { + return {*std::get<I + 1>(Iterators_)...}; + } + TValue operator*() const { + return {*std::get<I + 1>(Iterators_)...}; + } + void operator++() { + IncrementIteratorsTuple(); + } + bool operator!=(const TSentinel& other) const { + // not finished iterator VS sentinel (most frequent case) + if (std::get<0>(Iterators_) != std::get<0>(other.Iterators_)) { + return true; + } + // do not compare sentinels and finished iterators + if (std::get<0>(other.Iterators_)) { + return false; + } + // compare not finished iterators + return ((std::get<I + 1>(Iterators_) != std::get<I + 1>(other.Iterators_)) || ...); + } + bool operator==(const TSentinel& other) const { + return !(*this != other); + } + + TIteratorState Iterators_; + THolders* HoldersPtr_; + }; + public: + using iterator = TIterator; + using const_iterator = TIterator; using value_type = typename TIterator::value_type; using reference = typename TIterator::reference; using const_reference = typename TIterator::reference; - - TIterator begin() const { - bool isEmpty = !((std::begin(*std::get<I>(Holders_).Ptr()) != std::end(*std::get<I>(Holders_).Ptr())) && ...); - return {TIteratorState{int(isEmpty), std::begin(*std::get<I>(Holders_).Ptr())...}, &Holders_}; - } - - TSentinel end() const { - return {TSentinelState{1, std::end(*std::get<I>(Holders_).Ptr())...}, &Holders_}; - } - - mutable THolders Holders_; - }; - - template <std::size_t... I> - static auto CartesianMultiply(TContainers&&... containers, std::index_sequence<I...>) { - return TCartesianMultiplierWithIndex<I...>{{std::forward<TContainers>(containers)...}}; - } - }; - -} - -//! Usage: for (auto [ai, bi] : CartesianProduct(a, b)) {...} -//! Equivalent: for (auto& ai : a) { for (auto& bi : b) {...} } -template <typename... TContainers> -auto CartesianProduct(TContainers&&... containers) { - return NPrivate::TCartesianMultiplier<TContainers...>::CartesianMultiply( - std::forward<TContainers>(containers)..., std::make_index_sequence<sizeof...(TContainers)>{}); -} + + TIterator begin() const { + bool isEmpty = !((std::begin(*std::get<I>(Holders_).Ptr()) != std::end(*std::get<I>(Holders_).Ptr())) && ...); + return {TIteratorState{int(isEmpty), std::begin(*std::get<I>(Holders_).Ptr())...}, &Holders_}; + } + + TSentinel end() const { + return {TSentinelState{1, std::end(*std::get<I>(Holders_).Ptr())...}, &Holders_}; + } + + mutable THolders Holders_; + }; + + template <std::size_t... I> + static auto CartesianMultiply(TContainers&&... containers, std::index_sequence<I...>) { + return TCartesianMultiplierWithIndex<I...>{{std::forward<TContainers>(containers)...}}; + } + }; + +} + +//! Usage: for (auto [ai, bi] : CartesianProduct(a, b)) {...} +//! Equivalent: for (auto& ai : a) { for (auto& bi : b) {...} } +template <typename... TContainers> +auto CartesianProduct(TContainers&&... containers) { + return NPrivate::TCartesianMultiplier<TContainers...>::CartesianMultiply( + std::forward<TContainers>(containers)..., std::make_index_sequence<sizeof...(TContainers)>{}); +} diff --git a/library/cpp/iterator/concatenate.cpp b/library/cpp/iterator/concatenate.cpp index 4ca8e2e82d..1657b4246c 100644 --- a/library/cpp/iterator/concatenate.cpp +++ b/library/cpp/iterator/concatenate.cpp @@ -1 +1 @@ -#include "concatenate.h" +#include "concatenate.h" diff --git a/library/cpp/iterator/concatenate.h b/library/cpp/iterator/concatenate.h index 64d2cd451a..fe7e802aa0 100644 --- a/library/cpp/iterator/concatenate.h +++ b/library/cpp/iterator/concatenate.h @@ -1,136 +1,136 @@ -#pragma once - -#include <util/generic/store_policy.h> - -#include <iterator> -#include <tuple> - - -namespace NPrivate { - - template <typename TValue_, typename... TContainers> - struct TConcatenator { - template <std::size_t... I> - struct TConcatenatorWithIndex { - private: - using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>; - using TValue = TValue_; - using TIteratorState = std::tuple<decltype(std::begin(std::declval<TContainers&>()))...>; - using TSentinelState = std::tuple<decltype(std::end(std::declval<TContainers&>()))...>; - - struct TIterator; - struct TSentinelCandidate { - TSentinelState Iterators_; - std::size_t Position_; - THolders* HoldersPtr_; - }; - using TSentinel = std::conditional_t<std::is_same_v<TIteratorState, TSentinelState>, - TIterator, TSentinelCandidate>; - - struct TIterator { - private: - friend struct TConcatenatorWithIndex<I...>; - - // important, that it is a static function, compiler better optimizes such code - template <std::size_t index = 0, typename TMaybeConstIteratorState> - static TValue GetCurrentValue(std::size_t position, TMaybeConstIteratorState& iterators) { - if constexpr (index >= sizeof...(TContainers)) { - // never happened when use of iterator is correct - return *std::get<0>(iterators); - } else { - if (position == index) { - return *std::get<index>(iterators); - } else { - return GetCurrentValue<index + 1>(position, iterators); - } - } - } - - template <bool needIncrement, std::size_t index = 0> - void MaybeIncrementIteratorAndSkipExhaustedContainers() { - if constexpr (index >= sizeof...(TContainers)) { - return; - } else { - if (Position_ == index) { - if constexpr (needIncrement) { - ++std::get<index>(Iterators_); - } - if (!(std::get<index>(Iterators_) != std::end(*std::get<index>(*HoldersPtr_).Ptr()))) { - ++Position_; - MaybeIncrementIteratorAndSkipExhaustedContainers<false, index + 1>(); - } - } else { - MaybeIncrementIteratorAndSkipExhaustedContainers<needIncrement, index + 1>(); - } - } - } - public: - using difference_type = std::ptrdiff_t; - using value_type = TValue; - using pointer = std::remove_reference_t<TValue>*; - using reference = std::remove_reference_t<TValue>&; - using iterator_category = std::input_iterator_tag; - - TValue operator*() { - return GetCurrentValue(Position_, Iterators_); - } - TValue operator*() const { - return GetCurrentValue(Position_, Iterators_); - } +#pragma once + +#include <util/generic/store_policy.h> + +#include <iterator> +#include <tuple> + + +namespace NPrivate { + + template <typename TValue_, typename... TContainers> + struct TConcatenator { + template <std::size_t... I> + struct TConcatenatorWithIndex { + private: + using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>; + using TValue = TValue_; + using TIteratorState = std::tuple<decltype(std::begin(std::declval<TContainers&>()))...>; + using TSentinelState = std::tuple<decltype(std::end(std::declval<TContainers&>()))...>; + + struct TIterator; + struct TSentinelCandidate { + TSentinelState Iterators_; + std::size_t Position_; + THolders* HoldersPtr_; + }; + using TSentinel = std::conditional_t<std::is_same_v<TIteratorState, TSentinelState>, + TIterator, TSentinelCandidate>; + + struct TIterator { + private: + friend struct TConcatenatorWithIndex<I...>; + + // important, that it is a static function, compiler better optimizes such code + template <std::size_t index = 0, typename TMaybeConstIteratorState> + static TValue GetCurrentValue(std::size_t position, TMaybeConstIteratorState& iterators) { + if constexpr (index >= sizeof...(TContainers)) { + // never happened when use of iterator is correct + return *std::get<0>(iterators); + } else { + if (position == index) { + return *std::get<index>(iterators); + } else { + return GetCurrentValue<index + 1>(position, iterators); + } + } + } + + template <bool needIncrement, std::size_t index = 0> + void MaybeIncrementIteratorAndSkipExhaustedContainers() { + if constexpr (index >= sizeof...(TContainers)) { + return; + } else { + if (Position_ == index) { + if constexpr (needIncrement) { + ++std::get<index>(Iterators_); + } + if (!(std::get<index>(Iterators_) != std::end(*std::get<index>(*HoldersPtr_).Ptr()))) { + ++Position_; + MaybeIncrementIteratorAndSkipExhaustedContainers<false, index + 1>(); + } + } else { + MaybeIncrementIteratorAndSkipExhaustedContainers<needIncrement, index + 1>(); + } + } + } + public: + using difference_type = std::ptrdiff_t; + using value_type = TValue; + using pointer = std::remove_reference_t<TValue>*; + using reference = std::remove_reference_t<TValue>&; + using iterator_category = std::input_iterator_tag; + + TValue operator*() { + return GetCurrentValue(Position_, Iterators_); + } + TValue operator*() const { + return GetCurrentValue(Position_, Iterators_); + } TIterator& operator++() { - MaybeIncrementIteratorAndSkipExhaustedContainers<true>(); + MaybeIncrementIteratorAndSkipExhaustedContainers<true>(); return *this; - } - bool operator!=(const TSentinel& other) const { - // give compiler an opportunity to optimize sentinel case (-70% of time) - if (other.Position_ == sizeof...(TContainers)) { - return Position_ < sizeof...(TContainers); - } else { - return (Position_ != other.Position_ || - ((std::get<I>(Iterators_) != std::get<I>(other.Iterators_)) || ...)); - } - } - bool operator==(const TSentinel& other) const { - return !(*this != other); - } - - TIteratorState Iterators_; - std::size_t Position_; - THolders* HoldersPtr_; - }; - public: - using iterator = TIterator; - using const_iterator = TIterator; + } + bool operator!=(const TSentinel& other) const { + // give compiler an opportunity to optimize sentinel case (-70% of time) + if (other.Position_ == sizeof...(TContainers)) { + return Position_ < sizeof...(TContainers); + } else { + return (Position_ != other.Position_ || + ((std::get<I>(Iterators_) != std::get<I>(other.Iterators_)) || ...)); + } + } + bool operator==(const TSentinel& other) const { + return !(*this != other); + } + + TIteratorState Iterators_; + std::size_t Position_; + THolders* HoldersPtr_; + }; + public: + using iterator = TIterator; + using const_iterator = TIterator; using value_type = typename TIterator::value_type; using reference = typename TIterator::reference; using const_reference = typename TIterator::reference; - - TIterator begin() const { - TIterator iterator{TIteratorState{std::begin(*std::get<I>(Holders_).Ptr())...}, 0, &Holders_}; - iterator.template MaybeIncrementIteratorAndSkipExhaustedContainers<false>(); - return iterator; - } - - TSentinel end() const { - return {TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}, sizeof...(TContainers), &Holders_}; - } - - mutable THolders Holders_; - }; - - template <std::size_t... I> - static auto Concatenate(TContainers&&... containers, std::index_sequence<I...>) { - return TConcatenatorWithIndex<I...>{{std::forward<TContainers>(containers)...}}; - } - }; - -} - - -//! Usage: for (auto x : Concatenate(a, b)) {...} -template <typename TFirstContainer, typename... TContainers> -auto Concatenate(TFirstContainer&& container, TContainers&&... containers) { - return NPrivate::TConcatenator<decltype(*std::begin(container)), TFirstContainer, TContainers...>::Concatenate( - std::forward<TFirstContainer>(container), std::forward<TContainers>(containers)..., - std::make_index_sequence<sizeof...(TContainers) + 1>{}); -} + + TIterator begin() const { + TIterator iterator{TIteratorState{std::begin(*std::get<I>(Holders_).Ptr())...}, 0, &Holders_}; + iterator.template MaybeIncrementIteratorAndSkipExhaustedContainers<false>(); + return iterator; + } + + TSentinel end() const { + return {TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}, sizeof...(TContainers), &Holders_}; + } + + mutable THolders Holders_; + }; + + template <std::size_t... I> + static auto Concatenate(TContainers&&... containers, std::index_sequence<I...>) { + return TConcatenatorWithIndex<I...>{{std::forward<TContainers>(containers)...}}; + } + }; + +} + + +//! Usage: for (auto x : Concatenate(a, b)) {...} +template <typename TFirstContainer, typename... TContainers> +auto Concatenate(TFirstContainer&& container, TContainers&&... containers) { + return NPrivate::TConcatenator<decltype(*std::begin(container)), TFirstContainer, TContainers...>::Concatenate( + std::forward<TFirstContainer>(container), std::forward<TContainers>(containers)..., + std::make_index_sequence<sizeof...(TContainers) + 1>{}); +} diff --git a/library/cpp/iterator/enumerate.cpp b/library/cpp/iterator/enumerate.cpp index b195f0b31d..4118a67e85 100644 --- a/library/cpp/iterator/enumerate.cpp +++ b/library/cpp/iterator/enumerate.cpp @@ -1 +1 @@ -#include "enumerate.h" +#include "enumerate.h" diff --git a/library/cpp/iterator/enumerate.h b/library/cpp/iterator/enumerate.h index 2c83fb41bf..21eae25f39 100644 --- a/library/cpp/iterator/enumerate.h +++ b/library/cpp/iterator/enumerate.h @@ -1,82 +1,82 @@ -#pragma once - -#include <util/generic/store_policy.h> - -#include <limits> -#include <tuple> - - -namespace NPrivate { - - template <typename TContainer> - struct TEnumerator { - private: - using TStorage = TAutoEmbedOrPtrPolicy<TContainer>; - using TValue = std::tuple<const std::size_t, decltype(*std::begin(std::declval<TContainer&>()))>; - using TIteratorState = decltype(std::begin(std::declval<TContainer&>())); - using TSentinelState = decltype(std::end(std::declval<TContainer&>())); - - static constexpr bool TrivialSentinel = std::is_same_v<TIteratorState, TSentinelState>; - - struct TIterator; - struct TSentinelCandidate { - TSentinelState Iterator_; - }; - using TSentinel = std::conditional_t<TrivialSentinel, TIterator, TSentinelCandidate>; - - struct TIterator { - using difference_type = std::ptrdiff_t; - using value_type = TValue; - using pointer = TValue*; - using reference = TValue&; - using iterator_category = std::input_iterator_tag; - - TValue operator*() { - return {Index_, *Iterator_}; - } - TValue operator*() const { - return {Index_, *Iterator_}; - } - void operator++() { - ++Index_; - ++Iterator_; - } - bool operator!=(const TSentinel& other) const { - return Iterator_ != other.Iterator_; - } - bool operator==(const TSentinel& other) const { - return Iterator_ == other.Iterator_; - } - - std::size_t Index_; - TIteratorState Iterator_; - }; - public: - using iterator = TIterator; - using const_iterator = TIterator; +#pragma once + +#include <util/generic/store_policy.h> + +#include <limits> +#include <tuple> + + +namespace NPrivate { + + template <typename TContainer> + struct TEnumerator { + private: + using TStorage = TAutoEmbedOrPtrPolicy<TContainer>; + using TValue = std::tuple<const std::size_t, decltype(*std::begin(std::declval<TContainer&>()))>; + using TIteratorState = decltype(std::begin(std::declval<TContainer&>())); + using TSentinelState = decltype(std::end(std::declval<TContainer&>())); + + static constexpr bool TrivialSentinel = std::is_same_v<TIteratorState, TSentinelState>; + + struct TIterator; + struct TSentinelCandidate { + TSentinelState Iterator_; + }; + using TSentinel = std::conditional_t<TrivialSentinel, TIterator, TSentinelCandidate>; + + struct TIterator { + using difference_type = std::ptrdiff_t; + using value_type = TValue; + using pointer = TValue*; + using reference = TValue&; + using iterator_category = std::input_iterator_tag; + + TValue operator*() { + return {Index_, *Iterator_}; + } + TValue operator*() const { + return {Index_, *Iterator_}; + } + void operator++() { + ++Index_; + ++Iterator_; + } + bool operator!=(const TSentinel& other) const { + return Iterator_ != other.Iterator_; + } + bool operator==(const TSentinel& other) const { + return Iterator_ == other.Iterator_; + } + + std::size_t Index_; + TIteratorState Iterator_; + }; + public: + using iterator = TIterator; + using const_iterator = TIterator; using value_type = typename TIterator::value_type; using reference = typename TIterator::reference; using const_reference = typename TIterator::reference; - - TIterator begin() const { - return {0, std::begin(*Storage_.Ptr())}; - } - - TSentinel end() const { - if constexpr (TrivialSentinel) { - return TIterator{std::numeric_limits<std::size_t>::max(), std::end(*Storage_.Ptr())}; - } else { - return TSentinel{std::end(*Storage_.Ptr())}; - } - } - - mutable TStorage Storage_; - }; - -} - -//! Usage: for (auto [i, x] : Enumerate(container)) {...} -template <typename TContainerOrRef> -auto Enumerate(TContainerOrRef&& container) { - return NPrivate::TEnumerator<TContainerOrRef>{std::forward<TContainerOrRef>(container)}; -} + + TIterator begin() const { + return {0, std::begin(*Storage_.Ptr())}; + } + + TSentinel end() const { + if constexpr (TrivialSentinel) { + return TIterator{std::numeric_limits<std::size_t>::max(), std::end(*Storage_.Ptr())}; + } else { + return TSentinel{std::end(*Storage_.Ptr())}; + } + } + + mutable TStorage Storage_; + }; + +} + +//! Usage: for (auto [i, x] : Enumerate(container)) {...} +template <typename TContainerOrRef> +auto Enumerate(TContainerOrRef&& container) { + return NPrivate::TEnumerator<TContainerOrRef>{std::forward<TContainerOrRef>(container)}; +} diff --git a/library/cpp/iterator/filtering.cpp b/library/cpp/iterator/filtering.cpp index 8e1542f61b..c27612502c 100644 --- a/library/cpp/iterator/filtering.cpp +++ b/library/cpp/iterator/filtering.cpp @@ -1 +1 @@ -#include "filtering.h" +#include "filtering.h" diff --git a/library/cpp/iterator/filtering.h b/library/cpp/iterator/filtering.h index c28e3bc6c4..69099c2b70 100644 --- a/library/cpp/iterator/filtering.h +++ b/library/cpp/iterator/filtering.h @@ -1,10 +1,10 @@ #pragma once -#include <util/generic/iterator_range.h> -#include <util/generic/store_policy.h> +#include <util/generic/iterator_range.h> +#include <util/generic/store_policy.h> #include <iterator> - + template <class TIterator, class TCondition> class TFilteringIterator { public: @@ -19,7 +19,7 @@ public: TFilteringIterator(TIterator it, TIterator last, const TCondition& condition) : Iter(it) , Last(last) - , Condition(condition) + , Condition(condition) { Grep(); } @@ -56,47 +56,47 @@ private: TCondition Condition; }; - -template <class TContainer, class TCondition> -class TFilteringRange { - using TContainerStorage = TAutoEmbedOrPtrPolicy<TContainer>; - using TConditionStorage = TAutoEmbedOrPtrPolicy<TCondition>; - using TRawIterator = decltype(std::begin(std::declval<TContainer&>())); - using TConditionWrapper = std::reference_wrapper<std::remove_reference_t<TCondition>>; -public: + +template <class TContainer, class TCondition> +class TFilteringRange { + using TContainerStorage = TAutoEmbedOrPtrPolicy<TContainer>; + using TConditionStorage = TAutoEmbedOrPtrPolicy<TCondition>; + using TRawIterator = decltype(std::begin(std::declval<TContainer&>())); + using TConditionWrapper = std::reference_wrapper<std::remove_reference_t<TCondition>>; +public: //TODO: make TIterator typedef private - using TIterator = TFilteringIterator<TRawIterator, TConditionWrapper>; + using TIterator = TFilteringIterator<TRawIterator, TConditionWrapper>; - using iterator = TIterator; - using const_iterator = TIterator; + using iterator = TIterator; + using const_iterator = TIterator; using value_type = typename TIterator::value_type; using reference = typename TIterator::reference; - - TFilteringRange(TContainer&& container, TCondition&& predicate) - : Container(std::forward<TContainer>(container)) - , Condition(std::forward<TCondition>(predicate)) - {} - - TIterator begin() const { - return {std::begin(*Container.Ptr()), std::end(*Container.Ptr()), {*Condition.Ptr()}}; - } - - TIterator end() const { - return {std::end(*Container.Ptr()), std::end(*Container.Ptr()), {*Condition.Ptr()}}; - } - -private: - mutable TContainerStorage Container; - mutable TConditionStorage Condition; -}; - - + + TFilteringRange(TContainer&& container, TCondition&& predicate) + : Container(std::forward<TContainer>(container)) + , Condition(std::forward<TCondition>(predicate)) + {} + + TIterator begin() const { + return {std::begin(*Container.Ptr()), std::end(*Container.Ptr()), {*Condition.Ptr()}}; + } + + TIterator end() const { + return {std::end(*Container.Ptr()), std::end(*Container.Ptr()), {*Condition.Ptr()}}; + } + +private: + mutable TContainerStorage Container; + mutable TConditionStorage Condition; +}; + + template <class TIterator, class TCondition> auto MakeFilteringRange(TIterator begin, TIterator end, const TCondition& condition) { return MakeIteratorRange(TFilteringIterator<TIterator, TCondition>(begin, end, condition), TFilteringIterator<TIterator, TCondition>(end, end, condition)); } template <class TContainer, class TCondition> -auto MakeFilteringRange(TContainer&& container, TCondition&& condition) { - return TFilteringRange<TContainer, TCondition>(std::forward<TContainer>(container), std::forward<TCondition>(condition)); +auto MakeFilteringRange(TContainer&& container, TCondition&& condition) { + return TFilteringRange<TContainer, TCondition>(std::forward<TContainer>(container), std::forward<TCondition>(condition)); } diff --git a/library/cpp/iterator/functools.cpp b/library/cpp/iterator/functools.cpp index da2d0757ce..e63845acb6 100644 --- a/library/cpp/iterator/functools.cpp +++ b/library/cpp/iterator/functools.cpp @@ -1 +1 @@ -#include "functools.h" +#include "functools.h" diff --git a/library/cpp/iterator/functools.h b/library/cpp/iterator/functools.h index 57a0d66373..3a2548292c 100644 --- a/library/cpp/iterator/functools.h +++ b/library/cpp/iterator/functools.h @@ -1,57 +1,57 @@ -#pragma once - -#include "cartesian_product.h" -#include "concatenate.h" -#include "enumerate.h" -#include "filtering.h" -#include "mapped.h" -#include "zip.h" - -#include <util/generic/adaptor.h> -#include <util/generic/xrange.h> - -#include <tuple> -#include <algorithm> - - -namespace NFuncTools { - using ::Enumerate; - using ::Reversed; - using ::Zip; - using ::Concatenate; - using ::CartesianProduct; - - template <typename TValue> - auto Range(TValue from, TValue to, TValue step) { - return xrange(from, to, step); - } - - template <typename TValue> - auto Range(TValue from, TValue to) { - return xrange(from, to); - } - - template <typename TValue> - auto Range(TValue to) { - return xrange(to); - } - - //! Usage: for (i32 x : Map([](i32 x) { return x * x; }, a)) {...} - template <typename TMapper, typename TContainerOrRef> - auto Map(TMapper&& mapper, TContainerOrRef&& container) { - return ::MakeMappedRange(std::forward<TContainerOrRef>(container), std::forward<TMapper>(mapper)); - } - - //! Usage: for (auto i : Map<int>(floats)) {...} - template <typename TMapResult, typename TContainerOrRef> - auto Map(TContainerOrRef&& container) { - return Map([](const auto& x) { return TMapResult(x); }, std::forward<TContainerOrRef>(container)); - } - - //! Usage: for (i32 x : Filter(predicate, container)) {...} - template <typename TPredicate, typename TContainerOrRef> - auto Filter(TPredicate&& predicate, TContainerOrRef&& container) { - return ::MakeFilteringRange(std::forward<TContainerOrRef>(container), std::forward<TPredicate>(predicate)); - } - -} +#pragma once + +#include "cartesian_product.h" +#include "concatenate.h" +#include "enumerate.h" +#include "filtering.h" +#include "mapped.h" +#include "zip.h" + +#include <util/generic/adaptor.h> +#include <util/generic/xrange.h> + +#include <tuple> +#include <algorithm> + + +namespace NFuncTools { + using ::Enumerate; + using ::Reversed; + using ::Zip; + using ::Concatenate; + using ::CartesianProduct; + + template <typename TValue> + auto Range(TValue from, TValue to, TValue step) { + return xrange(from, to, step); + } + + template <typename TValue> + auto Range(TValue from, TValue to) { + return xrange(from, to); + } + + template <typename TValue> + auto Range(TValue to) { + return xrange(to); + } + + //! Usage: for (i32 x : Map([](i32 x) { return x * x; }, a)) {...} + template <typename TMapper, typename TContainerOrRef> + auto Map(TMapper&& mapper, TContainerOrRef&& container) { + return ::MakeMappedRange(std::forward<TContainerOrRef>(container), std::forward<TMapper>(mapper)); + } + + //! Usage: for (auto i : Map<int>(floats)) {...} + template <typename TMapResult, typename TContainerOrRef> + auto Map(TContainerOrRef&& container) { + return Map([](const auto& x) { return TMapResult(x); }, std::forward<TContainerOrRef>(container)); + } + + //! Usage: for (i32 x : Filter(predicate, container)) {...} + template <typename TPredicate, typename TContainerOrRef> + auto Filter(TPredicate&& predicate, TContainerOrRef&& container) { + return ::MakeFilteringRange(std::forward<TContainerOrRef>(container), std::forward<TPredicate>(predicate)); + } + +} diff --git a/library/cpp/iterator/mapped.cpp b/library/cpp/iterator/mapped.cpp index 047f38c58f..201f95f10e 100644 --- a/library/cpp/iterator/mapped.cpp +++ b/library/cpp/iterator/mapped.cpp @@ -1 +1 @@ -#include "mapped.h" +#include "mapped.h" diff --git a/library/cpp/iterator/mapped.h b/library/cpp/iterator/mapped.h index 6c5e763184..486efd0899 100644 --- a/library/cpp/iterator/mapped.h +++ b/library/cpp/iterator/mapped.h @@ -1,33 +1,33 @@ #pragma once #include <util/generic/iterator_range.h> -#include <util/generic/store_policy.h> - -#include <iterator> - +#include <util/generic/store_policy.h> +#include <iterator> + + namespace NIteratorPrivate { - template <class TIterator> - constexpr bool HasRandomAccess() { - return std::is_same_v<typename std::iterator_traits<TIterator>::iterator_category, - std::random_access_iterator_tag>; - } -}; - - + template <class TIterator> + constexpr bool HasRandomAccess() { + return std::is_same_v<typename std::iterator_traits<TIterator>::iterator_category, + std::random_access_iterator_tag>; + } +}; + + template <class TIterator, class TMapper> class TMappedIterator { protected: using TSelf = TMappedIterator<TIterator, TMapper>; using TSrcPointerType = typename std::iterator_traits<TIterator>::reference; using TValue = typename std::invoke_result_t<TMapper, TSrcPointerType>; -public: +public: using difference_type = std::ptrdiff_t; using value_type = TValue; using reference = TValue&; using pointer = std::remove_reference_t<TValue>*; using iterator_category = std::conditional_t<NIteratorPrivate::HasRandomAccess<TIterator>(), - std::random_access_iterator_tag, std::input_iterator_tag>; + std::random_access_iterator_tag, std::input_iterator_tag>; TMappedIterator(TIterator it, TMapper mapper) : Iter(it) @@ -43,9 +43,9 @@ public: --Iter; return *this; } - TValue operator*() { - return Mapper((*Iter)); - } + TValue operator*() { + return Mapper((*Iter)); + } TValue operator*() const { return Mapper((*Iter)); } @@ -92,13 +92,13 @@ private: TMapper Mapper; }; - + template <class TContainer, class TMapper> -class TInputMappedRange { -protected: - using TContainerStorage = TAutoEmbedOrPtrPolicy<TContainer>; - using TMapperStorage = TAutoEmbedOrPtrPolicy<TMapper>; - using TMapperWrapper = std::reference_wrapper<std::remove_reference_t<TMapper>>; +class TInputMappedRange { +protected: + using TContainerStorage = TAutoEmbedOrPtrPolicy<TContainer>; + using TMapperStorage = TAutoEmbedOrPtrPolicy<TMapper>; + using TMapperWrapper = std::reference_wrapper<std::remove_reference_t<TMapper>>; using TInternalIterator = decltype(std::begin(std::declval<TContainer&>())); using TIterator = TMappedIterator<TInternalIterator, TMapperWrapper>; public: @@ -108,68 +108,68 @@ public: using reference = typename TIterator::reference; using const_reference = typename TIterator::reference; - TInputMappedRange(TContainer&& container, TMapper&& mapper) - : Container(std::forward<TContainer>(container)) - , Mapper(std::forward<TMapper>(mapper)) + TInputMappedRange(TContainer&& container, TMapper&& mapper) + : Container(std::forward<TContainer>(container)) + , Mapper(std::forward<TMapper>(mapper)) { } TIterator begin() const { - return {std::begin(*Container.Ptr()), {*Mapper.Ptr()}}; + return {std::begin(*Container.Ptr()), {*Mapper.Ptr()}}; } TIterator end() const { - return {std::end(*Container.Ptr()), {*Mapper.Ptr()}}; + return {std::end(*Container.Ptr()), {*Mapper.Ptr()}}; } bool empty() const { return std::begin(*Container.Ptr()) == std::end(*Container.Ptr()); } -protected: - mutable TContainerStorage Container; - mutable TMapperStorage Mapper; -}; - +protected: + mutable TContainerStorage Container; + mutable TMapperStorage Mapper; +}; -template <class TContainer, class TMapper> -class TRandomAccessMappedRange : public TInputMappedRange<TContainer, TMapper> { - using TBase = TInputMappedRange<TContainer, TMapper>; + +template <class TContainer, class TMapper> +class TRandomAccessMappedRange : public TInputMappedRange<TContainer, TMapper> { + using TBase = TInputMappedRange<TContainer, TMapper>; using TInternalIterator = typename TBase::TInternalIterator; using TIterator = typename TBase::TIterator; -public: - using iterator = typename TBase::iterator; - using const_iterator = typename TBase::const_iterator; - using value_type = typename TBase::value_type; - using reference = typename TBase::reference; - using const_reference = typename TBase::const_reference; - - using difference_type = typename std::iterator_traits<iterator>::difference_type; - using size_type = std::size_t; - - TRandomAccessMappedRange(TContainer&& container, TMapper&& mapper) - : TBase(std::forward<TContainer>(container), std::forward<TMapper>(mapper)) - { - } - - using TBase::begin; - using TBase::end; +public: + using iterator = typename TBase::iterator; + using const_iterator = typename TBase::const_iterator; + using value_type = typename TBase::value_type; + using reference = typename TBase::reference; + using const_reference = typename TBase::const_reference; + + using difference_type = typename std::iterator_traits<iterator>::difference_type; + using size_type = std::size_t; + + TRandomAccessMappedRange(TContainer&& container, TMapper&& mapper) + : TBase(std::forward<TContainer>(container), std::forward<TMapper>(mapper)) + { + } + + using TBase::begin; + using TBase::end; using TBase::empty; - + size_type size() const { - return std::end(*this->Container.Ptr()) - std::begin(*this->Container.Ptr()); + return std::end(*this->Container.Ptr()) - std::begin(*this->Container.Ptr()); } const_reference operator[](size_t at) const { - Y_ASSERT(at < this->size()); + Y_ASSERT(at < this->size()); - return *(this->begin() + at); + return *(this->begin() + at); } reference operator[](size_t at) { - Y_ASSERT(at < this->size()); + Y_ASSERT(at < this->size()); - return *(this->begin() + at); + return *(this->begin() + at); } }; @@ -184,10 +184,10 @@ auto MakeMappedRange(TIterator begin, TIterator end, TMapper mapper) { } template <class TContainer, class TMapper> -auto MakeMappedRange(TContainer&& container, TMapper&& mapper) { +auto MakeMappedRange(TContainer&& container, TMapper&& mapper) { if constexpr (NIteratorPrivate::HasRandomAccess<decltype(std::begin(container))>()) { - return TRandomAccessMappedRange<TContainer, TMapper>(std::forward<TContainer>(container), std::forward<TMapper>(mapper)); - } else { - return TInputMappedRange<TContainer, TMapper>(std::forward<TContainer>(container), std::forward<TMapper>(mapper)); - } + return TRandomAccessMappedRange<TContainer, TMapper>(std::forward<TContainer>(container), std::forward<TMapper>(mapper)); + } else { + return TInputMappedRange<TContainer, TMapper>(std::forward<TContainer>(container), std::forward<TMapper>(mapper)); + } } diff --git a/library/cpp/iterator/ut/functools_ut.cpp b/library/cpp/iterator/ut/functools_ut.cpp index 2dee9a55c8..cd39158bd2 100644 --- a/library/cpp/iterator/ut/functools_ut.cpp +++ b/library/cpp/iterator/ut/functools_ut.cpp @@ -1,603 +1,603 @@ #include <library/cpp/iterator/functools.h> - + #include <library/cpp/testing/gtest/gtest.h> - -#include <util/generic/vector.h> -#include <util/generic/xrange.h> -#include <util/generic/adaptor.h> - -#include <set> - -// default-win-x86_64-release compiler can't decompose tuple to structure binding (02.03.2019) -#ifndef _WINDOWS -# define FOR_DISPATCH_2(i, j, r) \ - for (auto [i, j] : r) -# define FOR_DISPATCH_3(i, j, k, r) \ - for (auto [i, j, k] : r) -#else -# define FOR_DISPATCH_2(i, j, r) \ - for (auto __t_##i##_##j : r) \ - if (auto& i = std::get<0>(__t_##i##_##j); true) \ - if (auto& j = std::get<1>(__t_##i##_##j); true) -# define FOR_DISPATCH_3(i, j, k, r) \ - for (auto __t_##i##_##j##_##k : r) \ - if (auto& i = std::get<0>(__t_##i##_##j##_##k); true) \ - if (auto& j = std::get<1>(__t_##i##_##j##_##k); true) \ - if (auto& k = std::get<2>(__t_##i##_##j##_##k); true) -#endif - -using namespace NFuncTools; - - - template <typename TContainer> - auto ToVector(TContainer&& container) { - return std::vector{container.begin(), container.end()}; - } - - template <typename TContainerObjOrRef> - void TestViewCompileability(TContainerObjOrRef&& container) { - using TContainer = std::decay_t<TContainerObjOrRef>; - using TIterator = typename TContainer::iterator; - - static_assert(std::is_same_v<decltype(container.begin()), TIterator>); - - // iterator_traits must work! - using difference_type = typename std::iterator_traits<TIterator>::difference_type; - using value_type = typename std::iterator_traits<TIterator>::value_type; - using reference = typename std::iterator_traits<TIterator>::reference; - using pointer = typename std::iterator_traits<TIterator>::pointer; - - { - // operator assignment - auto it = container.begin(); - it = container.end(); - it = std::move(container.begin()); - // operator copying - auto it2 = it; - Y_UNUSED(it2); - auto it3 = std::move(it); - Y_UNUSED(it3); - Y_UNUSED(*it3); + +#include <util/generic/vector.h> +#include <util/generic/xrange.h> +#include <util/generic/adaptor.h> + +#include <set> + +// default-win-x86_64-release compiler can't decompose tuple to structure binding (02.03.2019) +#ifndef _WINDOWS +# define FOR_DISPATCH_2(i, j, r) \ + for (auto [i, j] : r) +# define FOR_DISPATCH_3(i, j, k, r) \ + for (auto [i, j, k] : r) +#else +# define FOR_DISPATCH_2(i, j, r) \ + for (auto __t_##i##_##j : r) \ + if (auto& i = std::get<0>(__t_##i##_##j); true) \ + if (auto& j = std::get<1>(__t_##i##_##j); true) +# define FOR_DISPATCH_3(i, j, k, r) \ + for (auto __t_##i##_##j##_##k : r) \ + if (auto& i = std::get<0>(__t_##i##_##j##_##k); true) \ + if (auto& j = std::get<1>(__t_##i##_##j##_##k); true) \ + if (auto& k = std::get<2>(__t_##i##_##j##_##k); true) +#endif + +using namespace NFuncTools; + + + template <typename TContainer> + auto ToVector(TContainer&& container) { + return std::vector{container.begin(), container.end()}; + } + + template <typename TContainerObjOrRef> + void TestViewCompileability(TContainerObjOrRef&& container) { + using TContainer = std::decay_t<TContainerObjOrRef>; + using TIterator = typename TContainer::iterator; + + static_assert(std::is_same_v<decltype(container.begin()), TIterator>); + + // iterator_traits must work! + using difference_type = typename std::iterator_traits<TIterator>::difference_type; + using value_type = typename std::iterator_traits<TIterator>::value_type; + using reference = typename std::iterator_traits<TIterator>::reference; + using pointer = typename std::iterator_traits<TIterator>::pointer; + + { + // operator assignment + auto it = container.begin(); + it = container.end(); + it = std::move(container.begin()); + // operator copying + auto it2 = it; + Y_UNUSED(it2); + auto it3 = std::move(it); + Y_UNUSED(it3); + Y_UNUSED(*it3); EXPECT_TRUE(it3 == it3); EXPECT_FALSE(it3 != it3); - // const TIterator - const auto it4 = it3; - Y_UNUSED(*it4); + // const TIterator + const auto it4 = it3; + Y_UNUSED(*it4); EXPECT_TRUE(it4 == it4); EXPECT_FALSE(it4 != it4); EXPECT_TRUE(it3 == it4); EXPECT_TRUE(it4 == it3); EXPECT_FALSE(it3 != it4); EXPECT_FALSE(it4 != it3); - } - - auto it = container.begin(); - - // sanity check for types - using TConstReference = const std::remove_reference_t<reference>&; - TConstReference ref = *it; - Y_UNUSED(ref); - (void) static_cast<value_type>(*it); - (void) static_cast<difference_type>(1); - if constexpr (std::is_reference_v<decltype(*it)>) { - pointer ptr = &*it; - Y_UNUSED(ptr); - } - - // std compatibility - ToVector(container); - - // const iterators - [](const auto& cont) { - auto constBeginIterator = cont.begin(); - auto constEndIterator = cont.end(); - static_assert(std::is_same_v<decltype(constBeginIterator), typename TContainer::const_iterator>); - Y_UNUSED(constBeginIterator); - Y_UNUSED(constEndIterator); - }(container); - } - - struct TTestSentinel {}; - struct TTestIterator { - int operator*() { - return X; - } - void operator++() { - ++X; - } - bool operator!=(const TTestSentinel&) const { - return X < 3; - } - - int X; - }; - - // container with minimal interface - auto MakeMinimalisticContainer() { - return MakeIteratorRange(TTestIterator{}, TTestSentinel{}); - } - - + } + + auto it = container.begin(); + + // sanity check for types + using TConstReference = const std::remove_reference_t<reference>&; + TConstReference ref = *it; + Y_UNUSED(ref); + (void) static_cast<value_type>(*it); + (void) static_cast<difference_type>(1); + if constexpr (std::is_reference_v<decltype(*it)>) { + pointer ptr = &*it; + Y_UNUSED(ptr); + } + + // std compatibility + ToVector(container); + + // const iterators + [](const auto& cont) { + auto constBeginIterator = cont.begin(); + auto constEndIterator = cont.end(); + static_assert(std::is_same_v<decltype(constBeginIterator), typename TContainer::const_iterator>); + Y_UNUSED(constBeginIterator); + Y_UNUSED(constEndIterator); + }(container); + } + + struct TTestSentinel {}; + struct TTestIterator { + int operator*() { + return X; + } + void operator++() { + ++X; + } + bool operator!=(const TTestSentinel&) const { + return X < 3; + } + + int X; + }; + + // container with minimal interface + auto MakeMinimalisticContainer() { + return MakeIteratorRange(TTestIterator{}, TTestSentinel{}); + } + + TEST(FuncTools, CompileRange) { - TestViewCompileability(Range(19)); - TestViewCompileability(Range(10, 19)); - TestViewCompileability(Range(10, 19, 2)); - } - - + TestViewCompileability(Range(19)); + TestViewCompileability(Range(10, 19)); + TestViewCompileability(Range(10, 19, 2)); + } + + TEST(FuncTools, Enumerate) { TVector<size_t> a = {1, 2, 4}; TVector<size_t> b; TVector<size_t> c = {1}; - for (auto& v : {a, b, c}) { + for (auto& v : {a, b, c}) { size_t j = 0; - FOR_DISPATCH_2(i, x, Enumerate(v)) { + FOR_DISPATCH_2(i, x, Enumerate(v)) { EXPECT_EQ(v[i], x); EXPECT_EQ(i, j++); EXPECT_LT(i, v.size()); - } + } EXPECT_EQ(j, v.size()); - } - + } + TVector<size_t> d = {0, 0, 0}; - FOR_DISPATCH_2(i, x, Enumerate(d)) { - x = i; - } + FOR_DISPATCH_2(i, x, Enumerate(d)) { + x = i; + } EXPECT_THAT( d, testing::ElementsAre(0u, 1u, 2u) ); - } - + } + TEST(FuncTools, EnumerateTemporary) { TVector<size_t> a = {1, 2, 4}; TVector<size_t> b; TVector<size_t> c = {1}; - for (auto& v : {a, b, c}) { + for (auto& v : {a, b, c}) { size_t j = 0; - FOR_DISPATCH_2(i, x, Enumerate(TVector(v))) { + FOR_DISPATCH_2(i, x, Enumerate(TVector(v))) { EXPECT_EQ(v[i], x); EXPECT_EQ(i, j++); EXPECT_LT(i, v.size()); - } + } EXPECT_EQ(j, v.size()); - } - + } + FOR_DISPATCH_2(i, x, Enumerate(TVector<size_t>{1, 2, 3})) { EXPECT_EQ(i + 1, x); - } - } - + } + } + TEST(FuncTools, CompileEnumerate) { - auto container = std::vector{1, 2, 3}; - TestViewCompileability(Enumerate(container)); - const auto constContainer = std::vector{1, 2, 3}; - TestViewCompileability(Enumerate(constContainer)); - const int arrayContainer[] = {1, 2, 3}; - TestViewCompileability(Enumerate(arrayContainer)); - - std::vector<std::pair<int, int>> res; - FOR_DISPATCH_2(i, x, Enumerate(MakeMinimalisticContainer())) { - res.push_back({i, x}); - } + auto container = std::vector{1, 2, 3}; + TestViewCompileability(Enumerate(container)); + const auto constContainer = std::vector{1, 2, 3}; + TestViewCompileability(Enumerate(constContainer)); + const int arrayContainer[] = {1, 2, 3}; + TestViewCompileability(Enumerate(arrayContainer)); + + std::vector<std::pair<int, int>> res; + FOR_DISPATCH_2(i, x, Enumerate(MakeMinimalisticContainer())) { + res.push_back({i, x}); + } EXPECT_EQ(res, (std::vector<std::pair<int, int>>{ - {0, 0}, {1, 1}, {2, 2}, - })); - } - + {0, 0}, {1, 1}, {2, 2}, + })); + } + TEST(FuncTools, Zip) { TVector<std::pair<TVector<size_t>, TVector<size_t>>> ts = { - {{1, 2, 3}, {4, 5, 6}}, - {{1, 2, 3}, {4, 5, 6, 7}}, - {{1, 2, 3, 4}, {4, 5, 6}}, - {{1, 2, 3, 4}, {}}, - }; - - FOR_DISPATCH_2(a, b, ts) { + {{1, 2, 3}, {4, 5, 6}}, + {{1, 2, 3}, {4, 5, 6, 7}}, + {{1, 2, 3, 4}, {4, 5, 6}}, + {{1, 2, 3, 4}, {}}, + }; + + FOR_DISPATCH_2(a, b, ts) { size_t k = 0; - FOR_DISPATCH_2(i, j, Zip(a, b)) { + FOR_DISPATCH_2(i, j, Zip(a, b)) { EXPECT_EQ(++k, i); EXPECT_EQ(i + 3, j); - } + } EXPECT_EQ(k, Min(a.size(), b.size())); - } - } - + } + } + TEST(FuncTools, ZipReference) { - TVector a = {0, 1, 2}; - TVector b = {2, 1, 0, -1}; - FOR_DISPATCH_2(ai, bi, Zip(a, b)) { - ai = bi; - } + TVector a = {0, 1, 2}; + TVector b = {2, 1, 0, -1}; + FOR_DISPATCH_2(ai, bi, Zip(a, b)) { + ai = bi; + } EXPECT_THAT( a, testing::ElementsAre(2u, 1u, 0u) ); - } - + } + TEST(FuncTools, Zip3) { - TVector<std::tuple<TVector<i32>, TVector<i32>, TVector<i32>>> ts = { - {{1, 2, 3}, {4, 5, 6}, {11, 3}}, - {{1, 2, 3}, {4, 5, 6, 7}, {9, 0}}, - {{1, 2, 3, 4}, {9}, {4, 5, 6}}, - {{1, 2, 3, 4}, {1}, {}}, - {{}, {1}, {1, 2, 3, 4}}, - }; - - FOR_DISPATCH_3(a, b, c, ts) { - TVector<std::tuple<i32, i32, i32>> e; - for (size_t j = 0; j < a.size() && j < b.size() && j < c.size(); ++j) { - e.push_back({a[j], b[j], c[j]}); - } - - TVector<std::tuple<i32, i32, i32>> f; - FOR_DISPATCH_3(ai, bi, ci, Zip(a, b, c)) { - f.push_back({ai, bi, ci}); - } - + TVector<std::tuple<TVector<i32>, TVector<i32>, TVector<i32>>> ts = { + {{1, 2, 3}, {4, 5, 6}, {11, 3}}, + {{1, 2, 3}, {4, 5, 6, 7}, {9, 0}}, + {{1, 2, 3, 4}, {9}, {4, 5, 6}}, + {{1, 2, 3, 4}, {1}, {}}, + {{}, {1}, {1, 2, 3, 4}}, + }; + + FOR_DISPATCH_3(a, b, c, ts) { + TVector<std::tuple<i32, i32, i32>> e; + for (size_t j = 0; j < a.size() && j < b.size() && j < c.size(); ++j) { + e.push_back({a[j], b[j], c[j]}); + } + + TVector<std::tuple<i32, i32, i32>> f; + FOR_DISPATCH_3(ai, bi, ci, Zip(a, b, c)) { + f.push_back({ai, bi, ci}); + } + EXPECT_EQ(e, f); - } - } - + } + } + TEST(FuncTools, CompileZip) { - auto container = std::vector{1, 2, 3}; - TestViewCompileability(Zip(container)); - TestViewCompileability(Zip(container, container, container)); - const auto constContainer = std::vector{1, 2, 3}; - TestViewCompileability(Zip(constContainer, constContainer)); - const int arrayContainer[] = {1, 2, 3}; - TestViewCompileability(Zip(arrayContainer, arrayContainer)); - - std::vector<std::pair<int, int>> res; - FOR_DISPATCH_2(a, b, Zip(MakeMinimalisticContainer(), container)) { - res.push_back({a, b}); - } + auto container = std::vector{1, 2, 3}; + TestViewCompileability(Zip(container)); + TestViewCompileability(Zip(container, container, container)); + const auto constContainer = std::vector{1, 2, 3}; + TestViewCompileability(Zip(constContainer, constContainer)); + const int arrayContainer[] = {1, 2, 3}; + TestViewCompileability(Zip(arrayContainer, arrayContainer)); + + std::vector<std::pair<int, int>> res; + FOR_DISPATCH_2(a, b, Zip(MakeMinimalisticContainer(), container)) { + res.push_back({a, b}); + } EXPECT_EQ(res, (std::vector<std::pair<int, int>>{ - {0, 1}, {1, 2}, {2, 3}, - })); - } - + {0, 1}, {1, 2}, {2, 3}, + })); + } + TEST(FuncTools, Filter) { - TVector<TVector<i32>> ts = { - {}, - {1}, - {2}, - {1, 2}, - {2, 1}, - {1, 2, 3, 4, 5, 6, 7}, - }; - - auto pred = [](i32 x) -> bool { return x & 1; }; - - for (auto& a : ts) { - TVector<i32> b; - for (i32 x : a) { - if (pred(x)) { - b.push_back(x); - } - } - - TVector<i32> c; - for (i32 x : Filter(pred, a)) { - c.push_back(x); - } - + TVector<TVector<i32>> ts = { + {}, + {1}, + {2}, + {1, 2}, + {2, 1}, + {1, 2, 3, 4, 5, 6, 7}, + }; + + auto pred = [](i32 x) -> bool { return x & 1; }; + + for (auto& a : ts) { + TVector<i32> b; + for (i32 x : a) { + if (pred(x)) { + b.push_back(x); + } + } + + TVector<i32> c; + for (i32 x : Filter(pred, a)) { + c.push_back(x); + } + EXPECT_EQ(b, c); - } - } - + } + } + TEST(FuncTools, CompileFilter) { - auto container = std::vector{1, 2, 3}; - auto isOdd = [](int x) { return bool(x & 1); }; - TestViewCompileability(Filter(isOdd, container)); - const int arrayContainer[] = {1, 2, 3}; - TestViewCompileability(Filter(isOdd, arrayContainer)); - } - + auto container = std::vector{1, 2, 3}; + auto isOdd = [](int x) { return bool(x & 1); }; + TestViewCompileability(Filter(isOdd, container)); + const int arrayContainer[] = {1, 2, 3}; + TestViewCompileability(Filter(isOdd, arrayContainer)); + } + TEST(FuncTools, Map) { - TVector<TVector<i32>> ts = { - {}, - {1}, - {1, 2}, - {1, 2, 3, 4, 5, 6, 7}, - }; - - auto f = [](i32 x) { return x * x; }; - - for (auto& a : ts) { - TVector<i32> b; - for (i32 x : a) { - b.push_back(f(x)); - } - - TVector<i32> c; - for (i32 x : Map(f, a)) { - c.push_back(x); - } - + TVector<TVector<i32>> ts = { + {}, + {1}, + {1, 2}, + {1, 2, 3, 4, 5, 6, 7}, + }; + + auto f = [](i32 x) { return x * x; }; + + for (auto& a : ts) { + TVector<i32> b; + for (i32 x : a) { + b.push_back(f(x)); + } + + TVector<i32> c; + for (i32 x : Map(f, a)) { + c.push_back(x); + } + EXPECT_EQ(b, c); - } - - TVector floats = {1.4, 4.1, 13.9}; - TVector ints = {1, 4, 13}; - TVector<float> roundedFloats = {1, 4, 13}; - TVector<int> res; - TVector<float> resFloat; - for (auto i : Map<int>(floats)) { - res.push_back(i); - } - for (auto i : Map<float>(Map<int>(floats))) { - resFloat.push_back(i); - } + } + + TVector floats = {1.4, 4.1, 13.9}; + TVector ints = {1, 4, 13}; + TVector<float> roundedFloats = {1, 4, 13}; + TVector<int> res; + TVector<float> resFloat; + for (auto i : Map<int>(floats)) { + res.push_back(i); + } + for (auto i : Map<float>(Map<int>(floats))) { + resFloat.push_back(i); + } EXPECT_EQ(ints, res); EXPECT_EQ(roundedFloats, resFloat); - } - + } + TEST(FuncTools, CompileMap) { - auto container = std::vector{1, 2, 3}; - auto sqr = [](int x) { return x * x; }; - TestViewCompileability(Map(sqr, container)); - const int arrayContainer[] = {1, 2, 3}; - TestViewCompileability(Map(sqr, arrayContainer)); - } - + auto container = std::vector{1, 2, 3}; + auto sqr = [](int x) { return x * x; }; + TestViewCompileability(Map(sqr, container)); + const int arrayContainer[] = {1, 2, 3}; + TestViewCompileability(Map(sqr, arrayContainer)); + } + TEST(FuncTools, MapRandomAccess) { - auto sqr = [](int x) { return x * x; }; - { - auto container = std::vector{1, 2, 3}; - auto mapped = Map(sqr, container); + auto sqr = [](int x) { return x * x; }; + { + auto container = std::vector{1, 2, 3}; + auto mapped = Map(sqr, container); static_assert( std::is_same_v<decltype(mapped)::iterator::iterator_category, std::random_access_iterator_tag> ); - } - { - auto container = std::set<int>{1, 2, 3}; - auto mapped = Map(sqr, container); + } + { + auto container = std::set<int>{1, 2, 3}; + auto mapped = Map(sqr, container); static_assert( std::is_same_v<decltype(mapped)::iterator::iterator_category, std::input_iterator_tag> ); - } - } - + } + } + TEST(FuncTools, CartesianProduct) { - TVector<std::pair<TVector<i32>, TVector<i32>>> ts = { - {{1, 2, 3}, {4, 5, 6}}, - {{1, 2, 3}, {4, 5, 6, 7}}, - {{1, 2, 3, 4}, {4, 5, 6}}, - {{1, 2, 3, 4}, {}}, - {{}, {1, 2, 3, 4}}, - }; - - for (auto [a, b] : ts) { - TVector<std::pair<i32, i32>> c; - for (auto ai : a) { - for (auto bi : b) { - c.push_back({ai, bi}); - } - } - - TVector<std::pair<i32, i32>> d; - FOR_DISPATCH_2(ai, bi, CartesianProduct(a, b)) { - d.push_back({ai, bi}); - } - + TVector<std::pair<TVector<i32>, TVector<i32>>> ts = { + {{1, 2, 3}, {4, 5, 6}}, + {{1, 2, 3}, {4, 5, 6, 7}}, + {{1, 2, 3, 4}, {4, 5, 6}}, + {{1, 2, 3, 4}, {}}, + {{}, {1, 2, 3, 4}}, + }; + + for (auto [a, b] : ts) { + TVector<std::pair<i32, i32>> c; + for (auto ai : a) { + for (auto bi : b) { + c.push_back({ai, bi}); + } + } + + TVector<std::pair<i32, i32>> d; + FOR_DISPATCH_2(ai, bi, CartesianProduct(a, b)) { + d.push_back({ai, bi}); + } + EXPECT_EQ(c, d); - } - - { - TVector<TVector<int>> g = {{}, {}}; - TVector h = {10, 11, 12}; - FOR_DISPATCH_2(gi, i, CartesianProduct(g, h)) { - gi.push_back(i); - } + } + + { + TVector<TVector<int>> g = {{}, {}}; + TVector h = {10, 11, 12}; + FOR_DISPATCH_2(gi, i, CartesianProduct(g, h)) { + gi.push_back(i); + } EXPECT_EQ(g[0], h); EXPECT_EQ(g[1], h); - } - } - + } + } + TEST(FuncTools, CartesianProduct3) { - TVector<std::tuple<TVector<i32>, TVector<i32>, TVector<i32>>> ts = { - {{1, 2, 3}, {4, 5, 6}, {11, 3}}, - {{1, 2, 3}, {4, 5, 6, 7}, {9}}, - {{1, 2, 3, 4}, {9}, {4, 5, 6}}, - {{1, 2, 3, 4}, {1}, {}}, - {{}, {1}, {1, 2, 3, 4}}, - }; - - FOR_DISPATCH_3(a, b, c, ts) { - TVector<std::tuple<i32, i32, i32>> e; - for (auto ai : a) { - for (auto bi : b) { - for (auto ci : c) { - e.push_back({ai, bi, ci}); - } - } - } - - TVector<std::tuple<i32, i32, i32>> f; - FOR_DISPATCH_3(ai, bi, ci, CartesianProduct(a, b, c)) { - f.push_back({ai, bi, ci}); - } - + TVector<std::tuple<TVector<i32>, TVector<i32>, TVector<i32>>> ts = { + {{1, 2, 3}, {4, 5, 6}, {11, 3}}, + {{1, 2, 3}, {4, 5, 6, 7}, {9}}, + {{1, 2, 3, 4}, {9}, {4, 5, 6}}, + {{1, 2, 3, 4}, {1}, {}}, + {{}, {1}, {1, 2, 3, 4}}, + }; + + FOR_DISPATCH_3(a, b, c, ts) { + TVector<std::tuple<i32, i32, i32>> e; + for (auto ai : a) { + for (auto bi : b) { + for (auto ci : c) { + e.push_back({ai, bi, ci}); + } + } + } + + TVector<std::tuple<i32, i32, i32>> f; + FOR_DISPATCH_3(ai, bi, ci, CartesianProduct(a, b, c)) { + f.push_back({ai, bi, ci}); + } + EXPECT_EQ(e, f); - } - } - + } + } + TEST(FuncTools, CompileCartesianProduct) { - auto container = std::vector{1, 2, 3}; - TestViewCompileability(CartesianProduct(container, container)); - const auto constContainer = std::vector{1, 2, 3}; - TestViewCompileability(CartesianProduct(constContainer, constContainer)); - const int arrayContainer[] = {1, 2, 3}; - TestViewCompileability(CartesianProduct(arrayContainer, arrayContainer)); - - std::vector<std::pair<int, int>> res; - FOR_DISPATCH_2(a, b, CartesianProduct(MakeMinimalisticContainer(), MakeMinimalisticContainer())) { - res.push_back({a, b}); - } + auto container = std::vector{1, 2, 3}; + TestViewCompileability(CartesianProduct(container, container)); + const auto constContainer = std::vector{1, 2, 3}; + TestViewCompileability(CartesianProduct(constContainer, constContainer)); + const int arrayContainer[] = {1, 2, 3}; + TestViewCompileability(CartesianProduct(arrayContainer, arrayContainer)); + + std::vector<std::pair<int, int>> res; + FOR_DISPATCH_2(a, b, CartesianProduct(MakeMinimalisticContainer(), MakeMinimalisticContainer())) { + res.push_back({a, b}); + } EXPECT_EQ(res, (std::vector<std::pair<int, int>>{ - {0, 0}, {0, 1}, {0, 2}, - {1, 0}, {1, 1}, {1, 2}, - {2, 0}, {2, 1}, {2, 2}, - })); - } - + {0, 0}, {0, 1}, {0, 2}, + {1, 0}, {1, 1}, {1, 2}, + {2, 0}, {2, 1}, {2, 2}, + })); + } + TEST(FuncTools, Concatenate2) { - TVector<std::pair<TVector<i32>, TVector<i32>>> ts = { - {{1, 2, 3}, {4, 5, 6}}, - {{1, 2, 3}, {4, 5, 6, 7}}, - {{1, 2, 3, 4}, {4, 5, 6}}, - {{1, 2, 3, 4}, {}}, - {{}, {1, 2, 3, 4}}, - }; - - for (auto [a, b] : ts) { - TVector<i32> c; - for (auto ai : a) { - c.push_back(ai); - } - for (auto bi : b) { - c.push_back(bi); - } - - TVector<i32> d; - for (auto x : Concatenate(a, b)) { - d.push_back(x); - } - + TVector<std::pair<TVector<i32>, TVector<i32>>> ts = { + {{1, 2, 3}, {4, 5, 6}}, + {{1, 2, 3}, {4, 5, 6, 7}}, + {{1, 2, 3, 4}, {4, 5, 6}}, + {{1, 2, 3, 4}, {}}, + {{}, {1, 2, 3, 4}}, + }; + + for (auto [a, b] : ts) { + TVector<i32> c; + for (auto ai : a) { + c.push_back(ai); + } + for (auto bi : b) { + c.push_back(bi); + } + + TVector<i32> d; + for (auto x : Concatenate(a, b)) { + d.push_back(x); + } + EXPECT_EQ(c, d); - } - - { - TVector<i32> a = {1, 2, 3, 4}; - TVector<i32> c; - for (auto x : Concatenate(a, TVector<i32>{5, 6})) { - c.push_back(x); - } + } + + { + TVector<i32> a = {1, 2, 3, 4}; + TVector<i32> c; + for (auto x : Concatenate(a, TVector<i32>{5, 6})) { + c.push_back(x); + } EXPECT_EQ(c, (TVector<i32>{1, 2, 3, 4, 5, 6})); - } - } - + } + } + TEST(FuncTools, CompileConcatenate) { - auto container = std::vector{1, 2, 3}; - TestViewCompileability(Concatenate(container, container)); - const auto constContainer = std::vector{1, 2, 3}; - TestViewCompileability(Concatenate(constContainer, constContainer)); - const int arrayContainer[] = {1, 2, 3}; - TestViewCompileability(Concatenate(arrayContainer, arrayContainer)); - - std::vector<int> res; - for (auto a : Concatenate(MakeMinimalisticContainer(), MakeMinimalisticContainer())) { - res.push_back(a); - } + auto container = std::vector{1, 2, 3}; + TestViewCompileability(Concatenate(container, container)); + const auto constContainer = std::vector{1, 2, 3}; + TestViewCompileability(Concatenate(constContainer, constContainer)); + const int arrayContainer[] = {1, 2, 3}; + TestViewCompileability(Concatenate(arrayContainer, arrayContainer)); + + std::vector<int> res; + for (auto a : Concatenate(MakeMinimalisticContainer(), MakeMinimalisticContainer())) { + res.push_back(a); + } EXPECT_EQ(res, (std::vector{0, 1, 2, 0, 1, 2})); - } - + } + TEST(FuncTools, Combo) { FOR_DISPATCH_2(i, j, Enumerate(xrange(10u))) { EXPECT_EQ(i, j); - } - + } + FOR_DISPATCH_2(i, jk, Enumerate(Enumerate(xrange(10u)))) { EXPECT_EQ(i, std::get<0>(jk)); EXPECT_EQ(std::get<0>(jk), std::get<1>(jk)); - } - + } + TVector<size_t> a = {0, 1, 2}; - FOR_DISPATCH_2(i, j, Enumerate(Reversed(a))) { + FOR_DISPATCH_2(i, j, Enumerate(Reversed(a))) { EXPECT_EQ(i, 2 - j); - } - - FOR_DISPATCH_2(i, j, Enumerate(Map<float>(a))) { + } + + FOR_DISPATCH_2(i, j, Enumerate(Map<float>(a))) { EXPECT_EQ(i, (size_t)j); - } - - FOR_DISPATCH_2(i, j, Zip(a, Map<float>(a))) { + } + + FOR_DISPATCH_2(i, j, Zip(a, Map<float>(a))) { EXPECT_EQ(i, (size_t)j); - } - - auto mapper = [](auto&& x) { - return std::get<0>(x) + std::get<1>(x); - }; - FOR_DISPATCH_2(i, j, Zip(a, Map(mapper, Zip(a, a)))) { + } + + auto mapper = [](auto&& x) { + return std::get<0>(x) + std::get<1>(x); + }; + FOR_DISPATCH_2(i, j, Zip(a, Map(mapper, Zip(a, a)))) { EXPECT_EQ(j, 2 * i); - } - } - - + } + } + + TEST(FuncTools, CopyIterator) { - TVector a = {1, 2, 3, 4}; - TVector b = {4, 5, 6, 7}; - - // calls f on 2nd, 3d and 4th positions (numeration from 1st) - auto testIterator = [](auto it, auto f) { - ++it; - auto it2 = it; - ++it2; - ++it2; - auto it3 = it; - ++it3; - f(*it, *it3, *it2); - }; - - { - auto iterable = Enumerate(a); - testIterator(std::begin(iterable), - [](auto p2, auto p3, auto p4) { + TVector a = {1, 2, 3, 4}; + TVector b = {4, 5, 6, 7}; + + // calls f on 2nd, 3d and 4th positions (numeration from 1st) + auto testIterator = [](auto it, auto f) { + ++it; + auto it2 = it; + ++it2; + ++it2; + auto it3 = it; + ++it3; + f(*it, *it3, *it2); + }; + + { + auto iterable = Enumerate(a); + testIterator(std::begin(iterable), + [](auto p2, auto p3, auto p4) { EXPECT_EQ(std::get<0>(p2), 1u); EXPECT_EQ(std::get<1>(p2), 2); EXPECT_EQ(std::get<0>(p3), 2u); EXPECT_EQ(std::get<1>(p3), 3); EXPECT_EQ(std::get<0>(p4), 3u); EXPECT_EQ(std::get<1>(p4), 4); - }); - } - - { - auto iterable = Map([](i32 x) { return x*x; }, a); - testIterator(std::begin(iterable), - [](auto p2, auto p3, auto p4) { + }); + } + + { + auto iterable = Map([](i32 x) { return x*x; }, a); + testIterator(std::begin(iterable), + [](auto p2, auto p3, auto p4) { EXPECT_EQ(p2, 4); EXPECT_EQ(p3, 9); EXPECT_EQ(p4, 16); - }); - } - - { - auto iterable = Zip(a, b); - testIterator(std::begin(iterable), - [](auto p2, auto p3, auto p4) { + }); + } + + { + auto iterable = Zip(a, b); + testIterator(std::begin(iterable), + [](auto p2, auto p3, auto p4) { EXPECT_EQ(std::get<0>(p2), 2); EXPECT_EQ(std::get<1>(p2), 5); EXPECT_EQ(std::get<0>(p3), 3); EXPECT_EQ(std::get<1>(p3), 6); EXPECT_EQ(std::get<0>(p4), 4); EXPECT_EQ(std::get<1>(p4), 7); - }); - } - - { - auto c = {1, 2, 3, 4, 5, 6, 7, 8}; - auto iterable = Filter([](i32 x) { return !(x & 1); }, c); - testIterator(std::begin(iterable), - [](auto p2, auto p3, auto p4) { + }); + } + + { + auto c = {1, 2, 3, 4, 5, 6, 7, 8}; + auto iterable = Filter([](i32 x) { return !(x & 1); }, c); + testIterator(std::begin(iterable), + [](auto p2, auto p3, auto p4) { EXPECT_EQ(p2, 4); EXPECT_EQ(p3, 6); EXPECT_EQ(p4, 8); - }); - } - - { - auto iterable = CartesianProduct(TVector{0, 1}, TVector{2, 3}); - // (0, 2), (0, 3), (1, 2), (1, 3) - testIterator(std::begin(iterable), - [](auto p2, auto p3, auto p4) { + }); + } + + { + auto iterable = CartesianProduct(TVector{0, 1}, TVector{2, 3}); + // (0, 2), (0, 3), (1, 2), (1, 3) + testIterator(std::begin(iterable), + [](auto p2, auto p3, auto p4) { EXPECT_EQ(std::get<0>(p2), 0); EXPECT_EQ(std::get<1>(p2), 3); EXPECT_EQ(std::get<0>(p3), 1); EXPECT_EQ(std::get<1>(p3), 2); EXPECT_EQ(std::get<0>(p4), 1); EXPECT_EQ(std::get<1>(p4), 3); - }); - } - } + }); + } + } diff --git a/library/cpp/iterator/ut/ya.make b/library/cpp/iterator/ut/ya.make index 601e5663b9..76db8d8a99 100644 --- a/library/cpp/iterator/ut/ya.make +++ b/library/cpp/iterator/ut/ya.make @@ -8,7 +8,7 @@ OWNER(g:util) SRCS( filtering_ut.cpp - functools_ut.cpp + functools_ut.cpp iterate_keys_ut.cpp iterate_values_ut.cpp mapped_ut.cpp diff --git a/library/cpp/iterator/ya.make b/library/cpp/iterator/ya.make index 1ba1ffb411..dfc4b43a89 100644 --- a/library/cpp/iterator/ya.make +++ b/library/cpp/iterator/ya.make @@ -2,18 +2,18 @@ OWNER(g:util) LIBRARY() -SRCS( - cartesian_product.cpp - concatenate.cpp - enumerate.cpp +SRCS( + cartesian_product.cpp + concatenate.cpp + enumerate.cpp iterate_keys.cpp iterate_values.cpp - filtering.cpp - functools.cpp - mapped.cpp - zip.cpp -) - + filtering.cpp + functools.cpp + mapped.cpp + zip.cpp +) + END() RECURSE_FOR_TESTS(ut) diff --git a/library/cpp/iterator/zip.cpp b/library/cpp/iterator/zip.cpp index 36338ea527..0b6121fbdc 100644 --- a/library/cpp/iterator/zip.cpp +++ b/library/cpp/iterator/zip.cpp @@ -1 +1 @@ -#include "zip.h" +#include "zip.h" diff --git a/library/cpp/iterator/zip.h b/library/cpp/iterator/zip.h index ac12ed35fe..6ec0993240 100644 --- a/library/cpp/iterator/zip.h +++ b/library/cpp/iterator/zip.h @@ -1,128 +1,128 @@ -#pragma once - -#include <util/generic/store_policy.h> - -#include <algorithm> -#include <tuple> - - -namespace NPrivate { - - template <typename TContainer, typename TIteratorCategory = typename std::iterator_traits<decltype(std::begin(std::declval<TContainer>()))>::iterator_category> - static constexpr bool HasRandomAccessIterator(int32_t) { - return std::is_same_v<TIteratorCategory, std::random_access_iterator_tag>; - } - - template <typename TContainer> - static constexpr bool HasRandomAccessIterator(uint32_t) { - return false; - } - - template <typename... TContainers> - struct TZipper { - template <std::size_t... I> - struct TZipperWithIndex { - private: - using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>; - using TValue = std::tuple<decltype(*std::begin(std::declval<TContainers&>()))...>; - using TIteratorState = std::tuple<decltype(std::begin(std::declval<TContainers&>()))...>; - using TSentinelState = std::tuple<decltype(std::end(std::declval<TContainers&>()))...>; - - static constexpr bool TrivialSentinel = std::is_same_v<TIteratorState, TSentinelState>; - - struct TIterator; - struct TSentinelCandidate { - TSentinelState Iterators_; - }; - using TSentinel = std::conditional_t<TrivialSentinel, TIterator, TSentinelCandidate>; - -#ifndef _MSC_VER - // windows compiler crashes here - static constexpr bool LimitByFirstContainer = TrivialSentinel && - ((HasRandomAccessIterator<TContainers>(0)) && ...); -#else - static constexpr bool LimitByFirstContainer = false; -#endif - - struct TIterator { - using difference_type = std::ptrdiff_t; - using value_type = TValue; - using pointer = TValue*; - using reference = TValue&; +#pragma once + +#include <util/generic/store_policy.h> + +#include <algorithm> +#include <tuple> + + +namespace NPrivate { + + template <typename TContainer, typename TIteratorCategory = typename std::iterator_traits<decltype(std::begin(std::declval<TContainer>()))>::iterator_category> + static constexpr bool HasRandomAccessIterator(int32_t) { + return std::is_same_v<TIteratorCategory, std::random_access_iterator_tag>; + } + + template <typename TContainer> + static constexpr bool HasRandomAccessIterator(uint32_t) { + return false; + } + + template <typename... TContainers> + struct TZipper { + template <std::size_t... I> + struct TZipperWithIndex { + private: + using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>; + using TValue = std::tuple<decltype(*std::begin(std::declval<TContainers&>()))...>; + using TIteratorState = std::tuple<decltype(std::begin(std::declval<TContainers&>()))...>; + using TSentinelState = std::tuple<decltype(std::end(std::declval<TContainers&>()))...>; + + static constexpr bool TrivialSentinel = std::is_same_v<TIteratorState, TSentinelState>; + + struct TIterator; + struct TSentinelCandidate { + TSentinelState Iterators_; + }; + using TSentinel = std::conditional_t<TrivialSentinel, TIterator, TSentinelCandidate>; + +#ifndef _MSC_VER + // windows compiler crashes here + static constexpr bool LimitByFirstContainer = TrivialSentinel && + ((HasRandomAccessIterator<TContainers>(0)) && ...); +#else + static constexpr bool LimitByFirstContainer = false; +#endif + + struct TIterator { + using difference_type = std::ptrdiff_t; + using value_type = TValue; + using pointer = TValue*; + using reference = TValue&; using const_reference = const TValue&; - using iterator_category = std::input_iterator_tag; - - TValue operator*() { - return {*std::get<I>(Iterators_)...}; - } - TValue operator*() const { - return {*std::get<I>(Iterators_)...}; - } + using iterator_category = std::input_iterator_tag; + + TValue operator*() { + return {*std::get<I>(Iterators_)...}; + } + TValue operator*() const { + return {*std::get<I>(Iterators_)...}; + } TIterator& operator++() { - (++std::get<I>(Iterators_), ...); + (++std::get<I>(Iterators_), ...); return *this; - } + } TIterator operator++(int) { return TIterator{TIteratorState{std::get<I>(Iterators_)++...}}; } - bool operator!=(const TSentinel& other) const { - if constexpr (LimitByFirstContainer) { - return std::get<0>(Iterators_) != std::get<0>(other.Iterators_); - } else { - // yes, for all correct iterators but end() it is a correct way to compare - return ((std::get<I>(Iterators_) != std::get<I>(other.Iterators_)) && ...); - } - } - bool operator==(const TSentinel& other) const { - return !(*this != other); - } - - TIteratorState Iterators_; - }; - public: - using iterator = TIterator; - using const_iterator = TIterator; + bool operator!=(const TSentinel& other) const { + if constexpr (LimitByFirstContainer) { + return std::get<0>(Iterators_) != std::get<0>(other.Iterators_); + } else { + // yes, for all correct iterators but end() it is a correct way to compare + return ((std::get<I>(Iterators_) != std::get<I>(other.Iterators_)) && ...); + } + } + bool operator==(const TSentinel& other) const { + return !(*this != other); + } + + TIteratorState Iterators_; + }; + public: + using iterator = TIterator; + using const_iterator = TIterator; using value_type = typename TIterator::value_type; using reference = typename TIterator::reference; using const_reference = typename TIterator::const_reference; - - TIterator begin() const { - return {TIteratorState{std::begin(*std::get<I>(Holders_).Ptr())...}}; - } - - TSentinel end() const { - if constexpr (LimitByFirstContainer) { - auto endOfFirst = std::begin(*std::get<0>(Holders_).Ptr()) + std::min({ - std::end(*std::get<I>(Holders_).Ptr()) - std::begin(*std::get<I>(Holders_).Ptr())...}); - TIterator iter{TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}}; - std::get<0>(iter.Iterators_) = endOfFirst; - return iter; - } else { - return {TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}}; - } - } - - mutable THolders Holders_; - }; - - template <std::size_t... I> - static auto Zip(TContainers&&... containers, std::index_sequence<I...>) { - return TZipperWithIndex<I...>{{std::forward<TContainers>(containers)...}}; - } - }; - -} - - -//! Acts as pythonic zip, BUT result length is equal to shortest length of input containers -//! Usage: for (auto [ai, bi, ci] : Zip(a, b, c)) {...} -template <typename... TContainers> -auto Zip(TContainers&&... containers) { + + TIterator begin() const { + return {TIteratorState{std::begin(*std::get<I>(Holders_).Ptr())...}}; + } + + TSentinel end() const { + if constexpr (LimitByFirstContainer) { + auto endOfFirst = std::begin(*std::get<0>(Holders_).Ptr()) + std::min({ + std::end(*std::get<I>(Holders_).Ptr()) - std::begin(*std::get<I>(Holders_).Ptr())...}); + TIterator iter{TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}}; + std::get<0>(iter.Iterators_) = endOfFirst; + return iter; + } else { + return {TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}}; + } + } + + mutable THolders Holders_; + }; + + template <std::size_t... I> + static auto Zip(TContainers&&... containers, std::index_sequence<I...>) { + return TZipperWithIndex<I...>{{std::forward<TContainers>(containers)...}}; + } + }; + +} + + +//! Acts as pythonic zip, BUT result length is equal to shortest length of input containers +//! Usage: for (auto [ai, bi, ci] : Zip(a, b, c)) {...} +template <typename... TContainers> +auto Zip(TContainers&&... containers) { return ::NPrivate::TZipper<TContainers...>::Zip( - std::forward<TContainers>(containers)..., - std::make_index_sequence<sizeof...(TContainers)>{} - ); -} + std::forward<TContainers>(containers)..., + std::make_index_sequence<sizeof...(TContainers)>{} + ); +} |