John Black said:
Hi,
I am not familiar with for_each very well, suppoase I have a
vector< pair< unsigned int, unsigned int > > vec1 and the contents are
{ < 0x00000000, 0x000000FF >, < 0x10000000, 0x2FFFFFFF > }
what I want is to create another vector, vector< int > vec2, which store
all the first 8 bit heads of the integer in vec1, for the above example,
0x00000000 & 0xFF000000 == > 0x00000000,
0x000000FF & 0xFF000000 == > 0x00000000,
0x10000000 & 0xFF000000 == > 0x10000000,
0x2FFFFFFF & 0xFF000000 == > 0x2F000000,
then vec2 should be {0x00000000, 0x10000000, 0x2F000000}.
You only put 3 items in vec2, was that on purpose? IE are repeted values
not supposed to be entered?
If that's the case, I would insert into a set first, then copy from the
set to the vector (see below...)
It's easy to specifically write a for loop, just wonder how to do it
use for_each.
The same way, except the loop body would be in a functor. So you might
start with:
for ( vector< pair< unsigned, unsigned > >::iterator it = v.begin();
it != v.end(); ++it )
{
v2.push_back( it->first & 0xFF000000 );
v2.push_back( it->second & 0xFF000000 );
}
This would become:
struct filler {
vector< unsigned >& v;
filler( vector< unsigned >& v ): v( v ) { }
void operator()( const pair< unsigned, unsigned >& p ) {
v.push_back( p.first & 0xFF000000 );
v.push_back( p.second & 0xFF000000 );
}
};
and:
for_each( v.begin(), v.end(), filler( v2 ) );
Seems like a bit of a waste. We've created a new type, that can only be
used in this one place, and when someone encounters the 'for_each' he
needs to search the code to find out what this new type does.
What if we could make something more resuable, and at the same time do a
better job of expressing what is happening at the place where it is
supposed to happen?
Let's take a look at what we are doing. Basically it's a transform
except each value in the first container is being transformed twice and
placed in the second container. So we can model something off of
std::transform.
template < typename InputIter, typename OutputIter,
typename Op1, typename Op2 >
OutputIter expanding_transform( InputIter first, InputIter last,
OutputIter result, Op1 op1, Op2 op2 )
{
while ( first != last )
{
*result++ = op1( *first );
*result++ = op2( *first );
++first;
}
return result;
}
Maybe the above isn't all that reusable, but it's certanly more resuable
than the 'filler' type above. For example, we can use this function to
output our vector< pair< unsigned, unsigned > > to cout... The fact that
we can already see two uses for this function argues well for its
reusablity.
Now, what arguments do we need to pass into this function? The first two
are easy 'v.begin()' and 'v.end()' we see this all the time in the
standard algorithms. The third is a little harder, but still often seen:
'back_inserter( v2 )'. Of course 'v' is our vector< pair< unsigned,
unsigned > > and 'v2' is our new vector< unsigned >... For the last two
parameters, we need to do some more work.
First we examine the 'functional' part of the stl. We find a
'logical_and' functor, but no 'bit_and'. Oh well, that's easy to fix:
template < typename T >
struct bit_and : public std::binary_function< T, T, T >
{
T operator()( const T& x, const T& y ) const { return x & y; }
};
So, for each of the Ops in our expanding_transform, we need to call
'bit_and' were the first argument is the first and second item in the
pair, and the second argument is 0xFF000000. Let's make a functor that
does that:
binder2nd< bit_and< unsigned > > high_8_mask =
bind2nd( bit_and< unsigned >(), 0xFF000000 );
Let's make sure we know what high_8_mask does:
assert( high_8_mask( 0x12345678 ) == 0x12000000 );
Unfortunatly, we can't just pass our pair< unsigned, unsigned > into the
above because it only takes one unsigned value.
Now we have to look in the past. The origional STL had several useful
functors that never made it into the standard. Two of them specifically
dealt with manipulating pair objects so that they could be used in
standard functors: select1st and select2nd. You can find them on SGI's
website and I'm sure other websites have them. They both do exactly what
you would think. You pass in a pair object, and it returns either the
first or second object of the pair.
Another useful functor is 'unary_compose'; which takes two other
functors and combines them like this: f1( f2( x ) ). There is also a
useful function 'compose1' that helps build a unary_compose objects (
like the way 'make_pair' builds 'pair' objects. ) You can also find this
in boost where it's called 'compose_f_gx'...
With these three functors in our arsenal we can build the functors that
will be used by our new algorithm.
I also want to make the pair easer to work with so I'll make a typedef.
Now I'll put all of this together to show how it works:
typedef pair< unsigned, unsigned > two_flags;
binder2nd< bit_and< unsigned > > high_8_mask =
bind2nd( bit_and< unsigned >(), 0xFF000000 );
expanding_transform( v.begin(), v.end(), back_inserter( v2 ),
compose1( high_8_mask, select1st< two_flags >() ),
compose1( high_8_mask, select2nd< two_flags >() ) );
There you go, we have written the loop using lots of small, highly
reusable components.
Now back to my question above about no repeat values. If that's the case
then insert into a set:
set< unsigned > s;
binder2nd< bit_and<unsigned int> > high_8_mask =
bind2nd( bit_and< unsigned >(), 0xFF000000 );
expanding_transform( v.begin(), v.end(),
inserter( s, s.begin() ),
compose1( high_8_mask, select1st< two_flags >() ),
compose1( high_8_mask, select2nd< two_flags >() ) );
copy( s.begin(), s.end(), back_inserter( v2 ) );