Use RAII
C++ c++ win32
Published: 2005-09-09
Use RAII

This is covered by any halfway-decent C++ book, but I believe it deserves reiteration: Use the RAII idiom. I don’t think I could explain RAII any better than HackCraft does in The RAII Programming Idiom.

Let me demonstrate how to use RAII with a semi-contrived example. Here’s the pre-RAII code:

1
2
3
4
5
6
7
8
9
HMODULE hm = LoadLibrary(_T("user32.dll"));
if (hm != NULL) {
    FARPROC proc = GetProcAddress(hm, "MessageBoxW");
    if (proc != NULL) {
        typedef int (WINAPI *FnMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT);FnMessageBoxW fnMessageBoxW = (FnMessageBoxW) proc;
        fnMessageBoxW(NULL, L"Hello World!", L"Hello World", MB_OK);
    }
    FreeLibrary(hm);
}

In this case, the resource wrapped is HMODULE, the resource acquisition function is LoadLibrary(), and the resource release function is FreeLibrary(). Beware of resources which have multiple resource release functions, such as Win32’s HANDLE with FindClose() and CloseHandle(); for these cases you will typically have to write multiple RAII classes. I will call the wrapper class HModule. Here’s how its use will look:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
HModule hm(LoadLibrary(_T("user32.dll")));
if (hm != NULL) {
    FARPROC proc = GetProcAddress(hm, "MessageBoxW");
    if (proc != NULL) {
        typedef int (WINAPI *FnMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT);
        FnMessageBoxW fnMessageBoxW = (FnMessageBoxW) proc;
        fnMessageBoxW(NULL, L"Hello World!", L"Hello World", MB_OK);
    }
}

// FreeLibrary() will happen automatically when hm
// goes out of scope

Things to note:

  1. I replaced the assignment statement HMODULE hm = LoadLibrary(_T("user32.dll")); with the construction statement HModule hm(LoadLibrary(_T("user32.dll")));. On newer compilers this may not be necessary.
  2. HModule needs to handle the case where LoadLibrary() fails—it must not call FreeLibrary() on an invalid HMODULE. Be careful of types such as HANDLE as it uses both NULL and INVALID_HANDLE_VALUE to denote invalid values.
  3. HModule should be transparently convertible to a HMODULE. This implies either inheriting from HMODULE or, preferably, providing a conversion operator to HMODULE. (Some resource wrapping classes, like STL’s std::basic_string, prefer a method rather than a conversion operator (c_str() in this case), probably to minimize unexpected, implicit conversions. The choice is up to you.)
  4. If a user calls FreeLibrary(hm); that will result in a double-free, a potentially dangerous bug. I don’t think there’s a whole lot that can be done about this besides saying “Don’t do that!”
  5. We need to worry assignment and the copy constructor: the default implementations will lead to double-free bugs. A few solutions are: disabling the operators, but this makes the class less useful than it could be; turning assignment/copy construction into transfer of ownership like STL’s std::auto_ptr, but this causes some unexpected semantics; or using reference counting like Boost’s shared_ptr, but this adds often-unneeded complexity and, by itself, introduces the problem of circular references. For simplicity, I will simply choose to disable assignment and the copy constructor.

Given these observations, here’s my version of HModule:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class HModule
{
private:
    HMODULE m_hm;

public:
    explicit HModule(HMODULE hm) : m_hm(hm) {}
    ~HModule() { if (m_hm) { FreeLibrary(m_hm); }}
    operator HMODULE() const { return m_hm; }

private:
    // Disable copy construction and assignment
    HModule(const HModule& hm);
    HModule& operator=(const HModule& hm);
};

Obviously, there’s a lot more that can be done to HModule, such as supporting release of ownership, reassignment, etc. However, by using RAII classes like HModule exclusively, you will immediately reap the gains of less thought about resource management, fewer bugs, and you will be one step closer towards exception safety.

BTW, if you don’t want to go to the trouble of writing RAII classes, there’s also ScopeGuard.