The Ensure Cleanup Template C Class (EnsureCleanup.h)

[Previous] [Next]

Certainly, the code forgetting to close or release a resource is one of the most common programming bugs developers face. To guarantee that a resource is properly cleaned up when its use goes out of scope, I created the CEnsureCleanup template C++ class shown in Listing B-1. Because this class is a template class, it can help you to clean up almost any type of object you can imagine.

I already defined specific instances of the CEnsureCleanup class for the most common Microsoft Windows data types. Defining a type requires three pieces of information: the type of the object, the address of the function used to clean up the object, and a value that indicates that the object is invalid (usually NULL or 0). For example, to clean up a kernel object, the data type is a HANDLE, the function used to clean up the object is CloseHandle, and an invalid handle is identified with NULL. Similarly, to clean up a dynamically loaded DLL, the data type is an HMODULE, the function used to clean up the resources is FreeLibrary, and an invalid object is identified with NULL. You can see all the different clean-up classes at the end of the CEnsureCleanup.h file.

Using the CEnsureCleanup class could not be easier. All you have to do is use an instance of the class wherever you'd typically use the normal data type. For example, you wouldn't have to write this:

 HANDLE hfile = CreateFile(...);  CloseHandle(hfile); 

You'd now write this:

 CEnsureCloseFile hfile = CreateFile(...);  

With the C++ class data type, you never have to call CloseHandle to release the resource. When the C++ object goes out of scope, the destructor is called and CloseHandle will be called for you automatically! In fact, you're guaranteed that the object will be cleaned up even if you return from the middle of a function or if an exception is raised.

By the way, if you're worried about overhead, don't be. I checked the assembly language produced by the optimizing compiler when using these classes, and the code produced is just as efficient as if you had explicitly called the cleanup functions yourself. This is because the CEnsureCleanup class uses all inline functions. I simply love these classes, and I can't believe that I've been programming all these years without them. I'm now using them for almost everything I do.

Listing B-1. The EnsureCleanup.h header file

 

EnsureCleanup.h

/****************************************************************************** Module: EnsureCleanup.h Notices: Copyright (c) 2000 Jeffrey Richter Purpose: These classes ensure object cleanup when an object goes out of scope. See Appendix B. ******************************************************************************/ #pragma once // Include this header file once per compilation unit /////////////////////////////////////////////////////////////////////////////// #include "..\CmnHdr.h" // See Appendix A. /////////////////////////////////////////////////////////////////////////////// // Data type representing the address of the object's cleanup function. // I used UINT_PTR so that this class works properly in 64-bit Windows. typedef VOID (WINAPI* PFNENSURECLEANUP)(UINT_PTR); /////////////////////////////////////////////////////////////////////////////// // Each template instantiation requires a data type, address of cleanup // function, and a value that indicates an invalid value. template<class TYPE, PFNENSURECLEANUP pfn, UINT_PTR tInvalid = NULL> class CEnsureCleanup { public: // Default constructor assumes an invalid value (nothing to cleanup) CEnsureCleanup() { m_t = tInvalid; } // This constructor sets the value to the specified value CEnsureCleanup(TYPE t) : m_t((UINT_PTR) t) { } // The destructor performs the cleanup. ~CEnsureCleanup() { Cleanup(); } // Helper methods to tell if the value represents a valid object or not.. BOOL IsValid() { return(m_t != tInvalid); } BOOL IsInvalid() { return(!IsValid()); } // Re-assigning the object forces the current object to be cleaned-up. TYPE operator=(TYPE t) { Cleanup(); m_t = (UINT_PTR) t; return(*this); } // Returns the value (supports both 32-bit and 64-bit Windows). operator TYPE() { // If TYPE is a 32-bit value, cast m_t to 32-bit TYPE // If TYPE is a 64-bit value, case m_t to 64-bit TYPE return((sizeof(TYPE) == 4) ? (TYPE) PtrToUint(m_t) : (TYPE) m_t); } // Cleanup the object if the value represents a valid object void Cleanup() { if (IsValid()) { // In 64-bit Windows, all parameters are 64-bits, // so no casting is required pfn(m_t); // Close the object. m_t = tInvalid; // We no longer represent a valid object. } } private: UINT_PTR m_t; // The member representing the object }; /////////////////////////////////////////////////////////////////////////////// // Macros to make it easier to declare instances of the template // class for specific data types. #define MakeCleanupClass(className, tData, pfnCleanup) \ typedef CEnsureCleanup<tData, (PFNENSURECLEANUP) pfnCleanup> className; #define MakeCleanupClassX(className, tData, pfnCleanup, tInvalid) \ typedef CEnsureCleanup<tData, (PFNENSURECLEANUP) pfnCleanup, \ (INT_PTR) tInvalid> className; /////////////////////////////////////////////////////////////////////////////// // Instances of the template C++ class for common data types. MakeCleanupClass(CEnsureCloseHandle, HANDLE, CloseHandle); MakeCleanupClassX(CEnsureCloseFile, HANDLE, CloseHandle, INVALID_HANDLE_VALUE); MakeCleanupClass(CEnsureLocalFree, HLOCAL, LocalFree); MakeCleanupClass(CEnsureGlobalFree, HGLOBAL, GlobalFree); MakeCleanupClass(CEnsureRegCloseKey, HKEY, RegCloseKey); MakeCleanupClass(CEnsureCloseServiceHandle, SC_HANDLE, CloseServiceHandle); MakeCleanupClass(CEnsureCloseWindowStation, HWINSTA, CloseWindowStation); MakeCleanupClass(CEnsureCloseDesktop, HDESK, CloseDesktop); MakeCleanupClass(CEnsureUnmapViewOfFile, PVOID, UnmapViewOfFile); MakeCleanupClass(CEnsureFreeLibrary, HMODULE, FreeLibrary); /////////////////////////////////////////////////////////////////////////////// // Special class for releasing a reserved region. // Special class is required because VirtualFree requires 3 parameters class CEnsureReleaseRegion { public: CEnsureReleaseRegion(PVOID pv = NULL) : m_pv(pv) { } ~CEnsureReleaseRegion() { Cleanup(); } PVOID operator=(PVOID pv) { Cleanup(); m_pv = pv; return(m_pv); } operator PVOID() { return(m_pv); } void Cleanup() { if (m_pv != NULL) { VirtualFree(m_pv, 0, MEM_RELEASE); m_pv = NULL; } } private: PVOID m_pv; }; /////////////////////////////////////////////////////////////////////////////// // Special class for freeing a block from a heap // Special class is required because HeapFree requires 3 parameters class CEnsureHeapFree { public: CEnsureHeapFree(PVOID pv = NULL, HANDLE hHeap = GetProcessHeap()) : m_pv(pv), m_hHeap(hHeap) { } ~CEnsureHeapFree() { Cleanup(); } PVOID operator=(PVOID pv) { Cleanup(); m_pv = pv; return(m_pv); } operator PVOID() { return(m_pv); } void Cleanup() { if (m_pv != NULL) { HeapFree(m_hHeap, 0, m_pv); m_pv = NULL; } } private: HANDLE m_hHeap; PVOID m_pv; }; ///////////////////////////////// End of File /////////////////////////////////



Programming Server-Side Applications for Microsoft Windows 2000
Programming Server-Side Applications for Microsoft Windows 2000 (Microsoft Programming)
ISBN: 0735607532
EAN: 2147483647
Year: 2000
Pages: 126

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net