Iterating over the seconds of a list of pairs

I have an std::list<std::pair<double, float> > internal to a class. I'd like to expose begin() and end() functions from this class, that iterate over the second element of the list. So basically I want the user of the class to only see a sequence of floats. transform_iterator seems to fit the bill, but I think I'm seriously overcomplicating this. I've tried the following and it's not working. I feel like this pair_second / pair_first, and iterator should either already all be defined somewhere that I'm just not aware of, or be much shorter to write than this. template<class First, class Second> struct pair_second : public std::unary_function<std::pair<First, Second>, const Second&> { const Second& operator()(std::pair<First,Second>& pair) const { return pair.second; } }; template<class First, class Second> struct pair_first : public std::unary_function<std::pair<First, Second>, const First&> { const First& operator()(std::pair<First,Second>& pair) const { return pair.first; } }; template<class pair_type> struct first_iterator { typedef boost::transform_iterator< pair_first<typename pair_type::first_type, typename pair_type::second_type>, typename pair_type > type; }; template<class pair_type> struct second_iterator { typedef boost::transform_iterator< pair_second<typename pair_type::first_type, typename pair_type::second_type>, typename pair_type > type; }; typedef typename first_iterator<double_float_pair>::type float_iterator; float_iterator begin() { return float_iterator(items_.begin(), second_iterator<double_float_pair>()); } float_iterator end() { return float_iterator(items_.end(), second_iterator<double_float_pair>()); } Any advice would be appreciated. Thanks Zach

Hello Zach,
I have an std::list<std::pair<double, float> > internal to a class. I'd like to expose begin() and end() functions from this class, that iterate over the second element of the list. So basically I want the user of the class to only see a sequence of floats. transform_iterator seems to fit the bill, but I think I'm seriously overcomplicating this. I've tried the following and it's not working. I feel like this pair_second / pair_first, and iterator should either already all be defined somewhere that I'm just not aware of, or be much shorter to write than this.
You're right -- transform_iterator is the right tool for the job. Try something like this. 1) Write a function object that extracts the second element of the pair. 2) Use the transform iterator adaptor to provide you with the type of the iterators for your class. 3) Use the helper function make_transform_iterator in your begin() and end() methods to create the iterators. Here's an example that does the above: #include <iostream> #include <list> #include <utility> #include "boost/iterator/transform_iterator.hpp" // Extract the second element of a pair template <typename T> class pair_second_t { public: typedef T result_type; template <typename U> T operator()(const std::pair<U,T>& element) const { return element.second; } }; class some_class { public: typedef std::list<std::pair<double,float> > container_type; typedef boost::transform_iterator<pair_second_t<float>, container_type::iterator> iterator_type; some_class() { // Add some sample elements container_.push_back(std::make_pair(2.123, 3.14f)); container_.push_back(std::make_pair(4.123, 4.14f)); container_.push_back(std::make_pair(7.123, 5.14f)); container_.push_back(std::make_pair(8.123, 6.14f)); } iterator_type begin() { return boost::make_transform_iterator( container_.begin(), pair_second_t<float>()); } iterator_type end() { return boost::make_transform_iterator( container_.end(), pair_second_t<float>()); } private: container_type container_; }; int main(int argc, char* argv[]) { some_class sc; some_class::iterator_type it, end; it = sc.begin(); end = sc.end(); while (it != end) { std::cout << *it++ << '\n'; } return 0; } Cheers, Bjorn Karlsson www.skeletonsoftware.net

2009/8/8 Björn Karlsson <Bjorn.Karlsson@readsoft.com>
template <typename U> T operator()(const std::pair<U,T>& element) const { return element.second; } };
To enable writing to the element.second, shouldn't that 'second' extractor return a reference? - Rob. -- ACCU - Professionalism in programming - http://www.accu.org

Hello Rob,
To enable writing to the element.second, shouldn't that 'second' extractor return a reference?
Good point, and that's certainly possible to do. The function object needs to be changed like so (note that the argument to the function call operator is now passed as a reference rather than reference to const): // Extract the second element of a pair template <typename T> class pair_second_t { public: typedef T& result_type; template <typename U> result_type operator()(std::pair<U,T>& element) const { return element.second; } }; Cheers, Bjorn Karlsson www.skeletonsoftware.net

...and now that we've seen the fundamental mechanics involved in using transform_iterator for this problem, here's a really terse version using two other useful libraries; Boost.Bind and Boost.Function.
1) Write a function object that extracts the second element of the pair. 2) Use the transform iterator adaptor to provide you with the type of the iterators for your class. 3) Use the helper function make_transform_iterator in your begin() and end() methods to create the iterators.
#include <list> #include <utility> #include "boost/iterator/transform_iterator.hpp" #include "boost/function.hpp" #include "boost/bind.hpp" class some_class { public: typedef std::list<std::pair<double,float> > container_type; typedef boost::transform_iterator< boost::function<float& (container_type::reference)>, container_type::iterator> iterator_type; iterator_type begin() { return boost::make_transform_iterator( container_.begin(), boost::bind<float&>(&container_type::value_type::second, _1)); } iterator_type end() { return boost::make_transform_iterator( container_.end(), boost::bind<float&>(&container_type::value_type::second, _1)); } private: container_type container_; }; We use boost::function<float& (container_type::reference)> to parameterize a very general unary function for transform_iterator. Then we use Boost.Bind to create a compatible function object that binds to the member second in std::pair. The advantages of this approach are that it's short, elegant, and all the logic is locally defined. (It's also quite cool.) However, you must know your audience -- not everyone is comfortable reading and understanding code like this. Cheers, Bjorn Karlsson www.skeletonsoftware.net

2009/8/9 Björn Karlsson <Bjorn.Karlsson@readsoft.com>
The advantages of this approach are that it's short, elegant, and all the logic is locally defined. (It's also quite cool.) However, you must know your audience -- not everyone is comfortable reading and understanding code like this.
Ok, I like this solution, and is the solution that broadly occurred to me when I read the OP - which you beat me to answering! However I'm more interested in different takes on your final comment, "..., you must know your audience...". I do struggle with this one at work sometimes - I'm generally inclined to write the kind of code you present, because it's good code, because it's the right solution, and because to do otherwise would be condescending and patronising to my colleagues. However, I sometimes find myself criticised for writing overly complex code, and by extension for not being a team player. Any thoughts? - Rob.

Hello Rob,
However I'm more interested in different takes on your final comment, "..., you must know your audience...".
Yes, this is one of the most important lessons I've learned the last decade or so.
I do struggle with this one at work sometimes - I'm generally inclined to write the kind of code you present, because it's good code, because it's the right solution, and because to do otherwise would be condescending and patronising to my colleagues. However, I sometimes find myself criticised for writing overly complex code, and by extension for not being a team player.
Any thoughts?
The code I presented can only be thought of as good code if programmers who need to understand it also know about: * STL (beyond spelling std::list) * Boost.Iterator * Boost.Function * Boost.Bind When my peers think I write overly complex code they are always right. There are two probable reasons: 1) I write overly complex code. 2) I'm wielding tools (in the language, libraries, or in the domain model) that are currently beyond them. The great opportunity here is to teach my peers about the tools I'm using. Or, if I find that there are no such tools, realize that I've written overly complex code and simplify it. Bjorn Karlsson www.skeletonsoftware.net

2009/8/11 Björn Karlsson <Bjorn.Karlsson@readsoft.com> Hi Björn Hello Rob,
.... When my peers think I write overly complex code they are always right.
Taken to its logical extreme that would suggest that we should always be writing at the lowest common denominator of all participating programmers. That seems at odds with 'best of breed' ideologies. - Rob.

2009/8/12 Robert Jones <robertgbjones@gmail.com>
That seems at odds with 'best of breed' ideologies.
Can you elaborate on this? I'm sure I'm wrong, but for me it sounds like "if I'm different, I'm better". Roman Perepelitsa.

On Wed, Aug 12, 2009 at 9:41 PM, Roman Perepelitsa < roman.perepelitsa@gmail.com> wrote:
2009/8/12 Robert Jones <robertgbjones@gmail.com>
That seems at odds with 'best of breed' ideologies.
Can you elaborate on this? I'm sure I'm wrong, but for me it sounds like "if I'm different, I'm better".
Hi Roman Yes, I know, and that's more than a little of the problem. It's not always me tho', sometimes it's someone else who, frankly, is better, does know a little more, does have a more informed perspective, and when I find myself in their company my hope is that I can learn from them, and that they will be ready to teach. Sometimes they do, sometimes they don't. More importantly tho', is to disassociate the message from the messenger. The only question that counts is whether there is merit in the techniques and methods being suggested. IIRC correctly you have kindly answered some of techincal questions in the past. Regards, Rob.

