#pragma once #include #include #include namespace NPrivate { template ()))>::iterator_category> static constexpr bool HasRandomAccessIterator(int32_t) { return std::is_same_v; } template static constexpr bool HasRandomAccessIterator(uint32_t) { return false; } template struct TZipper { template struct TZipperWithIndex { private: using THolders = std::tuple...>; using TValue = std::tuple()))...>; using TIteratorState = std::tuple()))...>; using TSentinelState = std::tuple()))...>; static constexpr bool TrivialSentinel = std::is_same_v; struct TIterator; struct TSentinelCandidate { TSentinelState Iterators_; }; using TSentinel = std::conditional_t; #ifndef _MSC_VER // windows compiler crashes here static constexpr bool LimitByFirstContainer = TrivialSentinel && ((HasRandomAccessIterator(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(Iterators_)...}; } TValue operator*() const { return {*std::get(Iterators_)...}; } TIterator& operator++() { (++std::get(Iterators_), ...); return *this; } TIterator operator++(int) { return TIterator{TIteratorState{std::get(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(Iterators_) != std::get(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(Holders_).Ptr())...}}; } TSentinel end() const { if constexpr (LimitByFirstContainer) { auto endOfFirst = std::begin(*std::get<0>(Holders_).Ptr()) + std::min({ std::end(*std::get(Holders_).Ptr()) - std::begin(*std::get(Holders_).Ptr())...}); TIterator iter{TSentinelState{std::end(*std::get(Holders_).Ptr())...}}; std::get<0>(iter.Iterators_) = endOfFirst; return iter; } else { return {TSentinelState{std::end(*std::get(Holders_).Ptr())...}}; } } mutable THolders Holders_; }; template static auto Zip(TContainers&&... containers, std::index_sequence) { return TZipperWithIndex{{std::forward(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 auto Zip(TContainers&&... containers) { return ::NPrivate::TZipper::Zip( std::forward(containers)..., std::make_index_sequence{} ); }