|
Introduction
Several times I worked with file names, I usually used Win32 API such as ::FindFirstFile.. But it turns out that it's so boring work. Finally, I realized I can use STL's great feature, iterator, to handle file name iteration. That's why I made a simple STL iterator class for file name iteration.
Usagewin32_file_iterator itBegin("c:\\*.*"), itEnd;
std::copy(itBegin, itEnd, ostream_iterator<std::string>(cout, "\n"));
The code above shows the simplest way to use the class. Actually, you can use almost all of STL algorithm, I think.. win32_file_iterator itBegin("c:\\*.*"), itEnd;
std::vector<std::string> vec(itBegin, itEnd);
You also can fill the STL container by using the constructor that takes begin iterator and end iterator.
Actually, win32_file_iterator class' constructor takes three parameters. The first one is the filter string that is for calling ::FindFirstFile function. Second one is the flag that specifies whether dereferenced path is full path or not. For example, if it's true, the returned path string is c:\test\aa.txt, otherwise it'll be aa.txt only. The last parameter is the other flags which specify file attribute. For simplicity, I used Win32 API's FILE_ATTRIBUTE_XXX flags..
If you want to get only directory names, and which is full path, the code will look like this: win32_file_iterator itBegin("c:\\*", true, FILE_ATTRIBUTE_DIRECTORY);
So easy, huh?
Source#include <windows.h>
#include <iterator>
#include <string>
class win32_file_iterator :
public std::iterator<std::input_iterator_tag, std::string>
{
private:
class internal_handle_data{
public:
internal_handle_data():_h(NULL), _ref(0){}
void setHandle(HANDLE handle){ _h = handle; }
HANDLE getHandle(){ return _h; }
void incRef(){ _ref++; }
unsigned decRef(){ return --_ref; }
operator HANDLE(){ return _h; }
private:
HANDLE _h;
unsigned _ref;
};
public:
win32_file_iterator(std::string strfilter, bool bFullPath = false,
int flag = FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY):
_bEnd(false), _bFullPath(bFullPath), _flag(flag){
HANDLE h = ::FindFirstFile(strfilter.c_str(), &_wfd);
_handle.setHandle(h);
if(h == INVALID_HANDLE_VALUE){
_bEnd = true;
}else{
_handle.incRef();
std::string::size_type n1 = strfilter.find_last_of("\\");
_strroot = strfilter.substr(0,n1+1);
_chkvalid(_wfd);
}
}
win32_file_iterator():_bEnd(true){}
win32_file_iterator(win32_file_iterator& rhs){
_handle = rhs._handle;
_handle.incRef();
_flag = rhs._flag;
_bFullPath = rhs._bFullPath;
_bEnd = rhs._bEnd;
_wfd = rhs._wfd;
_strfname = rhs._strfname;
_strroot = rhs._strroot;
}
~win32_file_iterator(){
if(_handle.decRef() == 0 && _handle.getHandle() != NULL ){
FindClose(_handle);
}
}
reference operator*(){
return _strfname;
}
bool operator==(const win32_file_iterator& rhs) const{
return (_bEnd == rhs._bEnd);
}
bool operator!=(const win32_file_iterator& rhs) const{
return (_bEnd != rhs._bEnd);
}
win32_file_iterator& operator++(){
_findnext();
return *this;
}
win32_file_iterator& operator++(int){
_findnext();
return *this;
}
private:
void _findnext(){
BOOL b = ::FindNextFile(_handle, &_wfd);
if(b){
_chkvalid(_wfd);
}else{
_bEnd = true;
}
}
void _chkvalid(WIN32_FIND_DATA& _wfd){
if(_wfd.dwFileAttributes & _flag){
_getval(_wfd);
}
else{
_findnext();
}
}
void _getval(WIN32_FIND_DATA& wfd){
if(_bFullPath)
_strfname = _strroot+ wfd.cFileName;
else
_strfname = wfd.cFileName;
}
private:
int _flag;
bool _bFullPath;
bool _bEnd;
internal_handle_data _handle;
WIN32_FIND_DATA _wfd;
std::string _strroot;
std::string _strfname;
};
Comment
The code might have many terrible bugs. But what I want was to show the way we can use STL like iteration to find filenames. I wish it'll help you. You can use this code in whatever ways you want, comments are welcome..
And also check out boost::filesystem library.. it's well-written but a little bit heavy. It needs an additional DLL, I suppose.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 32 (Total in Forum: 32) (Refresh) | FirstPrevNext |
|
 |
