[filesystem] C++11 range-based for statement and BOOST_FOREACH support

Support for the C++11 range-based for statement and BOOST_FOREACH for directory_iterator and recursive_directory_iterator has been added to trunk. This clears feature requests 5896 and 6521. The range-based for has been tested against gcc 4.6, which works, and against VC++2012 RC, which ICEs the compiler. The ICE has been reported to Microsoft. If anyone gives either C++11 range-based for or BOOST_FOREACH a try, I'd appreciate hearing about any problems. Both of these are new to me, and I'm nervous about getting the enabler free functions right. Here is what usage looks like: for (directory_entry& x : directory_iterator(".")) { std::cout << " " << x.path() << "\n"; } BOOST_FOREACH(directory_entry& x, directory_iterator(".")) { std::cout << " " << x.path() << "\n"; } Thanks, --Beman

I'm a little surprised to see a function that returns a range named "directory_iterator". Other Boost libraries, such as Boost.Iterator and Boost.Range, distinguish between the concepts of an iterator, which denotes a single position in a range, and a range, which is defined by iterators at is beginning and its end. Have you considered naming the function "directory_range" to be consistent with other Boost libraries? Otherwise, great idea! Regards, Nate

On Thu, Jul 12, 2012 at 1:39 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
directory_iterator is like std::istream_iterator in that a directory_iterator object identifies the end of a range simply by being equal to directory_iterator(). Thus these iterators are a little odd in that they self-identify both the beginning and the end of the range. So there is no real distinction between an my_iterator and begin(my_iterator). The only reason the function is there is to meet the range-based for statement requirements. Ditto for BOOST_FOREACH range helper functions.
Have you considered naming the function "directory_range" to be consistent with other Boost libraries?
"directory_iterator" is the constructor for class directory_iterator. I'm happy with that as a name since it identifies the primary use of the class. --Beman

On Friday 13 July 2012 07:16:36 Beman Dawes wrote:
There may be no distinction between my_iterator and begin(my_iterator) but there is between my_iterator and end(my_iterator). And since range-based for iterates, well, through a range, it is odd to see an iterator there, even though the range can be deduced from it.
I'm happy with the "directory_iterator" name for an iterator. But not for something acting as a range as well. I'd like to see a distinct range class (probably, a typedef for iterator_range<directory_iterator>) which can be used with the range-based loops naturally, instead of abusing begin() and end(). And make_directory_range(directory_iterator) function would be welcome as well. My 5 cents.

On Fri, Jul 13, 2012 at 7:36 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
I don't see any abuse of begin() and end(). They were put in the core language's list of ways to enable range-base for so that there would be no need to add the complication of a separate class plus a make_x_range function to handle cases like this. Unless I'm missing something, adding wrappers the user must use increases complexity and reduces teachability without adding additional functionality. The beauty of begin/end free functions is that the user doesn't even have to know they exist - range-based for "just works". --Beman

On 7/13/2012 8:01 AM, Andrey Semashev wrote:
+1 I too would like to see a directory_range(const path&). Not just for use with BOOST_FOR_EACH, but primarily for use with boost range. Whether this belongs in filesystem or in range is an open question. (to me at least:-)) Jeff

On Sun, Jul 15, 2012 at 9:22 AM, Jeff Flinn <Jeffrey.Flinn@gmail.com> wrote:
Let's wait a bit to see what the committee's Filesystem study group thinks. If they want the non-member being/end approach for the standard, that would still leave directory_range as something that could be added to Boost.Range. --Beman

Existing practice in Boost seems to be to make a corresponding _range function for such iterators, not to treat the iterator itself as a range: http://www.boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/ran... Regards, Nate

On 12-07-2012 16:10, Beman Dawes wrote:
Well, if you post the code, I'll take a look. I'd expect boost::filesystem::begin( ... ) and end() to be implemented in terms of the std:: mechanism. We ought to add a macro in Boost.Range to do just that. -Thorsten

On Fri, Jul 13, 2012 at 4:45 AM, Thorsten Ottosen <thorsten.ottosen@dezide.com> wrote:
See below for my implementation.
If a macro will cut the boilerplate required to support both C++11 range-based for and C++03 BOOST_FOREACH, I'm all for it. Thanks, --Beman // enable C++11 range-base for statement use ---------------------------------------// // begin() and end() are only used by a range-based for statement in the context of // auto - thus the top-level const is stripped - so returning const is harmless and // emphasizes begin() is just a pass through. inline const directory_iterator& begin(const directory_iterator& iter) {return iter;} inline directory_iterator end(const directory_iterator&) {return directory_iterator();} // enable BOOST_FOREACH ------------------------------------------------------------// inline directory_iterator& range_begin(directory_iterator& iter) {return iter;} inline directory_iterator range_begin(const directory_iterator& iter) {return iter;} inline directory_iterator range_end(const directory_iterator&) {return directory_iterator();} } // namespace filesystem // namespace boost template specializations template<> struct range_mutable_iterator<boost::filesystem::directory_iterator> { typedef boost::filesystem::directory_iterator type; }; template<> struct range_const_iterator <boost::filesystem::directory_iterator> { typedef boost::filesystem::directory_iterator type; };

On 13-07-2012 13:38, Beman Dawes wrote:
Hm. Why not follow this design: http://www.boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/ran... -Thorsten

On Fri, Jul 13, 2012 at 10:42 AM, Thorsten Ottosen <thorsten.ottosen@dezide.com> wrote:
The implementation would be more elegant, but the user would have to write: BOOST_FOREACH(directory_entry& x, directory_range(directory_iterator("."))) instead of the simpler: BOOST_FOREACH(directory_entry& x, directory_iterator(".")) I verified the above with an actual implementation and test. --Beman

I don't see why that's the case. In the posted link, the function istream_range() takes an argument of type istream&, which is the same argument type that the constructor of istream_iterator takes. Notice that istream_range() does *not* take an actual istream_iterator as its constructor argument. Similarly, I would expect directory_range() to take a path argument, and return something like make_iterator_range(directory_iterator(p), directory_iterator()). Regards, Nate

On Sat, Jul 14, 2012 at 12:25 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
The make_iterator_range template requires ForwardIterators, but directory_iterator only meets InputIterator requirements. Perhaps someone familiar with Boost.Range can come up with a workaround. For C++11 range-based for statement use, this works: class directory_range { public: directory_range(const path& p) : m_iter(p) {} directory_iterator begin() const { return m_iter; } directory_iterator end() const { return directory_iterator(); } private: directory_iterator m_iter; }; I'll ask the C++ committee's Filesystem Study Group if they prefer the above over the non-member begin()/end() approach. It is a bit less efficient, although that's probably swamped by operating system overhead. Thanks, --Beman

istream_iterator is not a forward iterator either, yet istream_range() returns an iterator_range<istream_iterator>. The documentation for iterator_range says: "If the template argument is not a model of Forward Traversal Iterator, one can still use a subset of the interface. In particular, size() requires Random Access Traversal Iterators whereas empty() only requires Single Pass Iterators." So it seems this should be OK. Regards, Nate

On Fri, Jul 13, 2012 at 9:30 AM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
BOOST_FOREACH on directory_iterators works against msvc 8, 9, 10, and 11. C++11 Range-based for statement is implemented in msvc 11 only. --Beman
participants (8)
-
Andrey Semashev
-
Beman Dawes
-
Edward Diener
-
Jeff Flinn
-
Mathias Gaunard
-
Nathan Ridge
-
Olaf van der Spek
-
Thorsten Ottosen