To do this, I wrote an iterative and combining adapter in the fn functional library :
#include <fn.h> #include <iostream> int main() { using std; using fn; for (auto &&values : combine(seq(20,25), seq(40,45), seq(2,4))) { int x, y, z; tie(x, y, z) = values; cout << x << ", " << y << ", " << z << "\n"; // or in your case: doStuff({x, y, z}); } }
Output:
20, 40, 2 20, 40, 3 20, 41, 2 20, 41, 3 ... 24, 43, 2 24, 43, 3 24, 44, 2 24, 44, 3
Here, seq(a, b) returns an implicit range that iterates through the values โโof [a, b) (i.e., the first is inclusive, the second is exclusive). (The third parameter may indicate the step, and more complex alternatives exist for more control over the iteration.)
The combine(ranges...) function returns an implicit range that iterates over all combinations for given ranges (where the first is considered the "most significant", similar to your outer-most loop). Its iterator splits into std::tuple , holding the current combination.
This tuple is then bound to the body of the loop to some variables. (Unfortunately, there is no "auto-binding" for a loop-based range, like for(tie(auto x, auto y, auto z) : ...) .)
Implementation:
This is pretty simple: it is a function that returns an adapter object that has begin() and end() functions. They return a custom iterator that increments the current value in operator++ and returns it in operator* .
This is more interesting: it returns an adapter object that contains the ranges provided as combine arguments in the tuple member. The iterator of this adapter contains iterators for the wrapped ranges in the tuple member, but three times: the current position, the beginning and the end, you will soon see why.
The operator++ iterator is most likely the most interesting one: it is implemented recursively using variational patterns in variadic_ops.h , va_next_combination() and it is set with iterator triplets (for each range, current, start and end):
// base case inline bool va_next_combination() { return true; } // recursive case template<typename Head, typename ...Tail> inline bool va_next_combination(std::tuple<Head&,Head&,Head&> && curr_and_begin_and_end, std::tuple<Tail&,Tail&,Tail&> &&...t) { // advance the "tail" to its next combination and check if it had an overflow if (va_next_combination(std::forward<std::tuple<Tail&,Tail&,Tail&>>(t)...)) { // advance the "head" iterator ++std::get<0>(curr_and_begin_and_end); // check if the "head" just overflow bool at_end = (std::get<0>(curr_and_begin_and_end) == std::get<2>(curr_and_begin_and_end)); // if it did, put it back to the beginning and report the overflow if (at_end) std::get<0>(curr_and_begin_and_end) = std::get<1>(curr_and_begin_and_end); return at_end; } else { // "tail" didn't overflow, so we do nothing and no overflow should be reported return false; } }
Starting with the right iterator itself in the set, it increments the iterator. If it has just reached the end of the range, it reports that the return value of the recursive function. The next iterator checks this value, if it is true, you need to promote it yourself (otherwise), as well as "reset" the iterator next to the right (ie, "Wrap" its overflow) and, finally, it passes the same information to the next left .
Basically, how mechanical counters work if you start with an "if" -condition at the deepest level of recursion.