|
|
Hi there, neither me nor my compiler (VC++6, no MFC) know the type "reference" in the definition of the operator*:
reference operator*(){ return _strfname; } What does that "reference" do? Is it MS-specific? (It is not a C++ keyword!!) 
By the way - a smart solution in one step - if I only could compile it 
Thanks in advance bye Gerald
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I changed reference to std::string and everything seems to work. But really I'm not sure if the substitution is correct. Ciao! mario
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The "reference" type is not declared for iterator in VC6. Use the equivalent "value_type&", i.e.
value_type& operator*(){ return _strfname; }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
* Referencecounting related issues: The presented implementation has a number of issues, and hardly ever works except for the most trivial case. To illustrate this, take a look at this code snippet:win32_file_iterator* pwfit = new win32_file_iterator( "C:\\*.*" ); win32_file_iterator wfit = *pwfit; delete pwfit; // wfit still holds a reference to the original HANDLE that has just been // closed, with a reference count of 2. Similar issues exist for the auto-generated assignment operator. A possible solution could be to use a static std::map<HANDLE, unsigned int> _handleReferences; member, and to insert code that deals with self-assignment, both for the copy-c'tor and the assignment operator. While this solves the issues of reference counting, a more significant issue still remains: since the FindXyzFile API calls aren't stateless, a shared resource introduces a more severe problem. Operating on any given iterator potentially also silently modifies the state of another, in a worst-case scenario even to a point where it's state goes out of sync (see _bEnd).
* operator== and operator!=: First of all, unless there is a reason not to (like performance issues), these operators should always share a common implementation, i.e. operator!= should be expressed in terms of !operator==. More importantly, though, they should expose proper semantics. In the current implementation, all iterators are split in 2 groups, with members in each one comparing equal to any given other of that same group.
* post- and prefix increment operators: Again, the semantics are off. While the prefix operator is fine as it is, the postfix operator should return a temporary value that reflects the state of the iterator prior to the operation.
To sum it up, once you get rid of the shared handles, the remaining troublesome operators will be more natural to implement, and on top of that, expose more accurate semantics.
.f
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
For the internal handle, you could use the boost::shared_ptr handle wrapper idea (posted here on codeproject). That way the FindClose (or any handle closure) is neatly wrapped up within it.
These kinds of articles, simple good ideas, makes me happy
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
Iterator class looks good and I like it (you got a 5 from me!) ...
In the constructor, the string parameter should be passed as a const reference, otherwise you will incur an unnecessary string copy during iterator creation with this constructor.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
|
Sorry but you've lost me there. The thing about STL is that it is portable, cross-platform. Why would you want to make something so hardbound to Win32?? As good as it might be, if I'm going to learn something else than Win32, I might as well go for a cross-platform approach such as wxWidgets (previously wxWindows) http://www.wxwidgets.com. Else, the shared_ptr approach to Win32 handles suffices nicely.
/Rob
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Rob, Yes I agree. I personally use Boost and STL wherever it makes sense. I don't use WinSTL, however I thought it would be of interest to CP readers. I think some of the other libraries maybe portable, but I've not used any of them so I can't say for sure.
Neville Franks, Author of ED for Windows www.getsoft.com and Surfulater www.surfulater.com "Save what you Surf"
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Robert Bielik wrote: The thing about STL is that it is portable, cross-platform. Why would you want to make something so hardbound to Win32??
Counter-question: How would you have implemented the iterator in a cross-platform manner?
The short answer is: You cannot.
The longer answer is, that generic programming is about providing a portable interface, rather than a portable implementation. And that's exactly what the STL does itself. Take for example operator new, that directly or indirectly calls into OS specific API's. Would that make it really non-portable? Or wouldn't you agree that the common interface is sufficient for being portable. The same is true for the win32_file_iterator class, which exposes a portable interface, and should you ever find yourself in a situation to port to a different platform, all you really had to do is provide a platform-specific implementation, with the rest of your source base remaining unchanged.
On a similar note, wxWidgest looks exactly the same under the hood. It's just that you never really get in touch with those implementation details. Your mistake, however, is that you deduce from a portable interface that the implementation is equally platform-independent.
.f
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Sorry, no mistake on my part. You must have misunderstood. I have no problem with the _implementation_ being hardwired to the platform. As you said, implementation cannot be cross-platform. Of course it is the interfaces that are to be abstracted. But WinSTL (which was the topic) has some interfaces that only makes sense on a Windows platform (registry/windows directory etc), and rightfully so, since it doesn't purport being cross-platform.
So, again: If you need to learn something NEW, I'd recommend going for a cross-platform library (like wxWidgets f.i.), because that would give a head start if you'd want to write cross-platform software. Otherwise simpler solutions suffice.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I'll give this a try in my game. I'm using stl and not looking to add boost as well. Was looking for a quick way to get screenshot## and find highest one so I can save yet another. Er before I get too excited is this only for .net? I'm just using regular unleaded c++. Well if not guess I will have to delve into boost.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
I did, and decided not to use it, because it does not support Unicode on Win32. That means it only works for file names from a single code page.
Keith
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
It's a bit harsh blaming boost libraries for not being usable with MS VC6. It does not conform to the relevant C++ standards, whereas VC7.1 does.
Keith
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I tried to compile boost library, but i had some prolems... Library compiled without any error, but I couldn't include it into my project. I'm using VC++ 6 and STLport-4.5.3, I gave right options during compilation (msvc-stlport e STLPORT_PATH) and I inserted the right path for includes and libraries in Options->Directories When I compile my project in Debug mode, VC gives me this error: warning: STLPort debug versions are built with /D_STLP_DEBUG=1 c:\librerie\boost_1_32_0\boost\config\auto_link.hpp(170) : fatal error C1189: #error : "Build options aren't compatible with pre-built libraries" Otherwise, when I compile in Release mode sometimes VC gives me link error - unresolved externals! Any ideas? Thank you very much
Gianluca Nastasi
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
I'm not pretty sure.. anyway are you trying to use boost::filesystem library? If you are, just try to change the code generation mode to 'multithreaded mode'
I remember I did it, and then succeeded to compile it. But It caused kinda infinite loop at runtime as I remember,, It might have some bugs related to boost::thread library with VS6 That's why I changed my IDE to vs.net 2003.. then everything is OK and fine..
I hope it helps you..
Best Regards. Bektek
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Yes, I'm trying to use boost::filesystem... I setted the right options in code generation too, but I can't work it out  Thanks a lot! Sadly, I must use VC++ 6 'couse of my job! I'd prefer programming for linux with gcc!  Bye
Gianluca Nastasi
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
If what you're doing, is simply iterating through a directory (or directories) for a particular file, why the need for reference counting?
Secondly, you're using a conversion operator to dereference the '_strfname' object. What I don't understand is the return type you're using, and why couldn't the two operations be consolidated (viz. those routines in which the conversion operator is used, with the work of the conversion operator itself)? It seemed to me you could have ended up with a single routine that would have accomplished both activities.
A handy little tool, nonetheless.
William
Fortes in fide et opere!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|