Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

extract_second - removing the template argument...

36 views
Skip to first unread message

Early Ehlinger

unread,
Sep 28, 2004, 5:27:06 PM9/28/04
to
Consider this little library snippet:

namespace stdplus2
{
/*
extract_second
--------------
Overview:
Extracts the second value from a given pair:

Example:
extract_second< some_result_type > extractor;
std::pair< int , double > the_pair;
double second = extractor( the_pair );

Typical Use / Why bother?:
std::map< key , value_type > the_map;
// ... populate map ...

std::for_each
( the_map.begin()
, the_map.end()
, boost::bind
( &value_type::member_fn
, boost::bind
( stdplus2::extract_second< value_type >
, _1 ) ) );
// conceptually:
// for-each item in the_map,
// item.member_fn()
*/
template< typename RESULT_TYPE >
class extract_second
{
public:
typedef RESULT_TYPE result_type;

template< typename PAIR_TYPE >
typename PAIR_TYPE::second_type const&
operator()( PAIR_TYPE const& the_pair )
{ return the_pair.second; }

template< typename PAIR_TYPE >
typename PAIR_TYPE::second_type&
operator()( PAIR_TYPE& the_pair )
{ return the_pair.second; }
};
}

I am happy with the snippet with the exception of a syntactic annoyance,
which is the need to include the RESULT_TYPE parameter to extract_second.
It appears to be necessary because boost::bind needs the
extract_second::result_type typedef.

I'm reasonably sure that I have missed a nice implementation of
extract_second (probably with a different name, since Google turns up the
null set on a search for "extract_second", at least with regards to C++) by
somebody else - if that's the case, please point me in the right direction.

If not, I would greatly appreciate any insight you might have with regards
to removing the need for extract_second to be a template class. It is
rather cumbersome (not to mention error-prone) to have to provide the
template parameter, so I would obviously just as soon get rid of it.

Thanks in advance!

--
-- Early Ehlinger CEO, ResPower Inc - Toll-Free : 866-737-7697
-- www.respower.com -- 1.5 THz Self-Service Render Farm

[ Yes, 1.5 TeraHertz - 1,500 Gigahertz. 1,500,000 Megahertz. 1,500,000,000
Kilohertz. 1,500,000,000,000 Clock Cycles Per Second. Every second. 24
hours a day. 7 Days a week. Upload your 3D scenes and feel the speed! ]


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Maxim Yegorushkin

unread,
Sep 30, 2004, 6:48:00 AM9/30/04
to
Early Ehlinger <ea...@respower.com> wrote:

You forgot here that boost::bind also works with data members, so you
don't need the extractor.

Example:

std::pair<int, double> the_pair;
int& i = bind(&std::pair<int , double>::first, _1)(the_pair);
double& d = bind(&std::pair<int , double>::second, _1)(the_pair);

--
Maxim Yegorushkin

Maxim Yegorushkin

unread,
Oct 14, 2004, 6:02:05 PM10/14/04
to
Maxim Yegorushkin <e-m...@yandex.ru> wrote:

[]

> Here is the syntax:
>
> template<class T> inline T const& constify(T& t) { return t; }
>
> int main()
> {

[]

> // const iterators check
> copy(
> over_first(constify(m).begin())
> , over_first(constify(m).end())
> , ostream_iterator<int>(cout, "\n")
> );
> }

Sorry, the code won't really work with const_iterators, some additional
coding needed, but I hope you get the idea.

Early Ehlinger

unread,
Oct 14, 2004, 5:59:28 PM10/14/04
to
From: "Early Ehlinger" <ea...@respower.com>
> So is there a way to avoid repeatedly providing the type name in this
> case?

Maxim has sent a second reply that has reached me by email, but is not yet
visible (at least to me) via the newsgroup. I want to make sure to thank
him because his answer was *exactly* what I was looking for, but I'm afraid
that if I wait for it to show up on the newsgroup I'll forget.

His basic approach was to look at it from a different perspective. Instead
of trying to transform the _1 with a nested boost::bind, he transformed the
iterators being passed to for_each. Beautiful, simple, elegant. I even
showed it to a colleague who is not as comfortable reading template code and
he commented on how much easier it was to read than my canonical
stdplus2::extract_second sample.

So, Maxim, thanks. I hope somebody else out there finds as much benefit
from your input as I have; I also hope I can repay the favor at some point
;)

-- Early Ehlinger CEO, ResPower Inc - Toll-Free : 866-737-7697
-- www.respower.com -- 1.5 THz Self-Service Render Farm

