Span referred to a continuous memory, for example an array;
template <typename T>
class Span {
public:
Span(T first, size_t s) ...;
Span(T first, T lasst) ...;
public:
// iterating underlying continuous memory;
begin() ...
end() ...
T & operator[](idx) ...
private:
T first;
T last;
};
loop function can iterating all passing spans, like a nested bubbling loop;
template <typename Func, typename ...Spans>
void loop(Func func, Spans &&...spans) {
// question: how to implements ?
}
example
int main() {
int arr[] {1, 2, 3};
float arr2[] {1.1, 2.2};
int arr3[] {-1, -2, -3};
loop(func, Span(arr, 3), Span(arr2, 2));
/*
we can get:
func(1, 1.1);
func(1, 2.2);
func(2, 1.1);
func(2, 2.2);
func(3, 1.1);
func(3, 2.2);
*/
loop(func, Span(arr, 3), Span(arr2, 2), Span(arr3, 3));
/*
we can get:
func(1, 1.1, -1);
func(1, 1.1, -2);
func(1, 1.1, -3);
func(1, 2.2, -1);
func(1, 2.2, -2);
func(1, 2.2, -3);
func(2, 1.1, -1);
func(2, 1.1, -2);
...
*/
}
how to implements the loop function, using c++ language features up to c++20 is ok, but no standard library or 3rd-part library, just implements manually;
no recursion function call, better for loop; cause recursion easily get stack overflow;
using vector and tuple, structure binding is ok;
Here is an alternative that uses an array to keep track of the indices for each of the spans. The index array is declared to have nSpans+1
elements where the first element serves as a sentinel. The array spanSizes
is declared to be the same size and also have a sentinel. The variable carryIndex
keeps track of which index should be incremented. The word "carry" here refers to the fact that the indices are incremented in a manner similar to carries in additions.
template<class Fn, class... Spans>
void loop(Fn&& fn, Spans... spans) {
constexpr std::size_t nSpans = sizeof...(spans);
// Array containing the sizes of each span, with a sentinel in front
std::size_t spanSizes[]{0, spans.size()...};
// Array containing the loop indexes, also with a sentinel in front
std::size_t indexes[nSpans+1]{};
// Points to the index that should be incremented, after considering carries from less significant indexes
std::size_t carryIndex = nSpans;
// While the sentinel (most significant index) is not reached
while(indexes[0] == 0){
// Call `fn` with perfect forwarding, passing in elements picked from each span according to the corresponding index.
// The +1 accounts for the sentinel.
[&]<std::size_t... Is>(std::index_sequence<Is...>){
std::forward<Fn>(fn)(spans[indexes[Is+1]]...);
}(std::make_index_sequence<nSpans>());
// Increment the current index.
++indexes[nSpans];
// Loop while there's need to carry to a more significant index
while(indexes[carryIndex] == spanSizes[carryIndex]){
// Reset the index.
indexes[carryIndex] = 0;
// Update `carryIndex` to point to the next more significant index and increment it.
++indexes[--carryIndex];
// The next increment should start over from the least significant index.
if(indexes[carryIndex] != spanSizes[carryIndex]){
carryIndex = nSpans;
}
}
}
}
Demo: https://godbolt.org/z/ejzhe7e5G (The main()
is taken from demo in the answer by @Turtlefight)