[functional] Generic Selector for STL Pair's and Boost.Tuple's

Hi Everyone, I'm CC'ing Mark Rogers who, upon looking at the Author of Boost.Functional (and I assume is the maintainer) may be able to help out with this regard. Recently, I've implemented a simple utility which allows users to select the nth element of an STL pair / Boost.Tuple in conjunction with STL algorithms. This allows precisely the following usage semantics: std::map<int, int> int_map; int_map[0] = 1; int_map[1] = 2; std::transform(int_map.begin(), int_map.end(), std::ostream_iterator<int>(std::cout, " "), boost::select<1>()); // output: "1 2 " This is a generalization of the SGI select1st and select2nd function objects. It also works with a container of Boost.Tuple's: std::list<boost::tuple<double, double, double> > coord_list; coord_list.push_back(boost::make_tuple(1.0, 2.0, 3.0)); coord_list.push_back(boost::make_tuple(4.0, 5.0, 6.0)); std::transform(coord_list.begin(), coord_list.end(), std::ostream_iterator<double>(std::cout, " "), boost::select<0>()); // output "1.0 4.0 " I've re-attached the implementation, for comments and review for inclusion in Boost.Functional. Thanks very much, and I hope to hear from you guys soon. -- Dean Michael C. Berris Software Engineer, Friendster, Inc. [http://cplusplus-soup.blogspot.com/] [mikhailberis@gmail.com] [+63 928 7291459] [+1 408 4049523]

On Nov 20, 2007 8:43 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
[...] Recently, I've implemented a simple utility which allows users to select the nth element of an STL pair / Boost.Tuple in conjunction with STL algorithms. [...]
This doesn't return the right thing with non const parameters. A very simple result_of compatible wrapper around fusion::at_c will do. In fact I think that fusion should provide function objects for every one of its algorithm. HTH, gpd

Giovanni Piero Deretta wrote:
In fact I think that fusion should provide function objects for every one of its algorithm.
That used to be the case at one point. I'll re-consider this again. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
Giovanni Piero Deretta wrote:
In fact I think that fusion should provide function objects for every one of its algorithm.
That used to be the case at one point. I'll re-consider this again.
I'll second the motion. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Joel de Guzman wrote:
Giovanni Piero Deretta wrote:
In fact I think that fusion should provide function objects for every one of its algorithm. That used to be the case at one point. I'll re-consider this again.
I'll second the motion.
Ok. I'll do it after 1.35 is branched. What's a good subnamespace to put that in? We already have result_of::xxx. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
Eric Niebler wrote:
Joel de Guzman wrote:
Giovanni Piero Deretta wrote:
In fact I think that fusion should provide function objects for every one of its algorithm. That used to be the case at one point. I'll re-consider this again. I'll second the motion.
Ok. I'll do it after 1.35 is branched. What's a good subnamespace to put that in? We already have result_of::xxx.
fusion::functional ? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Joel de Guzman wrote:
Eric Niebler wrote:
Joel de Guzman wrote:
Giovanni Piero Deretta wrote:
In fact I think that fusion should provide function objects for every one of its algorithm. That used to be the case at one point. I'll re-consider this again. I'll second the motion. Ok. I'll do it after 1.35 is branched. What's a good subnamespace to put that in? We already have result_of::xxx.
fusion::functional ?
That was my first thought. However, fusion has a "functional" module -- the one with fuse/unfuse and dispatch to function/function objects. This might cause confusion (no pun intended :-)). I'm CC'ing Tobias. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

On Nov 20, 2007 12:37 AM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
On Nov 20, 2007 8:43 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
[...] Recently, I've implemented a simple utility which allows users to select the nth element of an STL pair / Boost.Tuple in conjunction with STL algorithms. [...]
This doesn't return the right thing with non const parameters. A very simple result_of compatible wrapper around fusion::at_c will do. In fact I think that fusion should provide function objects for every one of its algorithm.
You mean: std::pair<int, int> pair(1, 2); std::cout << boost::select<1>()(pair) << std::endl; // should output 2 ? I think I'm missing something here... Can you elaborate? -- Dean Michael C. Berris Software Engineer, Friendster, Inc. [http://cplusplus-soup.blogspot.com/] [mikhailberis@gmail.com] [+63 928 7291459] [+1 408 4049523]