[ Yes, 1.5 TeraHertz - 1,500 Gigahertz. 1,500,000 Megahertz. 1,500,000,000
Kilohertz. 1,500,000,000,000 Clock Cycles Per Second. Every second. 24
hours a day. 7 Days a week. Upload your 3D scenes and feel the speed! ]

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Maxim Yegorushkin

unread,
Oct 15, 2004, 7:10:01 PM10/15/04
to
Maxim Yegorushkin <e-m...@yandex.ru> wrote:

[]

> Here is the syntax:
>
> template<class T> inline T const& constify(T& t) { return t; }
>
> int main()
> {

[]

> // const iterators check
> copy(
> over_first(constify(m).begin())
> , over_first(constify(m).end())
> , ostream_iterator<int>(cout, "\n")
> );
> }

Sorry, the code won't really work with const_iterators, some additional
coding needed, but I hope you get the idea.

--

Tokyo Tomy

unread,
Oct 15, 2004, 6:34:13 PM10/15/04
to
"Early Ehlinger" <ea...@respower.com> wrote in message news:<92Bbd.49569$VJ2....@fe40.usenetserver.com>...

> From: "Early Ehlinger" <ea...@respower.com>
> > So is there a way to avoid repeatedly providing the type name in this
> > case?
>
> ...

> His basic approach was to look at it from a different perspective. Instead
> of trying to transform the _1 with a nested boost::bind, he transformed the
> iterators being passed to for_each. Beautiful, simple, elegant. I even
> showed it to a colleague who is not as comfortable reading template code and
> he commented on how much easier it was to read than my canonical
> stdplus2::extract_second sample.
>
> So, Maxim, thanks. I hope somebody else out there finds as much benefit
> from your input as I have; I also hope I can repay the favor at some point
> ;)
> ...

I am sorry that I don't get the Maxim idea well. Would you kindly
write the idea in the example below?

In the example, I use a helper function for for_each, which I think
many problems can be solved easily and simply without help from boost
utilities. Please notice that "<int, double>" is written once.

Example:

#include <algorithm>
#include <iostream>
#include <map>
#include <algorithm>

typedef map<int, double> map_t;
typedef map_t::value_type v_t;

// helper function for for_each
void f(const v_t& pr) { std::cout << pr.second << " "; }

