
I just tried using the lambda library for the first time. Here's what came out of it, after some struggle: #include <boost/lambda/lambda.hpp> #include <vector> #include <numeric> using namespace boost::lambda; struct X { X() : count(1) {} unsigned long count; }; std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + (&_2) ->* &X::count); This program adds up the "count" members in a vector of X objects. Now, no offense intended, but this is nasty. There's just too much extra syntax required for something so simple, plus you have to make the type into a pointer just so you can dereference it Before I realized I needed to take the address of _2, I had: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2 ->* &X::count); which is only slightly better (but incorrect). I realize that I can use bind: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + bind(&X::count,_2)); but that's slightly counter-intuitive since the order of the target object and its member pointer are reversed. It'd be nice to allow something like: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2.at(&X::count)); while if _2 were to refer to a pointer type, we could say: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2->at(&X::count)); -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Mar 3, 2004, at 3:31 PM, David Abrahams wrote:
Now, no offense intended, but this is nasty. There's just too much extra syntax required for something so simple, plus you have to make the type into a pointer just so you can dereference it
I agree, it is nasty. The guiding principle of lambda was to provide a 'delayed' version of each C++ operator, which is what ->* does. As you point out, it can lead to making simple use cases quite not so simple. It therefore makes sense to provide shortcuts (sparingly) for common use cases, like the one you showed. I'd really like to be able to overload . though :) Jaakko
object and its member pointer are reversed. It'd be nice to allow something like:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2.at(&X::count));
while if _2 were to refer to a pointer type, we could say:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2->at(&X::count));
Jaakko

David Abrahams wrote:
I realize that I can use bind:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + bind(&X::count,_2));
but that's slightly counter-intuitive since the order of the target object and its member pointer are reversed.
It sure would've been nice to be able to write _2.count, or count(_2), or _2.count(), but we can't, so we bind(). ;-) The regular syntax has its advantages, and the initial "member pointer first" surprise is a one-time adjustment.

"Peter Dimov" <pdimov@mmltd.net> writes:
It sure would've been nice to be able to write _2.count, or count(_2), or _2.count(), but we can't, so we bind(). ;-) The regular syntax has its advantages, and the initial "member pointer first" surprise is a one-time adjustment.
It's an issue of how far the language is, mentally, from the domain abstraction it represents. _2.member(&X::count) maps more directly to _2.count than bind(&X::count,_2) does. For me. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Peter Dimov" <pdimov@mmltd.net> writes:
It sure would've been nice to be able to write _2.count, or count(_2), or _2.count(), but we can't, so we bind(). ;-) The regular syntax has its advantages, and the initial "member pointer first" surprise is a one-time adjustment.
It's an issue of how far the language is, mentally, from the domain abstraction it represents.
_2.member(&X::count)
maps more directly to
_2.count
than
bind(&X::count,_2)
does. For me.
Yep, but you are optimizing the rare "public member" case. Are you going to introduce new syntax for count(_2) and _2.count() as well? bind(count, _2) bind(&X::count, _2) That's the regularity I spoke of.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:000e01c40178$27c45ca0$1d00a8c0@pdimov2...
Yep, but you are optimizing the rare "public member" case.
I beg to differ. Simple ADTs are likely to have public members, ditto std::pair<>. Not to mention the fact that the member does not have to be public if the pointer you're binding to it is "this" or if you're in the "friendly" code. In fact, the problem David described was what largely prevented me from using BLL in my code -- an inline function + boost::ptr_fun() is very often easier to type and easier to read. ...Max...

David Abrahams wrote:
I just tried using the lambda library for the first time. Here's what came out of it, after some struggle:
#include <boost/lambda/lambda.hpp> #include <vector> #include <numeric>
using namespace boost::lambda;
struct X { X() : count(1) {}
unsigned long count; };
std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + (&_2) ->* &X::count);
Views make it much cleaner, both conceptually and syntactically: unsigned long n = accumulate( transform_view( xs, mem_fn(&X::count) ) , 0UL ); -- Aleksey Gurtovoy MetaCommunications Engineering

