Create your own Iterator Adapter - File Iterator

xiaoxiao2021-03-06  18

Introduction

Sometimes we need to list files in a folder in Windows. According to a conventional method, the search file is inserted in a certain linked list. For example, the following pseudo code

Void List_file (const st: string & pat, file_list& flist)

{

WHILE (determining if the PATH has a file)

{

Filst.push_back; file name;

Find the next file;

}

}

List_file This algorithm can work normally, but it is closely coupled to the data container file_list to form a very high coupling. If, now you need to search for the specified file in a folder, then you need to search or re-design a algorithm with list_file.

For such a design, let's go back to STL, STL shows us new thinking of C programming. Iterators is an abstract concept that provides a method to traverse all elements in a polymer without exposing detail interior of the polymer. The iterator in STL separates the data container and algorithm and reaches the generalization of the algorithm. More importantly, it provides a unified form to allow the program to operate different data structures.

Then we can abstract a folder as a data container, while the subfolders and files are abstract as an element. In this way, an iterator can be designed to traverse files in the folder. If you design a iterator that meets the STL specification, you can also combine with the STL algorithm to utilize the algorithm provided by STL.

Design goals

Take a look at the code below

Void Display (const file_info & x)

{

Std :: cout << (x.is_dir ()? "*": "); // Output * means this is a folder

Std :: cout << x.file_name () << std :: endl;

}

int main ()

{

Std :: for_each (file_iterator ("c: // windows"), file_iterator (), display);

}

We can also put the file list in a container

Std :: vector vec_file;

Std :: copy (file_iterator ("c: // windows"), file_iterator (), std :: back_inserter (vec_file);

We need to recall the concept of the STL in the STL specification.

Let's get started

In fact, iterators are not a simple concept, and STL divides the iterator into five categories according to different operations:

Input iterator can only be single-step forward, and do not allow modification of elements referenced by this type iterator.

Output Iterator This type iterator and ITERATOR are extremely similar, and can only be single-step iterative elements, and the difference is that the type of iterator is only written to the element.

Forward Iterator This type iterator can read and write in a correct interval, which has all features of the Input Iterator, and the partial feature of Output Iterator, and the ability of single-step iterative elements.

The Bidirectional Iterator This type of iterator is the ability to provide a single-step backbound iterative element based on the Forward Iterator.

Random Access Iterator This type of iterator can complete the work of all the iterators above, its own unique features are arithmetic calculations like pointers, rather than just single step forward or backward. Iter N, indicating that movement to N units in a constant time. The dependence of the five types iterator, as shown in the following figure, in which arrows A → B indicate that A is the intensive type of B, which also illustrates that if an algorithm requires B, then A can also be applied therein.

Start design

First, in the top five iterator, one of our upcoming Iterator, combines the actual situation, our purpose is to get files in a directory, then Input Iterator is what we choose.

An Input Iterator requires:

1, can be constructed by default: provide the default constructor

2, copy: provide copy constructor and copy assignment operator

3, comparatively: provide Operator ==, and Operator! =

4, single-step forward iterative ability: provide front self-amplifier operators and rear self-increasing operations

5, solution quote: Provide Operator *

How to construct an iterator pointing to the end? You can use the ITREAM ITERATORS, the object of the iterator is constructed by default, indicating an iterator pointing to the end.

How to deal with Windows by Windows during the file iteration? For file iterators to be designed, there is no write operation, you can exit iteration, and the error caused by Windows is necessary to pass to the caller, but it is impossible to pass the traditional return value here because Iterator It is an object, not the call of the function, so we can consider using anomalies. Throw an exception object for file_exception.

VALUE_TYPE for file iterators. Since the information of the file is provided by Windows, the information of Windows uses the information of the file in a Win32_find_data structure object. In order to pass the file information to the object of value_type, it is necessary to provide Value_Type to provide a T (Const Win32_Find_Data &) constructor.

Resource problem, the search for files will use the handle returned by Windows. The handle of this system object is not like a copy constructor like a memory resource in C , and the upcoming file iterator can be copied, which means Multiple file iterators will reference the same system object, so that when the file iterator is destructed, it cannot correctly determine whether or not to release the current system object, how to solve this problem? We can design a local class file_operate_raii in the file iterator, through RAII skills, and add a reference count to successfully solve this problem.

Thread security issues, due to the above mentioned file_operate_raii is actually a total object read by multiple file iterators, then we must consider the problem of thread security.

The code is the design, let's take a look at the implemented C code

// file: file_iterator.h

#ifndef _file_iterator_h

#define _file_iterator_h

#include

#include

#include

Namespace f_iter

{

Class file_exception

: Public Std :: Exception

{

PUBLIC:

Const char * what () const throw () {

Return "An Error Was Occurred";

}

}

Template

Class file_iterator

: public st: ip :: Iterator_tag, _value_type>

{

PUBLIC:

FILE_ITERATOR (const std :: string & file_path)

: END_ (FALSE), File_Path_ (file_path), HFIND_ (0), P_FO_RAII_ (0)

{

_m_prepare ();

}

File_iterator (): end_ (true), hfind_ (0), p_fo_raii_ (0) {} // Default Construction

File_iterator (const file_iterator & x) // copied

: END_ (X.End_), file_path_ (x.file_path_), hfind_ (x.hfind_), p_fo_rii_ (x.p_fo_raii_), value_ (x.value_)

{

IF (p_fo_raii_)

p_fo_raii _-> incsease ();

}

File_iterator & operator = (const file_iterator & x) // can copy

{

IF (this == & x) Return * this; / / Prevent self copy

End_ = x.end_;

FILE_PATH_ = X.file_path_;

Hfind_ = x.hfind_;

Value_ = x.Value_;

IF (p_fo_raii_)

{

p_fo_raii _-> Decrease ();

IF (p_fo_raii _-> EMPTY ())

{

Delete p_fo_raii_;

}

}

p_fo_raii_ = x.p_fo_raii_;

IF (p_fo_raii_)

p_fo_raii_ -> increase ();

Return * this

}

~ file_iterator ()

{

IF (p_fo_raii_)

{

p_fo_raii _-> Decrease ();

IF (p_fo_raii _-> EMPTY ())

{

Delete p_fo_raii_;

P_fo_raii_ = 0;

}

}

}

PUBLIC:

Const _Value_type & // Read only the element, so return const

Operator * () const {return value_;}

Const _value_type *

Operator -> () const {return (operator * ());

File_iterator & // The ability to own forward iteration

Operator ()

{_m_read (); return * this;

FILE_ITERATOR

Operator (int)

{

File_iterator TMP = * this;

_m_read ();

Return TMP;

}

BOOL Equal (const file_iterator) const

{

IF (end_ && (end_ == x.end_)) Return True;

Return (strcmp (find_file_data_.cfilename, x.find_file_data_.cfilename) == 0

&&&&

FILE_PATH_ == X.file_path_);

}

Private:

Void _m_prepare ()

{

IF (p_fo_raii_ == 0)

P_fo_raii_ = new file_operate_raii (file_path_, find_file_data_);

Hfind_ = p_fo_raii _-> get_handle ();

IF (HFIND_ == Invalid_Handle_Value)

{

End_ = true;

HFIND_ = 0;

Return;

}

IF (* find_file_data_.cfilename == '.')

{

:: FindNextFile (HFIND_, & FIND_FILE_DATA_);

IF (: FindNextFile (Hfind_, & Find_File_Data_)! = 0)

Value_ = _Value_type (Find_File_Data_); // The requirements of _Value_type have T (Const Win32_Find_Data &) constructor

Else

{

End_ = true;

HFIND_ = 0;

IF (error_no_more_files! = :: getLastError ())

Throw file_exception ();

}

}

Else

{

Value_ = _Value_type (find_file_data_);

}

}

Void_m_read ()

{

IF (FindNextFile (Hfind_, & Find_File_Data_)! = 0)

{

Value_ = _Value_type (Find_File_Data_); // The requirements of _Value_type have T (Const Win32_Find_Data &) constructor

}

Else

{

End_ = true;

HFIND_ = 0;

IF (error_no_more_files! = :: getLastError ())

Throw file_exception ();

}

}

Private:

// Class Lock Solve thread security issues

Class lock {

PUBLIC:

Lock () {

:: InitializeCriticalSection (& CS_);

}

~ lock () {

:: DeletecriticalSection (& CS_);

}

Void Enter () const {

:: EntercriticalSection (& CS_);

}

Void Leave () const {

:: LeaveCriticalSection (& CS_);

}

Private:

Mutable :: critical_section cs_;

}

// Class File_operate_raii is responsible for the creation and release of the system object

Class file_operate_raii

{

FILE_OPERATE_RAII (); // Non-Default-Constructable

FILE_OPERATE_RAII (const file_operate_raii); // Non-Copyable

FILE_OPERATE_RAII & OPERATOR = (const file_operate_raii); // Non-CopyAble

PUBLIC:

File_operate_raii (const st: string & x, win32_find_data & y)

: counter_ (1)

{

Handle_ = :: FindFirstFile ((x "// *"). c_str (), & y);

~ file_operate_raii ()

{

:: FindClose (Handle_);

}

PUBLIC:

Void increase ()

{

LOCK_.Enter ();

Counter_ ;

Lock_.Leave ();

}

Void Decrease ()

{

LOCK_.Enter ();

Counter_-;

Lock_.Leave ();

}

BOOL EMPTY () Const

{Return Counter_ == 0;}

SIZE_T Count () Const

{Return Counter_;}

:: Handle Get_Handle () Const

{Return Handle_;

Private:

:: Handle Handle_;

SIZE_T Counter_;

}

Private:

BOOL end_;

_Value_type value_;

Std :: string file_path_;

WIN32_FIND_DATA FIND_FILE_DATA_;

:: Handle Hfind_;

FILE_OPERATE_RAII * P_FO_RAII_;

}

Template

Inline Bool Operator == (const file_iterator <_value_type> & x, const file_iterator <_value_type> & y)

{

Return x.equal (y);

}

Template

Inline Bool Operator! = (const file_iterator <_value_type> & x, const file_iterator <_value_type> & y)

{

Return! x.equal (y);

}

}

#ENDIF

A large pile of code above, you may see something. Because multiple file_iterator objects can reference the same system object, in other words, such as two file_iterator objects A, B

A = B;

IF ( a == b) {} // This judgment will not be established

Yes, the Input Iterator has this feature!

Finally, let's use this file_iterator

#include "file_iterator.h"

#include

#include

Using namespace std;

Class file_info

{

PUBLIC:

FILE_INFO (): IS_DIR_ (FALSE)

{}

FILE_INFO (const win32_find_data "// file_iterator requires this constructor

: Is_dir _ (file_attribute_directory & x.dwfileAttributes) == file_attribute_directory, file_name_ (x.cfilename)

{}

PUBLIC:

Std: string file_name () const

{Return file_name_;}

BOOL IS_DIR () const {return is_dir_;}

Private:

BOOL IS_DIR_;

Std :: string file_name_;

}

Void Display (const file_info & x)

{

Std :: cout << (x.is_dir ()? "*": "");

Std :: cout << x.file_name () << std :: endl;

}

int main ()

{

Using namespace std;

Using namespace f_iter;

For_each (file_iterator ("c: // windows"), file_iterator (), display);

}

For this design, there may be BUG, ​​welcome everyone to point out, and if you have any ideas or opinions, I hope that all the emails will be taken! I am very grateful to Boxban to point out the errors in the article and comment on the article.

转载请注明原文地址:https://www.9cbs.com/read-50615.html

New Post(0)