On 11/20/07, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Nov 20, 2007 12:37 AM, Giovanni Piero Deretta <gpderetta@gmail.com>
[...] This doesn't return the right thing with non const parameters. A very simple result_of compatible wrapper around fusion::at_c will do. In fact I think that fusion should provide function objects for every one of its algorithm.
You mean:
std::pair<int, int> pair(1, 2); std::cout << boost::select<1>()(pair) << std::endl; // should output 2
?
I think I'm missing something here... Can you elaborate?
In fact I was wrong. Now I do not think it return the right thing even with const parameters :). The problem is that you always return a copy of the Nth element (as a temporary). This work fine if you only need read access *and* the object is small. In this example 'select' copy the vector at every extraction! std::map<int, std::vector<int> > map = ....; void foo(std::vector<int> const&); std::for_each(map.begin(), map.end(), bind(foo, bind(boost::select<1>(), _1))); If you remove the const from the signature of foo, the example won't compile at all. It is a bit elaborate to deduce the return value of a generic tuple accessor in the most general case possible, but it can be done. Anyways, fusion at_c already takes care of all the work. HTH, gpd

On Nov 22, 2007 12:35 AM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
On 11/20/07, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Nov 20, 2007 12:37 AM, Giovanni Piero Deretta <gpderetta@gmail.com>
[...] This doesn't return the right thing with non const parameters. A very simple result_of compatible wrapper around fusion::at_c will do. In fact I think that fusion should provide function objects for every one of its algorithm.
You mean:
std::pair<int, int> pair(1, 2); std::cout << boost::select<1>()(pair) << std::endl; // should output 2
?
I think I'm missing something here... Can you elaborate?
In fact I was wrong. Now I do not think it return the right thing even with const parameters :). The problem is that you always return a copy of the Nth element (as a temporary). This work fine if you only need read access *and* the object is small. In this example 'select' copy the vector at every extraction!
std::map<int, std::vector<int> > map = ....;
void foo(std::vector<int> const&);
std::for_each(map.begin(), map.end(), bind(foo, bind(boost::select<1>(), _1)));
If you remove the const from the signature of foo, the example won't compile at all. It is a bit elaborate to deduce the return value of a generic tuple accessor in the most general case possible, but it can be done. Anyways, fusion at_c already takes care of all the work.
Ah, you're referring to const-correctness and not returning a reference instead. In this case, I agree that this isn't acceptable in a lot of cases, thanks for pointing it out. Would it help if the current version of select<> would be called select_copy<> and select<> would return a correctly const-qualified reference instead? I've also tried going with fusion::at_c in my initial attempt to get the correct result type, only that I run into problems with const correctness. I'll try my hand at it some other time though. Thanks for the elaboration! -- Dean Michael C. Berris Software Engineer, Friendster, Inc. [http://cplusplus-soup.blogspot.com/] [mikhailberis@gmail.com] [+63 928 7291459] [+1 408 4049523]

On Nov 21, 2007 7:27 PM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Nov 22, 2007 12:35 AM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
std::map<int, std::vector<int> > map = ....;
void foo(std::vector<int> const&);
std::for_each(map.begin(), map.end(), bind(foo, bind(boost::select<1>(), _1)));
If you remove the const from the signature of foo, the example won't compile at all. It is a bit elaborate to deduce the return value of a generic tuple accessor in the most general case possible, but it can be done. Anyways, fusion at_c already takes care of all the work.
Ah, you're referring to const-correctness and not returning a reference instead.
In this case, I agree that this isn't acceptable in a lot of cases, thanks for pointing it out.
Would it help if the current version of select<> would be called select_copy<> and select<> would return a correctly const-qualified reference instead?
I do not think so. There is no reason not to make select as general as possible. Really, having select work in all cases is not that hard. Implementing it is a good exercise in metaprogramming. Start by looking at how boost::tuples::get computes its return value.
I've also tried going with fusion::at_c in my initial attempt to get the correct result type, only that I run into problems with const correctness. I'll try my hand at it some other time though.
I've never really used Fusion in real programs (at work I'm stuck with an older fusionless boost), but it should be const correct. If you have found any bugs you shuld report them. Doesn't something like this work? namespace ft = boost::fusion; typename <int N> struct at_c { // result_of protocol template<typename Sig> struct result; template<typename Seq> struct result<at_c(Seq&)> : fl::result_of::at_c<Seq, N> {}; template<typename Seq> struct result<at_c(Seq const&)> : fl::result_of::at_c<Seq const, N> {}; // actual operator() implementation template<typename Seq> typename fl::result_of::at_c<Seq, N>::type operator()(Seq& s) const { return fl::at_c<N>(s); } template<typename Seq> typename fl::result_of::at_c<Seq const, N>::type operator()(Seq const& s) const { return fl::at_c<N>(s); } }; Of course this is completely untested. I didn't even try to compile it. HTH, -- gpd