Hello Rob,
Taken to its logical extreme that would suggest that we should always be writing at the lowest common denominator of all participating programmers.
That seems at odds with 'best of breed' ideologies.
I agree, and that's why I advocate this approach: * Write the best code you possibly can using the best tools you have. (Boost has lots of magnificent tools.) * When your code and tools are too complex for your peers to understand, teach them what you know. My point regarding "when my peers think I write overly complex code they are always right"? It's that I have the power to change their minds by teaching them things I know that they previously didn't. Or I can learn from them how to write code that's only as complex as it needs to be. (It should be duly noted that I all too often fail to follow my own advice.) This is a very interesting topic and I've just published an article covering the Boost question we're talking about. It's called "Good Code && Good Advice", is packed with Boost usage, and you'll find it on www.skeletonsoftware.net/downloads.aspx. I guess this thread is slowly drifting off topic, but thank you for bringing it up! Cheers, Bjorn Karlsson

2009/8/13 Björn Karlsson <Bjorn.Karlsson@readsoft.com>
This is a very interesting topic and I've just published an article covering the Boost question we're talking about. It's called "Good Code && Good Advice", is packed with Boost usage, and you'll find it on www.skeletonsoftware.net/downloads.aspx.
Hi Björn Nice article, and btw, it was your book that got me started with Boost, so it's all your fault really! Regards, Rob. ps, How about volume two, covering a few more libraries?

Hello Rob,
Nice article, and btw, it was your book that got me started with Boost, so it's all your fault really!
I gladly accept responsibility for that. :-)
ps, How about volume two, covering a few more libraries?
Yes, I'm currently planning a new Boost book. There's massive potential in these wonderful libraries, yet relatively little coverage in literature; that needs to change. I would love to know what everyone here wants from a new (general) Boost book. (Feel free to contact me off list with your thoughts.) Bjorn Karlsson www.skeletonsoftware.net

Yes, I'm currently planning a new Boost book. There's massive potential in these wonderful libraries, yet relatively little coverage in literature; that needs to change.
I would love to know what everyone here wants from a new (general) Boost book. (Feel free to contact me off list with your thoughts.)
Hi, You might find the following of interest. https://svn.boost.org/trac/boost/wiki/GlueDocsProject I found it accidentally by looking for Debug Visualizers. There seams to be no link from Boost's page. The project's goal seams to be similar to yours. Regards, &rzej

Björn Karlsson <Bjorn.Karlsson@readsoft.com> writes: [...]
I would love to know what everyone here wants from a new (general) Boost book. (Feel free to contact me off list with your thoughts.)
I can usually find detailed API docs online OK, and often code samples. What can be hard is getting a high-level overview of what's available, learning scenarios that the different classes were designed for, and finding the details that would let you decide which to use. For example, I can easily find the methods for shared_ptr, but what are the circumstances where I might want to use that? And when do I use auto_ptr vs. shared_ptr? Things that I know now after using both for awhile, but didn't know when I first started out. Basically, what would be useful to me would be the advice of an experienced C++/Boost programmer on how to get the most out of boost. As far as individual libraries, thorough documentation on asio would be very useful. Hope this feedback is helpful! -----Scott.

On Fri, 14 Aug 2009 23:33:49 -0400 Scott Gifford <sgifford@suspectclass.com> wrote:
Björn Karlsson <Bjorn.Karlsson@readsoft.com> writes:
[...]
I would love to know what everyone here wants from a new (general) Boost book. (Feel free to contact me off list with your thoughts.)
I can usually find detailed API docs online OK, and often code samples. What can be hard is getting a high-level overview of what's available, learning scenarios that the different classes were designed for, and finding the details that would let you decide which to use.
For example, I can easily find the methods for shared_ptr, but what are the circumstances where I might want to use that? And when do I use auto_ptr vs. shared_ptr? Things that I know now after using both for awhile, but didn't know when I first started out.
Basically, what would be useful to me would be the advice of an experienced C++/Boost programmer on how to get the most out of boost.
As far as individual libraries, thorough documentation on asio would be very useful.
Hope this feedback is helpful!
-----Scott.
I agree. Now I am having to relearn C, and learn C++ after a hiatus of about 20 years, although I have been programming in other languages. This is in addition to my regular work. I learn best by reading books, and trying out examples (first from the book, then trying something different), and then applying what I learned to real world problems. Sort of like, crawl-walk-run. I have relearned C (by reading a good C++ book). I am learning C++ oop, by reading more from that book. What I would like is a really good book on learning the STL and Boost. Libraries that either come with C++ or looks like it ought to. The STL/Boost library book, should show me the way to crawl-walk-run on steroids. jerry -- Hobbit Name: Pimpernel Loamsdown Registered Linux User: 275424 K7AZJ This email's Fortune: If Machiavelli were a programmer, he'd have worked for AT&T.

