Boost... wow

Started by
11 comments, last by Fruny 19 years, 8 months ago
Will someone explain how boost's for_each works? It apperently can take an expression, and exectute it:
for_each(a.begin(), a.end(), std::cout << _1 << ' ');
I downloaded the library, but it looks like I will have to read up on tons of boost's types. Does anyone have a short, simple explaination of how on earth an expression can be passed to a function? Or am I overestimating it?
Not giving is not stealing.
Advertisement
either for_each is a macro or << is an overloaded operator either returning a reference or a value.
for_each is a STL algorithms that can be implemented as follow.

template<typename FwdIter, typename Functor>Functor for_each( FwdIter begin, FwdIter end, Functor func){   while(begin != end)     func(*begin++);   return func;}


The real magic thingy is that unassuming _1 placeholder.

_1 is a reference to a global object of type boost::lambda::placeholder1_type.

operator<<() is overloaded to take such an object as a parameter, returning a function object which, when called, do perform the appropriate action.

That is std::cout << _1 returns an object holding a reference to std::cout which, when called with a parameter foo does call operator<<(std::cout, foo).

That 'function call' operator is templated to accept any foo parameter.

Further work is done so that the function object returned can be used as _1 was originally, making further combination, like std::cout << _1 << ' ' possible.

There's a lot more going on behind the scenes - boost::lambda is one of the most complicated libraries in Boost.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
for_each is a STL algorithms that can be implemented as follow.

template<typename FwdIter, typename Functor>Functor for_each( FwdIter begin, FwdIter end, Functor func){   while(begin != end)     func(*begin++);   return func;}


The real magic thingy is that unassuming _1 placeholder.

_1 is a reference to a global object of type boost::lambda::placeholder1_type.

operator<<() is overloaded to take such an object as a parameter, returning a function object which, when called, do perform the appropriate action.

That is std::cout << _1 returns an object holding a reference to std::cout which, when called with a parameter foo does call operator<<(std::cout, foo).

That 'function call' operator is templated to accept any foo parameter.

Further work is done so that the function object returned can be used as _1 was originally, making further combination, like std::cout << _1 << ' ' possible.

There's a lot more going on behind the scenes - boost::lambda is one of the most complicated libraries in Boost.


Nice reply.

So they overload cout's << stream operator? How is that done without including their own iostream with boost?
Not giving is not stealing.
Quote:So they overload cout's << stream operator?


If I understand fruny correctly they did not. They've created a templated << operator which takes two arguments (the two sides of the << operator) the first of which can be any type specified by the template parameter, the second of which is of type boost::lambda::placeholder1_type. This operator then returns an reference that when called using the ( ) operator calls operator << passing in as the first arugment what was on the left of the original << operator and on the right whatever was passed to the ( ) operator when it was called.
Quote:Original post by Monder
Quote:So they overload cout's << stream operator?


If I understand fruny correctly they did not. They've created a templated << operator which takes two arguments (the two sides of the << operator) the first of which can be any type specified by the template parameter, the second of which is of type boost::lambda::placeholder1_type. This operator then returns an reference that when called using the ( ) operator calls operator << passing in as the first arugment what was on the left of the original << operator and on the right whatever was passed to the ( ) operator when it was called.


Nifty. That does make it seem a lot worse though. For example, x += 10 could not be used as the expression.
Not giving is not stealing.
Quote:Original post by thedevdan
Nice reply.

So they overload cout's << stream operator? How is that done without including their own iostream with boost?


First, it is not "cout's << stream operator". operator<< is the bitshift operator, which just happen to also be overloaded for std::basic_ostream, of which std::cout is an instance, to do stream output.

But, yes, they do write an overload for basic_ostream (they treat it as a special case, see boost/lambda/detail/operators.hpp). They do refer to the standard iostream classes which are included with your compiler. They do pull some tricks so as not to require you to include the appropriate iostream headers before the boost::lambda headers -- template metaprogramming at its best -- but you still have to include them yourself at some point.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
Quote:Original post by thedevdan
Nice reply.

So they overload cout's << stream operator? How is that done without including their own iostream with boost?


First, it is not "cout's << stream operator". operator<< is the bitshift operator, which just happen to also be overloaded for std::basic_ostream, of which std::cout is an instance, to do stream output.

But, yes, they do write an overload for basic_ostream (they treat it as a special case, see boost/lambda/detail/operators.hpp). They do refer to the standard iostream classes which are included with your compiler. They do pull some tricks so as not to require you to include the appropriate iostream headers before the boost::lambda headers -- template metaprogramming at its best -- but you still have to include them yourself at some point.


I know that << is the bitshift operator, I was being more descriptive. Still, it doesn't seem terribly useful as you can't have normal expressions like x += 10. Thanks for you persistent help!
Not giving is not stealing.
Quote:Nifty. That does make it seem a lot worse though. For example, x += 10 could not be used as the expression.


It sure can, they also made overloads for operator +=.

  GNU nano 1.3.2                    File: foo.cc                      Modified#include <iostream>#include <boost/lambda/lambda.hpp>#include <vector>#include <algorithm>using namespace std;using namespace boost::lambda;int main(){  vector<int> vec;  vec.push_back(vec.size());  vec.push_back(vec.size());  vec.push_back(vec.size());  vec.push_back(vec.size());  vec.push_back(vec.size());  for_each( vec.begin(), vec.end(), _1 += 10);  for_each( vec.begin(), vec.end(), cout << _1 << '\n' );  int sum = 0;  for_each( vec.begin(), vec.end(), sum += _1 );  // though you're better off using accumulate()  cout << sum << endl;}


There are limitations to what it can do though, you should read the doc for details.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
Quote:Nifty. That does make it seem a lot worse though. For example, x += 10 could not be used as the expression.


It sure can, they also made overloads for operator +=.

*** Source Snippet Removed ***


Geeze, they must have worked hard. [wow]

So it works with any single expression using STL/built-in-types (or anything castable to them)?

Still, I definitely see what you mean about fighting the language. I still am confused what _1 evalutates to in 'sum += _1'. I guess it picks up it's value from a previous for_each.
Not giving is not stealing.

This topic is closed to new replies.

Advertisement