Sign in to follow this  

Boost... wow

This topic is 4867 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Quote:
Original post by thedevdan
Geeze, they must have worked hard.


There aren't THAT many operators, and Boost provide some extremely nifty macros (have a look at the boost preprocessor library, and get ready to be wowed again).

But yeah, there's hard work involved. And many, many, many 'broken compiler' workarounds (as well as language workarounds)

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


The STL is not involved, except as a consumer of the function object. That is, for_each will, in turn, call the function object that boost::lambda create, passing it container elements, one by one.

And boost::lambda does have its limitations. There are some expressions it cannot deal with without modifications. One simple example is cout << ' ' << _1. The problem is that, following language rules, cout << ' ' is evaluated first and, since boost::lamba is not yet involved, that's an ordinary output statement (though, you know, iostreams are somewhat magic too [smile]), and the expression doesn't "do what you mean".
To fix that, you have to wrap the ' ' into boost::lambda's representation of a constant: cout << constant(' ') << _1. While still more convenient than writing your own functor, it's not quite as elegant.

Quote:
Still, I definitely see what you mean about fighting the language.


Heh, I haven't tried understanding all the implementation details, where they do fight the limitations of the language (e.g. no references to references), and strive to make the library efficient. But the overall technique is relatively easy to understand - it's called "expression templates" - the expression returns an object which, when called, will execute the operation the expression was intended to represent.

Still, it's nice to see functional-language-style lambdas expressions in C++ - even in such a limited form [wink]

Quote:
I still am confused what _1 evalutates to in 'sum += _1'. I guess it picks up it's value from a previous for_each.


sum += 1 evaluates to a function object which takes a single parameter and adds it to the sum variable which it holds as a reference (another case of 'do what I mean' special-casing, it wouldn't make much sense for += to work on a copy of sum that gets discared).
The for_each algorithm feeds that function object with values from the vector.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Fruny
There's a lot more going on behind the scenes - boost::lambda is one of the most complicated libraries in Boost.


That's too bad, lambda expressions are supposed to be simple.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
That's too bad, lambda expressions are supposed to be simple.


"simple to use" generally means "hard to implement". Ask any library writer -- or for that matter, game engine writer [smile]

Share this post


Link to post
Share on other sites

This topic is 4867 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this