Functors, STL algorithms, how to?

Started by
36 comments, last by persil 18 years, 10 months ago
Quote:
swish stuff by Fruny


hmmm you know, I never thought of handling them like that.. I'll have to try intergrating that info my code as it certainly fits my style defining functions/for-loops/while-loops et al
Advertisement
Quote:Original post by _the_phantom_
Frankly, isnt as clear by a long shot.
I bet at least 90% of C++ programmers disagree with you there, but hey, you're entitled to your opinion.
Quote:which is all fun and games until someone helpfully redefines the FOR_EACH macro with their own "helpfull" code.
In the unlikely event of that happening, there's always #undef. Or just to be on the safer side, use less idiomatic name for the macro like xFOR_EACH. This really is all fun and games!
Quote:Also, this code hides the intent of the function
Heh, I can see how
bind(&entity::set_visible, boost::tuple::get(_1), true)
hides the intent, but a clear FOR_EACH-macro..Nope.
Quote:doesnt allow you to specifiy a subrange
Either modify the macro to do that, write another macro to do that, or write subrange iterations as plain for/while/whatever loops. At least IME it's much more common to loop through the whole container instead of a subrange, and making the common case easy is a sound strategy.
Quote:and requires you to perform some setup by telling it the type you are operating on, a nightmare if the type changes
Oh please, that's what typedefs are for.
Quote:might as well use WARTs on ya varibles if ya gonna do that, and supplying it with an iterator to work with which would require an extra declaration, so you're one line macro solution is hard to maintain and requires two lines... hardly compact and not a win at all... (unless you like highly coupled code which is a pain to maintain ofcourse)
All void thanks to typedef. I like maintainability, and readability is a large part of it.
Quote:No, I think I'll avoid macros and stick to the standard, thanks for playing all the same...
Boost is not part of the standard. This kind of simple macro would be custom library all the same.
Quote:Original post by _the_phantom_
No, I think I'll avoid macros and stick to the standard, thanks for playing all the same...

macros are described in the standard and very useful for metaprogramming and many other things. i'm sure we all make use the CPP in every program. don't fear the preprocessor.

Quote:Original post by Sneftel
Quote:Original post by mfawcett
And although it's been said (and proven) time and time again, in books, and on these boards, for_each is usually faster than a hand written loop.

Not quite; you're confusing it with algorithms that can exploit optimized ASM loops. std::copy and std::fill on vectors are faster than a hand-written loop. std::for_each is faster than a badly done hand-written loop. std::for_each on a map, however, is likely to be the same speed as, or slower than, the equivalent hand-written loop (it depends on how intelligent your compiler is, and how well designed your STL is).


It should be noted that not only optimal methods for POD-types are used (where applicable) but also more efficent forms of iteration maybe taken for iterators in certain iterator category, for example std::copy on GCC when the iterators are random accessible and the type is a NON POD-type it will do tag dispatch at compile-time to use this form of iteration:

//....typedef typename std::iterator_traits<RandomAccessIterator>::difference_type          Distance;for(Distance n = last - first; n > 0; --n)//...


reason being as it has more of an opportunity of loop unrolling as opposed to the common "itr != end" idiom.

I'm not making it up either:

// ....// if random access iterators are passed, then the// loop count will be known (and therefore a candidate for compiler//  optimizations such as unrolling).


Another thing is most people when writing explicit loops tend to do:

for(const_iterator itr = foo.begin(); itr != foo.end(); ++itr)  //....


where typically "end" never changes

so it would have been better to write:

for(const_iterator itr = foo.begin(), end = foo.end(); itr != end; ++itr)   //...


With the generic algorithms there is no need to remember to do that when you can.

In the case of std::for_each typically no other optimal method is taken because its so general, same goes for iterators typically most/all imps do not take advantage of iterator categories like as GCC's imp of std::copy's for NON pod-types but using random accessible iterators.

