Summary:
Plowing through some devilish details of template argument deduction, the conditional operator and the macro preprocessor, Eric develops a robust FOR_EACH iterator. Whether you're using arrays, strings, or containers, this one does it all.
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: April 2, 2005 7:59 AM by
Eric
|
Eric Niebler shows how to use the subtleties of the conditional operator, coupled with templates and macro magic, to create a robust iteration construct. http://www.artima.com/cppsource/foreach.html
|
|
|
I really had a great time reading this article. I just loved the cleverness of using the ternary operator to get the type and pass it to auto_any functions. This is one of those articles that just amaze you, because you already knew how the parts work but it would never have ocurred you to combine them.
The part about rvalue detection isn't that great, though, as the you knew it part doesn't exist.
|
|
|
Awesome article, FOREACH is the kind of utility that can massively improve the quality of C++ code and it is amazing to see how it is implemented.
I decided to delve deeper and looked at foreach.hpp. I noticed a number of difference from the article, I guess these were mainly for exposition purposes.
BOOST_FOREACH doesnt use boost::variant, it has a mini roll-your-own simple_variant class, any reason why? Also simple_variant doesnt have an assignment operator, i guess the default implementation is safe here.
Things like rvalue_probe and auto_any could be useful in other contexts, are they used elsewhere in Boost or the wider world? If so should they be factored out into their own headers?
Finally is it safe to nest two BOOST_FOREACH macros?
|
|
|
> Awesome article
Thanks!
> BOOST_FOREACH doesnt use boost::variant, it has a mini > roll-your-own simple_variant class, any reason why?
I found it was hurting compile times. It's a bigger hammer than I really need in this case.
> Also simple_variant doesnt have an assignment operator, > i guess the default implementation is safe here.
It's not! But I never assign one of these guys. I should make the assignment operator private, thanks!
> Things like rvalue_probe and auto_any could be useful > in other contexts, are they used elsewhere in Boost or > the wider world? If so should they be factored out into > their own headers?
I'll factor them out as need requires.
> Finally is it safe to nest two BOOST_FOREACH macros?
You bet!
-- Eric Niebler Boost Consulting www.boost-consulting.com
|
|
|
Incidentally, I have found a simpler formulation of ENCODED_TYPEOF that doesn't require type2type or anytype. Consider:
template<class T> T const * encode_type( T const & t ) { return 0; }
#define ENCODED_TYPEOF( expr )\ ( true ? 0 : encode_type( expr ) )
This uses the fact that the null pointer constant (0) is implicitly convertible to a pointer of any type. Now, you can use "T const*" as a dummy parameter to functions that need to infer the type T. Not only is this simpler, but it even works on ancient compilers like VC6.
Also, Andrei Alexandrescu has sent me a "perfect" version of the MIN/MAX macros that use the conditional operator trick to avoid reevaluation of their arguments. Very neat stuff! We'll hopefully be posting it soon.
-- Eric Niebler Boost Consulting www.boost-consulting.com
|
|
|
Whew! A lot of nice stuff - I'll have to reread this a few times. I personally love the conditional operator, and use it a lot in both C and gawk. My "secret" to making nested conditionals understandable is to treat it as being precisely analogous to nested if-then-else statements. An example is a = (b > 0 ? (c/2) : (g in hoohaa ? (int(h) == h ? (cos(r) <= sin(r) ? tanh(e) : lngamma(pi) ) : (2/zeta(e)) ) : (g-1) ) );
(Hope everything is lined up and balanced - Emacs helps a lot)
|
|
|
Despite of my absolute loving this facility,I wonder if there exists one little pitfall.
It seems to me that the problem raises its ugly head when we use BOOST_FOREACH for some container that contains instances of type like std::string or whatever owns its own resource.
IMO,some classes that hold their own resources will do copy assignment differently as they do "destruction-then-in-place-construction".
Since BOOST_FOREACH uses nested for-statement,the variable in the inner loop will get initialized each time the outer for-loop recurs.Note that I refered to "initialize" instead of "copy-assignment",the former means a destruction followed by a construction,while the latter means merely one copy assignment.
For me,the situation can become really tough when the class gets a optimized copy assignment which can reuse the previously allocated storage to hold the new stuffs,while its destructor *delete* the storage and its constructor *re-require* them.
e.g.
for(string s = ...;...;...) { s = ...; // copy assignment , the lifetime of s lasts until the for-statement ends }
// BOOST_FOREACH's implementation for(...) { for(string s = ...;...;...) // get re-constructed each time the outer loop recurs { } }
Surely,I know that there should be some caveat when using BOOST_FOREACH,one of which should refer to this.But I read through the document and see nothing about that,so I wonder if it's an oversight,or "not-a-problem",or merely my misunderstand?
Anyway,thank you for doing such an excellent job,it's really usefull and amazing.
|
|
|
liu wei peng, you are correct, there are scenarios where BOOST_FOREACH could cause an extra allocation over a hand-coded loop. Funny, that hadn't even occured to me. I don't see a way to fix it while allowing BOOST_FOREACH to continue working with references, because the reference simply must be rebound each time:
for(..;..;..) for(int &i = ..;..;..)
This is certainly a trade-off, though, and I see your point. You can avoid the construction/destruction issue if you use BOOST_FOREACH like this:
std:::string str; BOOST_FOREACH(str, vector_of_string) ..
There should probably be a note about this in BOOST_FOREACH's documentation.
Thanks for raising the issue.
-- Eric Niebler Boost Consulting www.boost-consulting.com
|
|