boost::function - why does this work??

Heya Everyone, I am trying to create a generic way to extract a list of objects from another list of objects. Let me explain! Say I have a vector (or any container with fwd iterators and value_type) that contains objects that have a function which returns a long (could actually be any type). I want to iterate over that vector and extract each of the longs into a list (or any container that supports push_back and defines value_type). Something like the following: class HandleClass { public: long handle() const { return val_++; } private: static long val_; }; // ... std::vector<HandleClass> listOfHcs; // ... fill listOfHcs here std::list<long> needToFill; OK so far. I then wrote this extraction function (yes, there are many ways I could have done this - with a functor and using foreach is another possibility worth exploring, but not now :) ). template < class ReturnContainer, class FwdIterator > void Extract(const FwdIterator & itBegin, const FwdIterator & itEnd, ReturnContainer & container, boost::function1<ReturnContainer::value_type, FwdIterator::value_type> f) { for (FwdIterator it = itBegin ;it != itEnd; ++it) { container.push_back(f(*it)); } }; And I was able to do this: Extract(listOfHcs.begin(), listOfHcs.end(), needToFill, &HandleClass::handle); And it worked like a charm. needToFill gets filled and I was happy. :) However we also had containers of _pointers_to_ HandleClasses. So in some cases our vector looked like: std::vector<HandleClass *> listOfHcs; Yet the same code worked! I couldn't, and can't, for the life of me understand what was going on (I expected that I would have to wrap the (*it) in a boost::remove_pointer which worried me because my compiler - MSVC7.0 - doesn't support partial template specialization). Don't get me wrong, I'm happy that it work but can anyone explain what fandangelory is going on inside boost::function (or perhaps it's some C++ magic?) to allow this? Cheers, Matt

Matt S Trentini wrote: [...]
And I was able to do this:
Extract(listOfHcs.begin(), listOfHcs.end(), needToFill, &HandleClass::handle);
And it worked like a charm. needToFill gets filled and I was happy. :)
However we also had containers of _pointers_to_ HandleClasses. So in some cases our vector looked like:
std::vector<HandleClass *> listOfHcs;
Yet the same code worked! I couldn't, and can't, for the life of me understand what was going on (I expected that I would have to wrap the (*it) in a boost::remove_pointer which worried me because my compiler - MSVC7.0 - doesn't support partial template specialization). Don't get me wrong, I'm happy that it work but can anyone explain what fandangelory is going on inside boost::function (or perhaps it's some C++ magic?) to allow this?
When you construct a boost::function from &HandleClass::handle, what actually gets stored is boost::mem_fn(&HandleClass::handle). See http://www.boost.org/libs/mem_fn/ Even std::vector< boost::shared_ptr<HandleClass> > listOfHcs; will work.

Peter Dimov wrote:
When you construct a boost::function from &HandleClass::handle, what actually gets stored is boost::mem_fn(&HandleClass::handle). See
http://www.boost.org/libs/mem_fn/
Even
std::vector< boost::shared_ptr<HandleClass> > listOfHcs;
will work.
Thanks Peter, makes sense after reading that documentation now. I've actually got another question - I'd really prefer to replace ReturnContainer with an output iterator type. So Extract would look like this: template < class InIterator, class OutIterator, > void Extract(const InIterator & itBegin, const InIterator & itEnd, OutIterator & itOut, boost::function1<OutIterator::value_type, FwdIterator::value_type> f) { for (FwdIterator it = itBegin ;it != itEnd; ++it) { *itOut++ = f(*it); } }; And then you'd typically call Extract with an inserter: Extract(listOfHcs.begin(), listOfHcs.end(), back_inserter(needToFill), &HandleClass::handle); But this doesn't compile because the inserters (at least on my platform) don't define value_type. I'd prefer to take an iterator because it gives greater flexibility (can use inserter, front_inserter or back_inserter) and it more closely models the standard algorithms. However, I need to know the return type of the function and I can't see any way to get it without specifying it as a template parameter. Actually, I think I could use the typedefs that are defined in the inserters to retrieve the container, and then the value type, but this then forces the client to use inserters. Am I overlooking something here? Cheers, Matt

Matt S Trentini wrote:
Peter Dimov wrote:
When you construct a boost::function from &HandleClass::handle, what actually gets stored is boost::mem_fn(&HandleClass::handle). See
http://www.boost.org/libs/mem_fn/
Even
std::vector< boost::shared_ptr<HandleClass> > listOfHcs;
will work.
Thanks Peter, makes sense after reading that documentation now.
I've actually got another question - I'd really prefer to replace ReturnContainer with an output iterator type. So Extract would look like this:
template < class InIterator, class OutIterator, > void Extract(const InIterator & itBegin, const InIterator & itEnd, OutIterator & itOut, boost::function1<OutIterator::value_type, FwdIterator::value_type> f) { for (FwdIterator it = itBegin ;it != itEnd; ++it) { *itOut++ = f(*it); } };
And then you'd typically call Extract with an inserter:
Extract(listOfHcs.begin(), listOfHcs.end(), back_inserter(needToFill), &HandleClass::handle);
But this doesn't compile because the inserters (at least on my platform) don't define value_type.
They aren't required to. You should use std::iterator_traits<It>::value_type. [...]
Am I overlooking something here?
1. Iterators are typically passed by value. 2. You don't need to use boost::function in a template. It is typically only used when you want a fixed compile-time signature. template<class InIt, class OutIt, class F> void Extract(InIt first, InIt last, OutIt out, F f) { for (; first != last; ++first, ++out) { *out = f(*first); } } Now look at std::transform. :-) (You'll no longer be able to pass &X::f directly, though, use mem_fn explicitly.)

Heya Peter, Peter Dimov wrote:
But this doesn't compile because the inserters (at least on my platform) don't define value_type.
They aren't required to. You should use std::iterator_traits<It>::value_type.
Ah, makes sense.
Am I overlooking something here?
1. Iterators are typically passed by value.
I've noticed that - is there are reason for this? My reason for passing by const reference is mostly academic anyway because copying iterators is typically cheap, I just have a curious nature... :)
2. You don't need to use boost::function in a template. It is typically only used when you want a fixed compile-time signature.
template<class InIt, class OutIt, class F> void Extract(InIt first, InIt last, OutIt out, F f) { for (; first != last; ++first, ++out) { *out = f(*first); } }
Now look at std::transform. :-)
Oh the shame of it! ;) Covered with embarrassment I've dutifully erased my Extract function... :) Kinda funny how sometimes you can't see the wood through the trees...only yesterday I recommended that a colleague use transform for (what I now recognise as) a similar application. I got hung up on the fact that I wanted to use boost::function I guess...
(You'll no longer be able to pass &X::f directly, though, use mem_fn explicitly.)
Got it. For those that are curious here's the improved solution that Peter was alluding to: std::transform(listOfHcs.begin(), listOfHcs.end(), back_inserter(needToFill), boost::mem_fn(&HandleClass::handle)); Thanks Peter - both for your help and your work in boost! :) Cheers, Matt
participants (2)
-
Matt S Trentini
-
Peter Dimov