In Qt, there is a foreach loop which is implemented using macros (Q_FOREACH). There are different implementations, depending on the compiler.
The definition for GCC is as follows:
#define Q_FOREACH(variable, container) \
for (QForeachContainer<__typeof__(container)> _container_(container); \
!_container_.brk && _container_.i != _container_.e; \
__extension__ ({ ++_container_.brk; ++_container_.i; })) \
for (variable = *_container_.i;; __extension__ ({--_container_.brk; break;}))
… using the helper class QForeachContainer which is defined as follows:
template <typename T>
class QForeachContainer {
public:
inline QForeachContainer(const T& t) : c(t), brk(0), i(c.begin()), e(c.end()) { }
const T c;
int brk;
typename T::const_iterator i, e;
};
The container in a Q_FOREACH macro has to be a class T which at least has to provide a T::const_iterator type, a T.begin() and a T.end() method, as do all STL containers as well as most Qt containers like QList, QVector, QMap, QHash, …
My question is now: How does this macro work?
One thing seems to be really odd: The variable only appears once in the macro definition. So e.g. foreach(QString item, list) has a QString item = but no item = afterwards at any time… How can the variable item then be changed in each step?
Even more confusing is the following definition of Q_FOREACH for the MS VC++ compiler:
#define Q_FOREACH(variable,container) \
if(0){}else \
for (const QForeachContainerBase &_container_ = qForeachContainerNew(container); \
qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->condition(); \
++qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->i) \
for (variable = *qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->i; \
qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->brk; \
--qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))->brk)
Why true : 0 ? ...? Doesn’t this always get evaluated to 0? Is the function call qForeachPointer(container) executed even if the condition before ? is true?
And why do we need two for-loops?
It would be cool if anyone can make things a bit clearer for me!
The GCC version
The GCC one is really quite simple. First of all it is used like this:
And that will be expanded to
So first of all:
This is the actual
forloop. It sets up aQForeachContainerto help with the iteration. Thebrkvariable is intitialised to 0. Then the condition is tested:brkis zero so!brkis true, and presumably if the container has any elementsi(the current element) doesn’t equale(the last element) yet.Then the body of that outer
foris entered, which is:So
xis set to*_container_.iwhich is the current element the iteration is on, and there is no condition so presumably this loop will continue forever. Then the body of the loop is entered, which is our code, and it’s just a comment so it doesn’t do anything.Then the increment part of the inner loop is entered, which is interesting:
It decrements
brkso that’s now -1, and breaks out of the loop (with__extension__which makes GCC not emit warnings for using GCC extensions, like you now know).Then the increment part of the outer loop is entered:
which increments
brkagain and makes it 0 again, and theniis incremented so we get to the next element. The condition is checked, and sincebrkis now 0 andipresumably doesn’t equaleyet (if we have more elements) the process is repeated.Why did we decrement and then increment
brklike that? The reason is because the increment part of the inner loop will not be executed if we usedbreakin the body of our code, like this:Then
brkwould still be 0 when it breaks out of the inner loop, and then the increment part of the outer loop would be entered and increment it to 1, then!brkwould be false and the outer loop’s condition would evaluate to false, and the foreach would stop.The trick is to realise that there are two
forloops; the outer one’s lifetime is the whole foreach, but the inner one only lasts for one element. It would be an infinite loop since it doesn’t have a condition, but it is eitherbreaked out of by it’s increment part, or by abreakin the code you provide it. That’s whyxlooks like it is assigned to "only once" but actually it’s assigned to on every iteration of the outer loop.The VS version
The VS version is a little more complicated because it has to work around the lack of the GCC extension
__typeof__and block-expressions, and the version of VS it was written for (6) didn’t haveautoor other fancy C++11 features.Let’s look at an example expansion for what we used earlier:
The
if(0){}elseis because VC++ 6 did the scoping offorvariables wrong and a variable declared in the initialisation part of aforloop could be used outside the loop. So it’s a workaround for a VS bug. The reason they didif(0){}elseinstead of justif(0){...}is so that you can’t add anelseafter the loop, likeSecond, let’s look at the initialisation of the outer
for:The definition of
QForeachContainerBaseis:And the definition of
qForeachContainerNewisAnd the definition of
QForeachContainerisSo to make up for the lack of
__typeof__(which analogous to thedecltypeof C++11) we have to use polymorphism. TheqForeachContainerNewfunction returns aQForeachContainer<T>by value but due to lifetime extension of temporaries, if we store it in aconst QForeachContainer&, we can prolong it’s lifetime till the end of the outerfor(actually theifbecause of VC6’s bug). We can store aQForeachContainer<T>in aQForeachContainerBasebecause the former is a subclass of the latter, and we have to make it a reference likeQForeachContainerBase&instead of a value likeQForeachContainerBaseto avoid slicing.Then for the condition of the outer
for:The definition of
qForeachContainerisAnd the definition of
qForeachPointerisThis is where you might not be aware of what’s going on since these functions seem kind of pointless. Well here’s how they work and why you need them:
We have a
QForeachContainer<T>stored in a reference to aQForeachContainerBasewith no way to get it back out (that we can see). We have to cast it to the proper type somehow, and that’s where the two functions come in. But how do we know what type to cast it to?A rule of the ternary operator
x ? y : zis thatyandzmust be of the same type. We need to know the type of the container, so we use theqForeachPointerfunction to do that:The return type of
qForeachPointerisT*, so we use template type deduction to deduce the type of the container.The
true ? 0 : qForeachPointer(cont)is to be able to pass aNULLpointer of the right type toqForeachContainerso it will know what type to cast the pointer we give it to. Why do we use the ternary operator for this instead of just doingqForeachContainer(&_container_, qForeachPointer(cont))? It’s to avoid evaluatingcontmany times. The second (actually third) operand to?:is not evaluated unless the condition isfalse, and since the condition istrueitself, we can get the right type ofcontwithout evaluating it.So that solves that, and we use
qForeachContainerto cast_container_to the right type. The call is:And again the definition is
The second parameter will always be
NULLbecause we dotrue ? 0which always evaluates to0, and we use qForeachPointer to deduce the typeT, and use that to cast the first argument to aQForeachContainer<T>*so we can use its member functions/variables with the condition (still in the outerfor):And
conditionreturns:which is the same as the GCC version above except that it increments
brkafter evaluating it. So!brk++evaluates totrueand thenbrkis incremented to 1.Then we enter the inner
forand begin with the initialisation:Which just sets the variable to what the iterator
iis pointing to.Then the condition:
Since
brkis 1, the body of the loop is entered, which is our comment:Then the increment is entered:
That decrements
brkback to 0. Then the condition is checked again:And
brkis 0 which isfalseand the loop is exited. We come to the increment part of the outerfor:And that increments
ito the next element. Then we get to the condition:Which checks that
brkis 0 (which it is) and increments it to 1 again, and the process is repeated ifi != e.This handles
breakin client code only a little differently than the GCC version, sincebrkwill not be decremented if we usebreakin our code and it will still be 1, and thecondition()will be false for the outer loop and the outer loop willbreak.And as GManNickG stated in the comments, this macro is a lot like Boost’s
BOOST_FOREACHwhich you can read about here.