> Sadly there is not a simple way to integrate this to native loop constructs without macros and even then it is a bit ugly.
I’ve implemented something like this before, without macros. It’s a little ugly, but not that bad IMO.
If you write a native range-based for loop:
for (auto foo : obj) { /* do something */ }
it essentially desugars to auto it = foo.begin();
auto end = foo.end();
for (; it != end; ++it) {
auto foo = *it;
/* do something */
}
To make it work with a custom type, you need to implement `begin()` and `end()` methods, but the returned objects don’t need to support the full STL iterator protocol; they only need to support the exact sequence of operations from the desugaring. So, for example, `end()` can return a unique `End` type that contains no data and does nothing. `begin()` can return a different type that does all the real work and implements `operator!=(End)`. With that, it’s not too hard to implement a wrapper around a Python-like iterator protocol.The main drawback is that you need to temporarily store each item in the begin object before it’s moved into the iteration variable. This is because you have to already know whether a next item exists at the point of `it != end`, but then the item isn’t actually retrieved until `*it`. The extra move has a slight cost, but the compiler can often optimize it away to nothing. You can also avoid this if the for loop uses a reference type (`for (auto& foo : obj)`).
(Since c++17)
The technique you so well describe in your comment does not work with the original range for :(
Not that it is an issue anymore, but I'd forgive anyone who tried to write code like this when range for was added from not trying anymore, because they remember the original semantics.
The page: https://cppreference.com/w/cpp/language/range-for.html
reads like a testimony in a negligence court case.
I think you can also define begin/end as non member functions (eg if you don’t own the code for the type).