On Nov 22, 2007 3:01 AM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
On Nov 21, 2007 7:27 PM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Nov 22, 2007 12:35 AM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
std::map<int, std::vector<int> > map = ....;
void foo(std::vector<int> const&);
std::for_each(map.begin(), map.end(), bind(foo, bind(boost::select<1>(), _1)));
If you remove the const from the signature of foo, the example won't compile at all. It is a bit elaborate to deduce the return value of a generic tuple accessor in the most general case possible, but it can be done. Anyways, fusion at_c already takes care of all the work.
Ah, you're referring to const-correctness and not returning a reference instead.
In this case, I agree that this isn't acceptable in a lot of cases, thanks for pointing it out.
Would it help if the current version of select<> would be called select_copy<> and select<> would return a correctly const-qualified reference instead?
I do not think so. There is no reason not to make select as general as possible. Really, having select work in all cases is not that hard. Implementing it is a good exercise in metaprogramming. Start by looking at how boost::tuples::get computes its return value.
That being said, the rationale for select<> are the following: 1. It's a generalization of the select1st and select2nd SGI extensions to the STL, intended to work at least with Boost.Tuple's (tr1::tuple ?) and STL Pairs. 2. This is intended to be really simple, and perhaps somehow made self-contained and easy to factor out so that a) it doesn't pull in Fusion (which I absolutely think is an excellent library, which I fortunately am able to use at work every single day) b) it only pulls in the required MPL headers, which can even be re-implemented in cases where having the MPL present is unacceptable c) it only relies on the presence of Boost Tuple (can be made optional by doing some preprocessor checking) and STL pair's So personally, I appreciate the feedback -- it's invaluable feedback at that. But I'd like to be able to make it work by making it self-contained; and if suddenly Joel et al decide to make all Fusion functions (like at_c) be reflected as function objects as well (do I hear Phoenix coming along to contain this? :D), then that's something a lot of people would appreciate as well. Precisely for people who don't have Fusion available, or don't want to pull Fusion into the project, something simple is welcome: so I'll try hard to make select<> not rely on Fusion to do the heavy lifting. ;-)
I've also tried going with fusion::at_c in my initial attempt to get the correct result type, only that I run into problems with const correctness. I'll try my hand at it some other time though.
I've never really used Fusion in real programs (at work I'm stuck with an older fusionless boost), but it should be const correct. If you have found any bugs you shuld report them.
I have used Fusion in real programs and they work great. The bug about const correctness which I'm talking about is described below, and has nothing to do with Fusion.
Doesn't something like this work?
[snip]
Of course this is completely untested. I didn't even try to compile it.
So the idea is to use fusion::at_c<>() which I've been trying to avoid precisely because this static assert will fail: BOOST_STATIC_ASSERT(( boost::is_same< boost::result_of<select<0>(std::pair<int, int>)>::type, int
::value ));
This I think is because Fusion's at_c<>::type will return 'int const &' which is correct behavior as far as fusion goes, but not what I intend with the current implementation of select<> which is to return copies and not references. Perhaps it's going to be good to rethink this decision, and come up with select<> and select_copy<>. I'll get back to this when time permits. Thanks for the feedback! -- Dean Michael C. Berris Software Engineer, Friendster, Inc. [http://cplusplus-soup.blogspot.com/] [mikhailberis@gmail.com] [+63 928 7291459] [+1 408 4049523]

