zip.h 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #pragma once
  2. #include <util/generic/store_policy.h>
  3. #include <algorithm>
  4. #include <tuple>
  5. namespace NPrivate {
  6. template <typename TContainer, typename TIteratorCategory = typename std::iterator_traits<decltype(std::begin(std::declval<TContainer>()))>::iterator_category>
  7. static constexpr bool HasRandomAccessIterator(int32_t) {
  8. return std::is_same_v<TIteratorCategory, std::random_access_iterator_tag>;
  9. }
  10. template <typename TContainer>
  11. static constexpr bool HasRandomAccessIterator(uint32_t) {
  12. return false;
  13. }
  14. template <typename... TContainers>
  15. struct TZipper {
  16. template <std::size_t... I>
  17. struct TZipperWithIndex {
  18. private:
  19. using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>;
  20. using TValue = std::tuple<decltype(*std::begin(std::declval<TContainers&>()))...>;
  21. using TIteratorState = std::tuple<decltype(std::begin(std::declval<TContainers&>()))...>;
  22. using TSentinelState = std::tuple<decltype(std::end(std::declval<TContainers&>()))...>;
  23. static constexpr bool TrivialSentinel = std::is_same_v<TIteratorState, TSentinelState>;
  24. struct TIterator;
  25. struct TSentinelCandidate {
  26. TSentinelState Iterators_;
  27. };
  28. using TSentinel = std::conditional_t<TrivialSentinel, TIterator, TSentinelCandidate>;
  29. #ifndef _MSC_VER
  30. // windows compiler crashes here
  31. static constexpr bool LimitByFirstContainer = TrivialSentinel &&
  32. ((HasRandomAccessIterator<TContainers>(0)) && ...);
  33. #else
  34. static constexpr bool LimitByFirstContainer = false;
  35. #endif
  36. struct TIterator {
  37. using difference_type = std::ptrdiff_t;
  38. using value_type = TValue;
  39. using pointer = TValue*;
  40. using reference = TValue&;
  41. using const_reference = const TValue&;
  42. using iterator_category = std::input_iterator_tag;
  43. TValue operator*() {
  44. return {*std::get<I>(Iterators_)...};
  45. }
  46. TValue operator*() const {
  47. return {*std::get<I>(Iterators_)...};
  48. }
  49. TIterator& operator++() {
  50. (++std::get<I>(Iterators_), ...);
  51. return *this;
  52. }
  53. TIterator operator++(int) {
  54. return TIterator{TIteratorState{std::get<I>(Iterators_)++...}};
  55. }
  56. bool operator!=(const TSentinel& other) const {
  57. if constexpr (LimitByFirstContainer) {
  58. return std::get<0>(Iterators_) != std::get<0>(other.Iterators_);
  59. } else {
  60. // yes, for all correct iterators but end() it is a correct way to compare
  61. return ((std::get<I>(Iterators_) != std::get<I>(other.Iterators_)) && ...);
  62. }
  63. }
  64. bool operator==(const TSentinel& other) const {
  65. return !(*this != other);
  66. }
  67. TIteratorState Iterators_;
  68. };
  69. public:
  70. using iterator = TIterator;
  71. using const_iterator = TIterator;
  72. using value_type = typename TIterator::value_type;
  73. using reference = typename TIterator::reference;
  74. using const_reference = typename TIterator::const_reference;
  75. TIterator begin() const {
  76. return {TIteratorState{std::begin(*std::get<I>(Holders_).Ptr())...}};
  77. }
  78. TSentinel end() const {
  79. if constexpr (LimitByFirstContainer) {
  80. auto endOfFirst = std::begin(*std::get<0>(Holders_).Ptr()) + std::min({
  81. std::end(*std::get<I>(Holders_).Ptr()) - std::begin(*std::get<I>(Holders_).Ptr())...});
  82. TIterator iter{TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}};
  83. std::get<0>(iter.Iterators_) = endOfFirst;
  84. return iter;
  85. } else {
  86. return {TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}};
  87. }
  88. }
  89. mutable THolders Holders_;
  90. };
  91. template <std::size_t... I>
  92. static auto Zip(TContainers&&... containers, std::index_sequence<I...>) {
  93. return TZipperWithIndex<I...>{{std::forward<TContainers>(containers)...}};
  94. }
  95. };
  96. }
  97. //! Acts as pythonic zip, BUT result length is equal to shortest length of input containers
  98. //! Usage: for (auto [ai, bi, ci] : Zip(a, b, c)) {...}
  99. template <typename... TContainers>
  100. auto Zip(TContainers&&... containers) {
  101. return ::NPrivate::TZipper<TContainers...>::Zip(
  102. std::forward<TContainers>(containers)...,
  103. std::make_index_sequence<sizeof...(TContainers)>{}
  104. );
  105. }