#pragma once #include #include namespace NPrivate { template struct TCartesianMultiplier { template struct TCartesianMultiplierWithIndex { private: using THolders = std::tuple...>; using TValue = std::tuple()))...>; using TIteratorState = std::tuple()))...>; using TSentinelState = std::tuple()))...>; struct TIterator; struct TSentinelCandidate { TSentinelState Iterators_; THolders* HoldersPtr_; }; using TSentinel = std::conditional_t, TIterator, TSentinelCandidate>; struct TIterator { private: //! Return value is true when iteration is not finished template void IncrementIteratorsTuple() { auto& currentIterator = std::get(Iterators_); ++currentIterator; if (currentIterator != std::end(*std::get(*HoldersPtr_).Ptr())) { return; } else { currentIterator = std::begin(*std::get(*HoldersPtr_).Ptr()); if constexpr (position != 1) { IncrementIteratorsTuple(); } 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(Iterators_)...}; } TValue operator*() const { return {*std::get(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(Iterators_) != std::get(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(Holders_).Ptr()) != std::end(*std::get(Holders_).Ptr())) && ...); return {TIteratorState{int(isEmpty), std::begin(*std::get(Holders_).Ptr())...}, &Holders_}; } TSentinel end() const { return {TSentinelState{1, std::end(*std::get(Holders_).Ptr())...}, &Holders_}; } mutable THolders Holders_; }; template static auto CartesianMultiply(TContainers&&... containers, std::index_sequence) { return TCartesianMultiplierWithIndex{{std::forward(containers)...}}; } }; } //! Usage: for (auto [ai, bi] : CartesianProduct(a, b)) {...} //! Equivalent: for (auto& ai : a) { for (auto& bi : b) {...} } template auto CartesianProduct(TContainers&&... containers) { return NPrivate::TCartesianMultiplier::CartesianMultiply( std::forward(containers)..., std::make_index_sequence{}); }