Dean Michael Berris wrote:
So the idea is to use fusion::at_c<>() which I've been trying to avoid precisely because this static assert will fail:
BOOST_STATIC_ASSERT(( boost::is_same< boost::result_of<select<0>(std::pair<int, int>)>::type, int
::value ));
This I think is because Fusion's at_c<>::type will return 'int const &' which is correct behavior as far as fusion goes, but not what I intend with the current implementation of select<> which is to return copies and not references. Perhaps it's going to be good to rethink this decision, and come up with select<> and select_copy<>.
That's not a const-correctness problem. Fusion /at/ will return int& which is the correct exact type that the at(s) function returns. See my other post. Use /value_at/ if you want the actual value! :-) HTH, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Dean Michael Berris wrote:
That being said, the rationale for select<> are the following:
1. It's a generalization of the select1st and select2nd SGI extensions to the STL, intended to work at least with Boost.Tuple's (tr1::tuple ?) and STL Pairs.
2. This is intended to be really simple, and perhaps somehow made self-contained and easy to factor out so that a) it doesn't pull in Fusion (which I absolutely think is an excellent library, which I fortunately am able to use at work every single day) b) it only pulls in the required MPL headers, which can even be re-implemented in cases where having the MPL present is unacceptable c) it only relies on the presence of Boost Tuple (can be made optional by doing some preprocessor checking) and STL pair's
So personally, I appreciate the feedback -- it's invaluable feedback at that. But I'd like to be able to make it work by making it self-contained; and if suddenly Joel et al decide to make all Fusion functions (like at_c) be reflected as function objects as well (do I hear Phoenix coming along to contain this? :D), then that's something a lot of people would appreciate as well.
That's a good rationale. I suggest: 1. write lotsa tests around it 2. write it first using fusion 3. write it without fusion, making sure all tests pass 4. ifdef out fusion and/or mpl code -- give the user a chance to choose. there will be cases when fusion *is* available and your code becomes redundant. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

On Nov 22, 2007 6:35 AM, Joel de Guzman <joel@boost-consulting.com> wrote:
Dean Michael Berris wrote:
That being said, the rationale for select<> are the following:
1. It's a generalization of the select1st and select2nd SGI extensions to the STL, intended to work at least with Boost.Tuple's (tr1::tuple ?) and STL Pairs.
2. This is intended to be really simple, and perhaps somehow made self-contained and easy to factor out so that a) it doesn't pull in Fusion (which I absolutely think is an excellent library, which I fortunately am able to use at work every single day) b) it only pulls in the required MPL headers, which can even be re-implemented in cases where having the MPL present is unacceptable c) it only relies on the presence of Boost Tuple (can be made optional by doing some preprocessor checking) and STL pair's
So personally, I appreciate the feedback -- it's invaluable feedback at that. But I'd like to be able to make it work by making it self-contained; and if suddenly Joel et al decide to make all Fusion functions (like at_c) be reflected as function objects as well (do I hear Phoenix coming along to contain this? :D), then that's something a lot of people would appreciate as well.
That's a good rationale. I suggest:
1. write lotsa tests around it 2. write it first using fusion 3. write it without fusion, making sure all tests pass 4. ifdef out fusion and/or mpl code -- give the user a chance to choose. there will be cases when fusion *is* available and your code becomes redundant.
This makes sense. I'll do this when I finally find the time. This usually means weekends for me, so it isn't that far away. ;-) Curiously, how does one check whether Fusion or MPL is available? Is there something already available that allows: #if defined(BOOST_FUSION) # include <select/has_fusion.hpp> #else # if defined(BOOST_MPL) # include <select/has_mpl.hpp> # else # include <select/bare.hpp> # endif #endif ? Pointers would be most appreciated. Have a good one, and happy holidays to those celebrating! -- Dean Michael C. Berris Software Engineer, Friendster, Inc. [http://cplusplus-soup.blogspot.com/] [mikhailberis@gmail.com] [+63 928 7291459] [+1 408 4049523]

