concatenate.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #pragma once
  2. #include <util/generic/store_policy.h>
  3. #include <iterator>
  4. #include <tuple>
  5. namespace NPrivate {
  6. template <typename TValue_, typename... TContainers>
  7. struct TConcatenator {
  8. template <std::size_t... I>
  9. struct TConcatenatorWithIndex {
  10. private:
  11. using THolders = std::tuple<TAutoEmbedOrPtrPolicy<TContainers>...>;
  12. using TValue = TValue_;
  13. using TIteratorState = std::tuple<decltype(std::begin(std::declval<TContainers&>()))...>;
  14. using TSentinelState = std::tuple<decltype(std::end(std::declval<TContainers&>()))...>;
  15. struct TIterator;
  16. struct TSentinelCandidate {
  17. TSentinelState Iterators_;
  18. std::size_t Position_;
  19. THolders* HoldersPtr_;
  20. };
  21. using TSentinel = std::conditional_t<std::is_same_v<TIteratorState, TSentinelState>,
  22. TIterator, TSentinelCandidate>;
  23. struct TIterator {
  24. private:
  25. friend struct TConcatenatorWithIndex<I...>;
  26. // important, that it is a static function, compiler better optimizes such code
  27. template <std::size_t index = 0, typename TMaybeConstIteratorState>
  28. static TValue GetCurrentValue(std::size_t position, TMaybeConstIteratorState& iterators) {
  29. if constexpr (index >= sizeof...(TContainers)) {
  30. // never happened when use of iterator is correct
  31. return *std::get<0>(iterators);
  32. } else {
  33. if (position == index) {
  34. return *std::get<index>(iterators);
  35. } else {
  36. return GetCurrentValue<index + 1>(position, iterators);
  37. }
  38. }
  39. }
  40. template <bool needIncrement, std::size_t index = 0>
  41. void MaybeIncrementIteratorAndSkipExhaustedContainers() {
  42. if constexpr (index >= sizeof...(TContainers)) {
  43. return;
  44. } else {
  45. if (Position_ == index) {
  46. if constexpr (needIncrement) {
  47. ++std::get<index>(Iterators_);
  48. }
  49. if (!(std::get<index>(Iterators_) != std::end(*std::get<index>(*HoldersPtr_).Ptr()))) {
  50. ++Position_;
  51. MaybeIncrementIteratorAndSkipExhaustedContainers<false, index + 1>();
  52. }
  53. } else {
  54. MaybeIncrementIteratorAndSkipExhaustedContainers<needIncrement, index + 1>();
  55. }
  56. }
  57. }
  58. public:
  59. using difference_type = std::ptrdiff_t;
  60. using value_type = TValue;
  61. using pointer = std::remove_reference_t<TValue>*;
  62. using reference = std::remove_reference_t<TValue>&;
  63. using iterator_category = std::input_iterator_tag;
  64. TValue operator*() {
  65. return GetCurrentValue(Position_, Iterators_);
  66. }
  67. TValue operator*() const {
  68. return GetCurrentValue(Position_, Iterators_);
  69. }
  70. TIterator& operator++() {
  71. MaybeIncrementIteratorAndSkipExhaustedContainers<true>();
  72. return *this;
  73. }
  74. bool operator!=(const TSentinel& other) const {
  75. // give compiler an opportunity to optimize sentinel case (-70% of time)
  76. if (other.Position_ == sizeof...(TContainers)) {
  77. return Position_ < sizeof...(TContainers);
  78. } else {
  79. return (Position_ != other.Position_ ||
  80. ((std::get<I>(Iterators_) != std::get<I>(other.Iterators_)) || ...));
  81. }
  82. }
  83. bool operator==(const TSentinel& other) const {
  84. return !(*this != other);
  85. }
  86. TIteratorState Iterators_;
  87. std::size_t Position_;
  88. THolders* HoldersPtr_;
  89. };
  90. public:
  91. using iterator = TIterator;
  92. using const_iterator = TIterator;
  93. using value_type = typename TIterator::value_type;
  94. using reference = typename TIterator::reference;
  95. using const_reference = typename TIterator::reference;
  96. TIterator begin() const {
  97. TIterator iterator{TIteratorState{std::begin(*std::get<I>(Holders_).Ptr())...}, 0, &Holders_};
  98. iterator.template MaybeIncrementIteratorAndSkipExhaustedContainers<false>();
  99. return iterator;
  100. }
  101. TSentinel end() const {
  102. return {TSentinelState{std::end(*std::get<I>(Holders_).Ptr())...}, sizeof...(TContainers), &Holders_};
  103. }
  104. mutable THolders Holders_;
  105. };
  106. template <std::size_t... I>
  107. static auto Concatenate(TContainers&&... containers, std::index_sequence<I...>) {
  108. return TConcatenatorWithIndex<I...>{{std::forward<TContainers>(containers)...}};
  109. }
  110. };
  111. }
  112. //! Usage: for (auto x : Concatenate(a, b)) {...}
  113. template <typename TFirstContainer, typename... TContainers>
  114. auto Concatenate(TFirstContainer&& container, TContainers&&... containers) {
  115. return NPrivate::TConcatenator<decltype(*std::begin(container)), TFirstContainer, TContainers...>::Concatenate(
  116. std::forward<TFirstContainer>(container), std::forward<TContainers>(containers)...,
  117. std::make_index_sequence<sizeof...(TContainers) + 1>{});
  118. }