Aleksey Gurtovoy wrote:
David Abrahams wrote:
I just tried using the lambda library for the first time. Here's what came out of it, after some struggle:
#include <boost/lambda/lambda.hpp> #include <vector> #include <numeric>
using namespace boost::lambda;
struct X { X() : count(1) {}
unsigned long count; };
std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + (&_2) ->* &X::count);
Views make it much cleaner, both conceptually and syntactically:
unsigned long n = accumulate( transform_view( xs, mem_fn(&X::count) ) , 0UL );
Indeed ;) Using views/ranges makes the code much simpler/clearer. In time, I've accepted the fact that there's not much point in using in-place functions (like _1 + _2, etc.), since in real-life they just complicate the code. So I just create a new function (in anonymous namespace), and that's it. And I use boost::bind, which rocks big time :D My $0.02 Best, John

John Torjo <john.lists@torjo.com> writes:
Aleksey Gurtovoy wrote:
David Abrahams wrote:
I just tried using the lambda library for the first time. Here's what came out of it, after some struggle:
#include <boost/lambda/lambda.hpp> #include <vector> #include <numeric>
using namespace boost::lambda;
struct X { X() : count(1) {}
unsigned long count; };
std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + (&_2) ->* &X::count);
Views make it much cleaner, both conceptually and syntactically:
unsigned long n = accumulate( transform_view( xs, mem_fn(&X::count) ) , 0UL );
Indeed ;) Using views/ranges makes the code much simpler/clearer.
In time, I've accepted the fact that there's not much point in using in-place functions (like _1 + _2, etc.), since in real-life they just complicate the code. So I just create a new function (in anonymous namespace), and that's it.
And I use boost::bind, which rocks big time :D
What do you suppose boost::bind does other than creating an in-place function? I believe you guys are confusing issues. While I think views have benefits, the problems I mentioned above were not caused by the use of iterators or in-place functions: std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + bind(&X::count, _2)); Is really no worse than the alternative cited above: unsigned long n = accumulate( transform_view( xs, mem_fn(&X::count) ) , 0UL); and the one that uses std::plus instead of "_1 + ..." is really no fun at all. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
John Torjo <john.lists@torjo.com> writes:
Aleksey Gurtovoy wrote:
David Abrahams wrote:
I just tried using the lambda library for the first time. Here's what came out of it, after some struggle:
#include <boost/lambda/lambda.hpp> #include <vector> #include <numeric>
using namespace boost::lambda;
struct X { X() : count(1) {}
unsigned long count; };
std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + (&_2) ->* &X::count);
Views make it much cleaner, both conceptually and syntactically:
unsigned long n = accumulate( transform_view( xs, mem_fn(&X::count) ) , 0UL );
Indeed ;) Using views/ranges makes the code much simpler/clearer.
In time, I've accepted the fact that there's not much point in using in-place functions (like _1 + _2, etc.), since in real-life they just complicate the code. So I just create a new function (in anonymous namespace), and that's it.
And I use boost::bind, which rocks big time :D
What do you suppose boost::bind does other than creating an in-place function? I believe you guys are confusing issues. While I think
Of course, I knew that ;) I did misexplain however. What I should have said was that I prefer to create a simple and straightforward function (usually a one-liner) and use it with boost::bind over using boost::lambda. Makes the code more self-explanatory.
views have benefits, the problems I mentioned above were not caused by the use of iterators or in-place functions:
std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + bind(&X::count, _2));
Is really no worse than the alternative cited above:
unsigned long n = accumulate( transform_view( xs, mem_fn(&X::count) ) , 0UL);
what we were trying to say was that if you use views/ranges, problems like the one you've shown above usually don't appear/ appear very seldom (at least to me ;) ). Best, John
participants (6)
-
Aleksey Gurtovoy
-
David Abrahams
-
Jaakko Jarvi
-
John Torjo
-
Max Motovilov
-
Peter Dimov