Dean Michael Berris wrote:
Precisely for people who don't have Fusion available, or don't want to pull Fusion into the project, something simple is welcome: so I'll try hard to make select<> not rely on Fusion to do the heavy lifting. ;-)
FWIW, here is a generic get implementation which works with or without Boost.Fusion. doc: http://tinyurl.com/23z6y7 source: http://tinyurl.com/2kq56a Regards, -- Shunsuke Sogame

Dean Michael Berris wrote:
I've also tried going with fusion::at_c in my initial attempt to get the correct result type, only that I run into problems with const correctness. I'll try my hand at it some other time though.
What's the problem? I doubt it's const correctness. I'm guessing it's value/reference confusion. Here's a tip: /value_at/ returns the actual type while /at/ returns a reference or value depending on the sequence. For example: tuple<int&, int> t(i, 123); What should /at/ and /value_at/ return? Now, how about this: mpl::vector_c<123, 456> is; What should /at/ and /value_at/ return? /at/ always tries to return an L-value whenever possible to allow mutation (note: "whenever possible" -- it's not always possible). /at/ also tries to return a const-reference, again whenever possible, to avoid copies. It is naive to simply strip the reference to get the value because there's no way to know the actual value if the element is a reference to begin with (first example). That's where value_at helps. This *is* very tricky and I suggest you don't reinvent the wheel. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Giovanni Piero Deretta wrote:
On Nov 20, 2007 8:43 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
[...] Recently, I've implemented a simple utility which allows users to select the nth element of an STL pair / Boost.Tuple in conjunction with STL algorithms. [...]
This doesn't return the right thing with non const parameters. A very simple result_of compatible wrapper around fusion::at_c will do. In fact I think that fusion should provide function objects for every one of its algorithm.
BTW, fusion::at might return non-reference. Also, range_reference<> type of input range might be non-reference or even proxy. It is not so easy to implement generic range adaptor to extract nth element of tuple. Regards, -- Shunsuke Sogame

Recently, I've implemented a simple utility which allows users to select the nth element of an STL pair / Boost.Tuple in conjunction with STL algorithms. This allows precisely the following usage semantics:
Hi, It would also be nice to make this work with transform_iterator. For example #include <boost/iterator/transform_iterator.hpp> std::list<boost::tuple<double, double, double> > coord_list; coord_list.push_back(boost::make_tuple(1.0, 2.0, 3.0)); coord_list.push_back(boost::make_tuple(4.0, 5.0, 6.0)); std::transform( boost::make_transform_iterator(coord_list.begin(),boost::select<0>()), boost::make_transform_iterator(coord_list.end(),boost::select<0>()), std::ostream_iterator<double>(std::cout, " ")); // output "1.0 4.0 " This would require your select functor conforming to result_of, and transform_iterator using it. Chris

Hi Chris, On Nov 20, 2007 9:07 AM, Chris Weed <chrisweed@gmail.com> wrote:
Recently, I've implemented a simple utility which allows users to select the nth element of an STL pair / Boost.Tuple in conjunction with STL algorithms. This allows precisely the following usage semantics:
Hi, It would also be nice to make this work with transform_iterator. For example
#include <boost/iterator/transform_iterator.hpp> std::list<boost::tuple<double, double, double> > coord_list; coord_list.push_back(boost::make_tuple(1.0, 2.0, 3.0)); coord_list.push_back(boost::make_tuple(4.0, 5.0, 6.0));
std::transform( boost::make_transform_iterator(coord_list.begin(),boost::select<0>()), boost::make_transform_iterator(coord_list.end(),boost::select<0>()), std::ostream_iterator<double>(std::cout, " ")); // output "1.0 4.0 "
This would require your select functor conforming to result_of, and transform_iterator using it.
I've been trying, and it's taking me a while to first of all make it result_of compliant. Maybe something worth spending more time on, I'm not sure. I'll send an update if/when I come up with something to this effect. Have a good day! -- Dean Michael C. Berris Software Engineer, Friendster, Inc. [http://cplusplus-soup.blogspot.com/] [mikhailberis@gmail.com] [+63 928 7291459] [+1 408 4049523]
participants (6)
-
Chris Weed
-
Dean Michael Berris
-
Eric Niebler
-
Giovanni Piero Deretta
-
Joel de Guzman
-
shunsuke