Introduction
There are many instances when programming Win32 where a HANDLE
or other opaque entity is returned from an API call which must be subsequently released or closed with another API call. Managing these releases can get complicated, especially when an API call fails and cleanups are required, or in the event of an exception elsewhere in your code.
AutoClose
is a template class that helps to manage HANDLE
s and similar Windows objects that require releasing after use. The source code demonstrates two usages of the template, an elementary directory listing application, and an application that uses a memory mapped file class built with AutoClose
to open and copy to the console a specified file.
Background
'Resource Acquisition is Initialization' seems to be the catch-all name for the concept of using the constructor and destructor of a class to tie the lifetime of an instance of that class to the lifetime of an external object. Most commonly implemented as 'Smart Pointers' to manage dynamically allocated memory and objects. On the face of it, a Smart Pointer is very simple to design and use, in practice, they are very difficult to get right and the user has to be concerned about copying, reference counting, passing to and fro functions and other semantic issues. The Standard C++ Library includes a limited, albeit very useful type of Smart Pointer, std::auto_ptr
.
class Bar
{
...
} ;
void Foo ()
{
std::auto_ptr<Bar> p ( new Bar ) ;
}
AutoClose
is designed for a similar type of usage to std::auto_ptr
either as a local variable within a function or as a member of a class. It is not a Smart Pointer per se, it doesn't offer operator->
for example. There are a couple of other articles on CodeProject that achieve a similar end, but I think this implementation is simpler if rather less interesting an adventure into C++ templates. The key feature is being able to write the name of the function you want called at 'release' directly into the template definition.
Using the code
AutoClose
is defined thus:
template <typename T, BOOL ( WINAPI * F)( T ), T Invalid = 0> class AutoClose {}
The first template parameter, 'T
' is the type of object to hold, the second 'F
' the API function to call to release the object, which is assumed to take a single parameter of 'T
' and return BOOL
, and the third the value of the object that is treated as 'invalid'.
A typical usage might be:
WIN32_FIND_DATA fd ;
AutoClose<HANDLE, ::FindClose,
INVALID_HANDLE_VALUE> hFD ( ::FindFirstFile ( "*.*", &fd )) ;
if ( hFD )
{
while ( ::FindNextFile ( hFD, &fd ))
{
...
}
}
Or perhaps:
class HasAnEvent
{
private :
AutoClose<HANDLE, ::CloseHandle> hEvent_ ;
public :
HasAnEvent ()
{
hEvent_ = ::CreateEvent ( 0, FALSE, FALSE, 0 ) ;
}
HANDLE Event ()
{
return hEvent_ ;
}
} ;
This is the entire code for the AutoClose
class:
template<typename T, BOOL ( WINAPI * F)( T ), T Invalid = 0> class AutoClose
{
private :
T t_ ;
void Dispose ()
{
if ( t_ != Invalid )
F ( t_ ) ;
}
public :
AutoClose () : t_ ( Invalid )
{
}
explicit AutoClose ( T t ) : t_ ( t )
{
}
~AutoClose ()
{
Dispose () ;
}
AutoClose<T, F, Invalid>& operator= ( T t )
{
if ( t != t_ )
{
Dispose () ;
t_ = t ;
}
return *this ;
}
T Detach ()
{
T t = t_ ;
t_ = Invalid ;
return t ;
}
operator T () const
{
return t_ ;
}
operator bool () const
{
return t_ != Invalid ;
}
bool operator! () const
{
return t_ == Invalid ;
}
private :
AutoClose ( AutoClose<T, F, Invalid> const& ) ;
AutoClose<T, F, Invalid>&
operator= ( AutoClose<T, F, Invalid> const& ) ;
} ;
The constructor that takes a T
argument is declared explicit
to prevent code like this compiling:
int main ( int argc, char ** argv )
{
AutoClose<HANDLE, ::CloseHandle> Handle = argv ;
...
}
The Win32 HANDLE
type boils down to a void*
and any pointer can be converted to a void*
. By making the constructor explicit
, the types must match completely, the above won't compile and so potential coding errors will be trapped. Unfortunately, this also prohibits that style of construction/assignment.
The copy constructor and operator=
that take arguments of type AutoClose
are declared private and not implemented. This is to help avoid ending up with two AutoClose
objects each referencing the same handle yet without duplicating it, and hence 'closing' it more than required.
Detach
allows the managed handle to be removed from the control of the AutoClose
object.
HANDLE GetFile ( LPCTSTR sName )
{
AutoClose<HANDLE, ::CloseHandle,
INVALID_HANDLE_VALUE> hFile ( ::CreateFile ( sName,... )) ;
if ( hFile )
{
if ( !ValidateFileHeader ( hFile ))
{
return INVALID_HANDLE_VALUE ;
}
if ( !ValidateFileContents ( hFile ))
{
return INVALID_HANDLE_VALUE ;
}
}
return hFile.Detach () ;
}
The operator bool
and operator!
allow the AutoClose
object to be tested, whether the contained T
is a valid handle, in a direct way. I suspect there are downsides to this but hopefully they'll show up at compile time....
Build Notes
I've only built and tested this class and sample programs with Visual C++ 7.1 on Windows XP. I would expect them to build on earlier versions but I haven't tested it or supplied solution files.
References
Related Code Project articles:
License
You can do what you like with this class and example code except remove the copyright or sell the source.
History
First release - April 5 2004