I would like to see Not too complex or complete 'use cases' with ASIO Regular expressions Spirit file io, threads documentation and logging frameworks serialization It would be good if each one of those examples, actually 'repeat' the use of some fundamental stuff -- so that reader of the book can get the taste of how shared pointers, functors, lambda functions, etc get to be applied in all of those examples. I had difficulties understanding how serialization works on std::set of shared pointers, for example, I would have not been able to use ASIO without the help of PION library (threaded http server built on top of asio) Examples of how to use thread local data storage were not intuitive to me either, typically thread local data storage is used to bind something like a File or DB connection to a thread -- and an example of such concept would be nice. Spirit is extremely useful thing to have, but of course has some learning curve, it has some good examples. However introducing the users to it 'lightly' in the context of the book would be nice. Things that are really 'unnatural' to do with regular expressions like 'balanced' parenthesis matching/etc are a lot more natural with Spirit. It would help users to understand the difference between a regular expressions and a full blown parser. There are several constant questions on the list about what I consider horrible omission of C++ standard UTF-8/16, Database access, JSON,XML parsing, RMI (remote method invocation) (surprisingly almost all of these things require to have 'Reflection' functionality in the core language). I know those things are not covered by Boost, but perhaps mentioning them and recommending a comparative overview of what's out there -- would be nice. Because Boost is not considered just as 'yet-another library' -- instead it is for us, the poor C++ programmers, that are bombarded with 'why not Java or C#' -- an escape, a show-case and a standard what C++ could be -- if would not be managed by ISO standardization committee all these years. On Fri, 14 Aug 2009 23:33 -0400, "Scott Gifford" <sgifford@suspectclass.com> wrote:
Björn Karlsson <Bjorn.Karlsson@readsoft.com> writes:
[...]
I would love to know what everyone here wants from a new (general) Boost book. (Feel free to contact me off list with your thoughts.)
I can usually find detailed API docs online OK, and often code samples. What can be hard is getting a high-level overview of what's available, learning scenarios that the different classes were designed for, and finding the details that would let you decide which to use.
For example, I can easily find the methods for shared_ptr, but what are the circumstances where I might want to use that? And when do I use auto_ptr vs. shared_ptr? Things that I know now after using both for awhile, but didn't know when I first started out.
Basically, what would be useful to me would be the advice of an experienced C++/Boost programmer on how to get the most out of boost.
As far as individual libraries, thorough documentation on asio would be very useful.
Hope this feedback is helpful!
-----Scott. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users -- Vlad P author of C++ ORM http://github.com/vladp/CppOrm/tree/master
-- http://www.fastmail.fm - mmm... Fastmail...

2009/8/14 <toreason@fastmail.fm>:
Because Boost is not considered just as 'yet-another library' -- instead it is for us, the poor C++ programmers, that are bombarded with 'why not Java or C#' -- an escape, a show-case and a standard what C++ could be -- if would not be managed by ISO standardization committee all these years.
But we're on what, version 3 of the filesystem library, for example? If this were managed like, say, Java, instead of my the committee, then all three would still be around, with progressively worse names as the good ones are taken, cluttering up the API docs. I'll take a good language kernel with powerful library-building facilities over an inconsistent but all-inclusive one any day.

In any new edition of the boost book, I would like to see a strong emphasis on algorithms, containers, and utility functions. Stuff that *everyone* can make use of in a wide variety of projects. variant, boost, thread, filesystem, multi_array, any, accumulators, bind, function, circular_buffer, date_time, exception, foreach, random, enable_if, and probably a few others. This could in theory take up about half the book. For the next 25% I would like to see some discussion of libraries that are slightly less widely applicable, but still extremely useful and greatly simplifying when you need them. asio, flyweight, iostreams, signals, numeric conversion, etc. Note that use of asio implies neither asynchronous, nor i/o, despite the name. The documentation and examples of asio also applies almost exclusively to i/o related tasks on the network. It would be useful to extend this to other things, for example asynchronous file reading / writing, or asynchronous computation, or a generic pipeline. The last 25% I think could be dedicated to more specialized / advanced libraries. graph, math, MPL, fusion, spirit, etc. I'd also like to see a new edition of the book be about 50% longer than the first edition, and have a hardback edition available if possible.