Quote:Original post by Anonymous Poster
Boost is not part of the standard. This kind of simple macro would be custom library all the same.


They are more close to the standard than you may believe, a number of components of boost are currently being reviewed to be included in the C++ standard and already some compilers have implementated some of it std::tr1.
Quote:Original post by mgarriss
macros are described in the standard and very useful for metaprogramming and many other things. i'm sure we all make use the CPP in every program. don't fear the preprocessor.


I know this and I dont fear it I just find that avoiding it makes for less possible errors.

I should have really said standard algos. instead of standard, they are there for a reason as snk_kid points out they have optimisations you wont think of (or if you do wont all the time) and once you get used to them they are alot safer to work with than macro hacks.

Quote:Original post by _the_phantom_
Quote:Original post by mgarriss
macros are described in the standard and very useful for metaprogramming and many other things. i'm sure we all make use the CPP in every program. don't fear the preprocessor.


I know this and I dont fear it I just find that avoiding it makes for less possible errors.

I should have really said standard algos. instead of standard, they are there for a reason as snk_kid points out they have optimisations you wont think of (or if you do wont all the time) and once you get used to them they are alot safer to work with than macro hacks.


+, as you probably know

macros are the only way to stringify identifiers and paste identifiers together (using # or ## respectively)

Alexandrescu and Torjo provided a clever example on how to use macros for a industrial strength assert-like function.

Regards,
w00t! I generated quite a fuss :)

Thanks for all the replies and discussion, I don't know if someone already tried what's been recommended, but I'll be sure to try it!

This lambda functional programming, I think it's very appealing for concealness, because I'd hate to have to define a functor for every task I have to do that differs of a micron to the standard stuff.

Personally, to continue what seems to have ignited, I think that to a programmer that knows how to use STL, both the standard loop and the STL for_each with bind will be easily understood. The thing that appeals to me with functional programming is the fact that I can do more with less code, generally, and if it can be optimized better by the compiler, that's even better :)

Someone pointed out to define an end iterator specifically for a for() loop, I never thought of that, I always "hoped" the compiler was smart enough to do it for me, but now that this post has made me think about it, how could the compiler know for sure that .end() will return the same thing, there's no way, whereas in the for_each algo, it knows. Cool :)

On another point, I agree about the standardness of Boost. Next to the STL, I don't think there are many libraries that are as extensive and as known than Boost. The fact that part of it is evaluated for the standard says it all.
Quote:Original post by Fruny
It's not so much a question of speed as a question of correctness and of explicitely telling what the loop's purpose is. Plus you don't have to define an iteration variable. [smile] It's true that for_each isn't the best example, but that's also because it's the simplest. If you only even upgrade to find_if then, in my opinion, things start to frankly favor algorithms over hand-written loops.
I'll also grant you that having to separatly define predicates and functors is unwieldy, but short of introducing generic lambda expressions into C++, things like boost::bind, boost::lambda or FC++ are the best we're going to get. Sure, it's unfamiliar to many here but, you know, if that judgement is all that's stopping you from learning how things work, you'll never improve and it'll forever remain unfamiliar and uncomfortable. (<troll> kinda like goto </troll> [evil][smile]).

Even then, a bit of indentation definitely makes things more readable:
std::for_each(   m_map.begin(), m_map.end(),    bind   (      &entity::set_visible,       boost::tuple::get<1>(_1), true   ));


In the past month, I've begun treating parentheses and angle brackets in algorithms and template as if they were braces when it's appropriate (as the BLL use of if_(condition)[then_part].else[else_part] implicitely suggests). Sure, it does break the one-lineness that attracts some people, but writing pithy one-liners isn't quite the point of standard algorithms, is it?


It's me again! :)

I tried this method Fruny and it doesn't work. The Tuples library complains that it cannot find a matching function to call get<> with a lambda placeholder. :(

This topic is closed to new replies.

Advertisement