int main()
{
map_t m;
m[1] = 1.0; m[2] = 2.0; m[3] = 3.0;

std::for_each(m.begin(), m.end(), f);
std::cout << std::endl;

return 0;

Maxim Yegorushkin

unread,
Oct 16, 2004, 6:20:04 AM10/16/04
to
Tokyo Tomy <hos...@jtec.or.jp> wrote:

> I am sorry that I don't get the Maxim idea well. Would you kindly
> write the idea in the example below?

Strangely enough, the post did not make it here. Here is a repost (with a
fix for const_iterators).

Usage:

#include <iostream>
#include <map>
#include <string>
#include <algorithm>

#include "util/pair_iterator.hpp"

template<class T> inline T const& constify(T& t) { return t; }

int main()
{
using namespace std;
using namespace util;

map<int, string> m;
m[0] = "alice";
m[1] = "bob";
m[2] = "carol";
m[3] = "dave";
m[4] = "eve";

copy(
over_second(m.begin())
, over_second(m.end())
, ostream_iterator<string>(cout, "\n")
);

copy(
over_first(m.begin())
, over_first(m.end())


, ostream_iterator<int>(cout, "\n")
);

// const iterators check

copy(
over_second(constify(m).begin())
, over_second(constify(m).end())
, ostream_iterator<string>(cout, "\n")
);

copy(
over_first(constify(m).begin())
, over_first(constify(m).end())
, ostream_iterator<int>(cout, "\n")
);
}

Here is the machinery:

#include <iterator>

#include "boost/iterator/transform_iterator.hpp"
#include "boost/type_traits/remove_reference.hpp"
#include "boost/type_traits/is_const.hpp"
#include "boost/mpl/if.hpp"

namespace util {

namespace aux {

template<class T> struct dereference_type
: boost::remove_reference<typename
std::iterator_traits<T>::reference>
{
};

template<class PairT>
struct first_extracter
{
typedef typename boost::mpl::if_<
boost::is_const<PairT>
, typename PairT::first_type const
, typename PairT::first_type
>::type result_type;
result_type& operator()(PairT& p) const { return p.first; }
};

template<class PairT>
struct second_extracter
{
typedef typename boost::mpl::if_<
boost::is_const<PairT>
, typename PairT::second_type const
, typename PairT::second_type
>::type result_type;
result_type& operator()(PairT& p) const { return p.second; }
};

} // namespace aux {

template<class IteratorT>
inline
boost::transform_iterator<aux::first_extracter<typename
aux::dereference_type<IteratorT>::type>, IteratorT>
over_first(IteratorT const& i)
{
typedef aux::first_extracter<typename
aux::dereference_type<IteratorT>::type> extracter;
return boost::transform_iterator<extracter, IteratorT>(i,
extracter());
}

template<class IteratorT>
inline
boost::transform_iterator<aux::second_extracter<typename
aux::dereference_type<IteratorT>::type>, IteratorT>
over_second(IteratorT const& i)
{
typedef aux::second_extracter<typename
aux::dereference_type<IteratorT>::type> extracter;
return boost::transform_iterator<extracter, IteratorT>(i,
extracter());
}

--
Maxim Yegorushkin

Joshua Lehrer

unread,
Oct 16, 2004, 9:41:22 PM10/16/04
to
Here's a solution that does not require you to use the typename.
Instead, you call a creator function "make_second_extractor", passing in
a reference to the container. This function deduces the template types,
and returns a "second_extractor" that does the appropriate
work:

//a base class that the const and non-const second_extractor //will
inherit from.
template <typename PAIRREF, typename result> struct
second_extractor_base {
typedef result result_type;
inline result_type operator()(PAIRREF p) const {
return p.second;
}
};

//declare class to be partially specialized template <typename PAIR,
bool isConst> struct second_extractor;

//first specialization, when type is NOT const template <typename PAIR>
struct second_extractor <PAIR,false>
: public second_extractor_base<PAIR&,typename PAIR::second_type&> { };

//second specialization, const type. Notice both pair //and second_type
are passed as const to base class template <typename PAIR> struct
second_extractor <PAIR,true> : public second_extractor_base<const PAIR&,
const typename PAIR::second_type&> { };


//make_second_extractor
//takes container (just to do template deduction) //returns an object
that when passed in an element //returns a [const] reference to the
pair's second template <typename CONTAINER> inline
second_extractor<typename CONTAINER::value_type,
boost::is_const<CONTAINER>::value>
make_second_extractor(CONTAINER&) {
return
second_extractor<typename CONTAINER::value_type,
boost::is_const<CONTAINER>::value>();
}

//two test functions to call
void func_const(const double&) { }
void func(double&) { }

int main() {
{
//ensure that const works
const std::map<int,double> mp;

std::for_each(mp.begin(),mp.end(),boost::bind(func_const,boost::bind(mak
e_second_extractor(mp),_1)));
}

{
//ensure non-const works
std::map<int,double> mp;

std::for_each(mp.begin(),mp.end(),boost::bind(func,boost::bind(make_seco
nd_extractor(mp),_1)));
}
}


joshua lehrer
factset research systems
NYSE:FDS

John Torjo

unread,
Oct 18, 2004, 3:05:36 PM10/18/04
to
>
> If not, I would greatly appreciate any insight you might have with regards
> to removing the need for extract_second to be a template class. It is
> rather cumbersome (not to mention error-prone) to have to provide the
> template parameter, so I would obviously just as soon get rid of it.
>

I saw the implementation Maxim posted. Quite cool ;)

The rangelib (http://www.torjo.com/rangelib/) has some nice way for
extracting only the first or only the second value of a pair:

std::map<std::string,int> c;
std::vector<std::string> k;
std::vector<int> v;
rng::copy( transformed(pair_1st(c)), std::back_inserter(k) );
rng::copy( transformed(pair_2nd(c)), std::back_inserter(v) );

Also, stay tuned for the upcoming issue of Dec 04 CUJ, where rangelib
is explained in more detail ;)

Best,
John


John Torjo, Contributing editor, C/C++ Users Journal
-- "Win32 GUI Generics" -- generics & GUI do mix, after all
-- http://www.torjo.com/win32gui/
-- v1.5 - tooltips at your fingertips (work for menus too!)
+ bitmap buttons, tab dialogs, hyper links, lite html

Joshua Lehrer

unread,
Oct 20, 2004, 12:28:40 PM10/20/04
to
jto...@yahoo.com (John Torjo) wrote in message news:<c638aac5.04101...@posting.google.com>...

> I saw the implementation Maxim posted. Quite cool ;)
>
> The rangelib (http://www.torjo.com/rangelib/) has some nice way for
> extracting only the first or only the second value of a pair:
>
> std::map<std::string,int> c;
> std::vector<std::string> k;
> std::vector<int> v;
> rng::copy( transformed(pair_1st(c)), std::back_inserter(k) );
> rng::copy( transformed(pair_2nd(c)), std::back_inserter(v) );
>

This looks like a similar approach to what I posted yesterday. Once
you pass the container into the creator function pair_1st, the
template types can be deduced during function resolution.

joshua lehrer
factset research systems
NYSE:FDS

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Tokyo Tomy

unread,
Oct 31, 2004, 6:16:46 AM10/31/04
to
"Maxim Yegorushkin" <e-m...@yandex.ru> wrote in message
news:<opsfxp24auti5cme@wkcg6rirwp>...
> ...
> Usage:
>
> #include <iostream>
> #include <map>
> #include <string>
> #include <algorithm>
>
> #include "util/pair_iterator.hpp"
>
> template<class T> inline T const& constify(T& t) { return t; }
>
> int main()
> {
> using namespace std;
> using namespace util;
>
> map<int, string> m;
> m[0] = "alice";
> m[1] = "bob";
> m[2] = "carol";
> m[3] = "dave";
> m[4] = "eve";
>
> copy(
> over_second(m.begin())
> , over_second(m.end())
> , ostream_iterator<string>(cout, "\n")
> );
> ...
> }
>
> Here is the machinery:
>
> ...

Thank you for your fine code for the value extractor of map elements.
Your code can be beautifully used, and I like it.

I do not understand Boost template library well, and therefore I do
not understand your code well, but at first glance, I think:

>copy(over_second(m.begin(),
> over_second(m.end()),ostream_iterator<string>(cout, "\n"));

does
(1) extract a second value using a map iterator, and
(2) make an iterator pointing to the value.

This explanation is not good at over_second(m.end()), because I think
you cannot extract the second value from the area pointed by m.end().

Would you kindly explain further how your code works? Thank you in
advance.

Maxim Yegorushkin

unread,
Nov 1, 2004, 7:16:41 AM11/1/04
to
On 31 Oct 2004 06:16:46 -0500, Tokyo Tomy <hos...@jtec.or.jp> wrote:

> I do not understand Boost template library well, and therefore I do
> not understand your code well, but at first glance, I think:
>
>> copy(over_second(m.begin(),
>> over_second(m.end()),ostream_iterator<string>(cout, "\n"));
> does
> (1) extract a second value using a map iterator, and
> (2) make an iterator pointing to the value.
> This explanation is not good at over_second(m.end()), because I think
> you cannot extract the second value from the area pointed by m.end().

over_second() function returns a wrapper iterator which stores (wraps) the
original iterator. This iterator has the same interface and category as
wrapped iterator has (map<>::iterator here) and it forwards all calls to
the wrapped iterator, except operator*() and operator->(). The latter two
operators return the result of applying a functor (second_extracter<>
here) to the result of calling wrapped iterator's operator*/->().

I used boost::transform_iterator here because it takes care of all
boilerplate code that deals with iterator category and its assotiated
operators and their return types, so the code should work with any
category of iterators that iterate over pairs.

To make things clear here is map<> iterators only version of the wrapper
iterator (actually, it may work with other containers' iterators
implemented as classes):

#include <iterator>

#include "boost/type_traits/remove_reference.hpp"
#include "boost/type_traits/is_const.hpp"
#include "boost/mpl/if.hpp"

template<class WrappedIteratorT>
struct first_iterator : WrappedIteratorT
{
typedef typename boost::remove_reference<
typename std::iterator_traits<WrappedIteratorT>::reference
>::type pair_type;


typedef typename boost::mpl::if_<

boost::is_const<pair_type>
, typename pair_type::first_type const
, typename pair_type::first_type
>::type value_type;
typedef value_type& reference;

first_iterator(WrappedIteratorT const& i) : WrappedIteratorT(i) {}

reference operator*() const { return
this->WrappedIteratorT::operator*().first; }
value_type* operator->() const { return return &**this; }
};

template<class WrappedIteratorT>
inline first_iterator<WrappedIteratorT> over_first(WrappedIteratorT
const& i)
{
return first_iterator<WrappedIteratorT>(i);
}

--
Maxim Yegorushkin

0 new messages