Zachary Turner wrote:
In any new edition of the boost book, I would like to see a strong emphasis on algorithms, containers, and utility functions. Stuff that *everyone* can make use of in a wide variety of projects. variant, boost, thread, filesystem, multi_array, any, accumulators, bind, function, circular_buffer, date_time, exception, foreach, random, enable_if, and probably a few others.
This could in theory take up about half the book. For the next 25% I would like to see some discussion of libraries that are slightly less widely applicable, but still extremely useful and greatly simplifying when you need them. asio, flyweight, iostreams, signals, numeric conversion, etc. Note that use of asio implies neither asynchronous, nor i/o, despite the name. The documentation and examples of asio also applies almost exclusively to i/o related tasks on the network. It would be useful to extend this to other things, for example asynchronous file reading / writing, or asynchronous computation, or a generic pipeline.
The last 25% I think could be dedicated to more specialized / advanced libraries. graph, math, MPL, fusion, spirit, etc.
I agree with the above, except for the relative amounts of pages. I would rather think of a three volume collection. The reason is that the complex/specialized/advanced topics are, well, complex, specialized, advanced. And because they are, they always seem to be squeezed in last, not covered in enough detail. For instance, currently I think I could make good use of MPL, but the tutorial looks rather abstract to me, and somehow I do not really get a grasp on it. I would love to see a more down-to-earth introduction (although maybe that is an oxymoron?). Regards, Roland

Roland Bock wrote:
I agree with the above, except for the relative amounts of pages. I would rather think of a three volume collection. The reason is that the complex/specialized/advanced topics are, well, complex, specialized, advanced. And because they are, they always seem to be squeezed in last, not covered in enough detail.
For instance, currently I think I could make good use of MPL, but the tutorial looks rather abstract to me, and somehow I do not really get a grasp on it. I would love to see a more down-to-earth introduction (although maybe that is an oxymoron?).
Regards, I agree with this. MPL and other meta-programming related libraries (MPl, type_traits,fusion,proto) are not for the faint of heart when you have no idea what they are for.
-- ___________________________________________ Joel Falcou - Assistant Professor PARALL Team - LRI - Universite Paris Sud XI Tel : (+33)1 69 15 66 35

Björn Karlsson <Bjorn.Karlsson <at> readsoft.com> writes:
...and now that we've seen the fundamental mechanics involved in using
transform_iterator for this
problem, here's a really terse version using two other useful libraries; Boost.Bind and Boost.Function.
typedef boost::transform_iterator< boost::function<float& (container_type::reference)>, container_type::iterator> iterator_type;
The advantages of this approach are that it's short, elegant, and all the logic is locally defined.
Hello, I see a possible disadvantage: efficiency. I may be wrong but I see there will be the extra overhead of a call through boost::function for every iterator dereference. That is, compared to the version using the functor which would have no overhead (compared to a hand-made iterator that dereferenced the second element of a pair). Please correct me if I'm wrong with this. Thanks! PS: when i try to propose using boost I always get: 'Hum... what's the overhead compared to doing it by hand?'

On Thu, Aug 27, 2009 at 11:29 AM, dariomt <dariomt@gmail.com> wrote:
PS: when i try to propose using boost I always get: 'Hum... what's the overhead compared to doing it by hand?'
As ever the answer is to measure it. I have huge sympathy with this post tho', as it's an issue I often have to address too. Unfortunately, when I have done measurements it has generally shown that there is some performance penalty to be paid for use of bind/function combinations. OTOH, there is some performance penalty to paid for using C/C++ rather than assembler too, so where do you draw the line? The payback of course is clarity of expression, and cleaner representation of idioms and algorithms. I'd very much rather code a for_each algorithm than a for-loop, but that's because it seems to me to be better. Sometimes my colleagues don't share my perspective. I'd be interested if you find answers to any of these things. - Rob. -- ACCU - Professionalism in programming - http://www.accu.org
participants (12)
-
Andrzej Krzemienski
-
Björn Karlsson
-
dariomt
-
Jerry Davis
-
joel
-
Robert Jones
-
Roland Bock
-
Roman Perepelitsa
-
Scott Gifford
-
Scott McMurray
-
toreason@fastmail.fm
-
Zachary Turner