Click here to Skip to main content
Click here to Skip to main content
Go to top

Win32 File Name Iteration Boost Way

, 3 Nov 2005
Rate this:
Please Sign up or sign in to vote.
A Port of ::FindFirstFile to Boost.Range and Boost.Foreach

Introduction

I was inspired by the excellent article, Win32 File Name Iteration STL Way. I think the problem of the article is that the handle of ::FindFirstFile is managed by iterators. After combating Boost.Range for months, I realized that Boost.Range could remove that complexity. The result was named find_file_range, which is the direct translation from ::FindFirstFile to C++.

Requirements

find_file_range

find_file_range is a model of Single Pass Range whose value_type is WIN32_FIND_DATA. If you are not familiar with Boost.Range concepts, you could regard it as something like a Container which provides Input Iterators:

find_file_range ffrng("*.*");
typedef boost::range_result_iterator<find_file_range>::type iter_t;
for (iter_t it = boost::begin(ffrng), last = boost::end(ffrng); it != last; ++it) {
  std::cout << it->cFileName << std::endl;
}

If you iterate find_file_range twice, the behavior is undefined. This code proves find_file_range is a thin wrapper of ::FindFirstFile, but seems somewhat clumsy. Why must we write the idiom hundreds of times? If you resent it, it will come.

Foreach will Come

Boost.Foreach (accepted into Boost, but not yet part of the release) provides foreach that is missing from C++:

find_file_range ffrng("*.*");
BOOST_FOREACH (WIN32_FIND_DATA& data, ffrng) {
  std::cout << data.cFileName << std::endl;
}

Is this a new language? No, It's C++. The implementation is magical. If you are interested in it, check Conditional Love: FOREACH Redux written by Eric Niebler.

Filtering

One class, one responsibility. find_file_range doesn't offer any filtering using dwFileAttributes of WIN32_FIND_DATA. Instead, Adaptable Predicates named find_file_is_xxx are provided. They are available for boost::make_filter_range, which is a part of Boost.RangeEx:

find_file_range ffrng("*.*");
BOOST_FOREACH (
  WIN32_FIND_DATA& data,
  boost::make_filter_range(ffrng, boost::not1(find_file_is_dots()))
)
{
  std::cout << data.cFileName << std::endl;
}

std::not1 is completely broken by reference-to-reference problem, so you must use boost::not1 from Boost.Functional.

Transforming

As a mark of respect for the original article, find_file_construct template that makes a model of Adaptable Unary Functor is provided for those who want to get cFileName of WIN32_FIND_DATA as a string object. It is available for boost::make_transform_range:

find_file_range ffrng("*.*");
BOOST_FOREACH (
  std::string const& filename,
  boost::make_transform_range(ffrng, find_file_construct<std::string>())
)
{
  std::cout << filename << std::endl;
}

find_file_construct template takes a type Sequence. If your string type is not a Sequence like CString, you could use find_file_stringize instead.

Chain of Range Adaptors

The chain of filtering and transforming is also available:

find_file_range ffrng("*.*");
BOOST_FOREACH (
  std::string const& filename,
  boost::make_transform_range(
    boost::make_filter_range(
      boost::make_filter_range(
        ffrng,
        boost::not1(find_file_is_hidden())
      ),
      boost::not1(find_file_is_directory())
    ),
    find_file_stringize<std::string>()
  )
)
{
  std::cout << filename << std::endl;
}

This chain is lazy and functional, but unreadable. And so Boost.RangeEx provides Range Adaptors:

find_file_range ffrng("*.*");
BOOST_FOREACH (
  std::string const& filename,
  ffrng |
    boost::adaptor::filter(boost::not1(find_file_is_hidden())) |
    boost::adaptor::filter(boost::not1(find_file_is_directory())) |
    boost::adaptor::transform(find_file_construct<std::string>())
)
{
  std::cout << filename << std::endl;
}

This is rather "procedural" and shows the future of C++.

Points of Interest

find_file_range is just like a disposable container and doesn't offer any additional functionalities unlike Boost.Filesystem, but it doesn't demand any additional resources. Boost.Range concepts are wide-open to such classes. In fact, find_file_range depends on the fact that clients don't iterate twice, while boost::iterator_range depends on the fact that holding iterators are valid. You could easily make many classes conform to the concepts. I hope this article helps:

std::string inputs; {
  boost::copy(make_istream_range<char>(std::cin), std::back_inserter(inputs));
}
std::cout << inputs << std::endl;

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

mb2sync

Japan Japan
I am worried about my poor English...

Comments and Discussions

 
Generalassert Pinmembereugeneugene8-Nov-05 3:56 
GeneralRe: assert Pinmembermb2sync8-Nov-05 11:50 
Generalboost::dir_it PinmemberTodd Smith4-Nov-05 9:25 
FYI boost::dir_it already exists which inspired me to write a reg_it class if anyone's interested.
 
Todd Smith
GeneralRe: boost::dir_it Pinmembermb2sync4-Nov-05 12:56 
GeneralRe: boost::dir_it PinmemberGast1281-Oct-12 3:37 
Generalgood exploration of boost concepts PinmemberMartin.Holzherr4-Nov-05 2:22 
GeneralRe: good exploration of boost concepts Pinmembermb2sync4-Nov-05 4:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 4 Nov 2005
Article Copyright 2005 by mb2sync
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid