官术网_书友最值得收藏!

21. System handle wrapper

System handles are a form of reference to system resources. Because all operating systems were at least initially written in C, creating and releasing the handles is done through dedicated system functions. This increases the risk of leaking resources because of erroneous disposal, such as in the case of an exception. In the following snippet, specific to Windows, you can see a function where a file is opened, read from, and eventually closed. However, this has a couple of problems: in one case, the developer forgot to close the handle before leaving the function; in another case, a function that throws is called before the handle is properly closed, without the exception being caught. However, since the function throws, that cleanup code never executes:

void bad_handle_example()
{
bool condition1 = false;
bool condition2 = true;
HANDLE handle = CreateFile(L"sample.txt",
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr);

if (handle == INVALID_HANDLE_VALUE)
return;

if (condition1)
{
CloseHandle(handle);
return;
}

std::vector<char> buffer(1024);
unsigned long bytesRead = 0;
ReadFile(handle,
buffer.data(),
buffer.size(),
&bytesRead,
nullptr);

if (condition2)
{
// oops, forgot to close handle
return;
}

// throws exception; the next line will not execute
function_that_throws();

CloseHandle(handle);
}

A C++ wrapper class can ensure proper disposal of the handle when the wrapper object goes out of scope and is destroyed (whether that happens through a normal execution path or as the result of an exception). A proper implementation should account for different types of handles, with a range of values to indicate an invalid handle (such as 0/null or -1). The implementation shown next provides:

  • Explicit acquisition and automatic release of the handle when the object is destroyed
  • Move semantics to enable transfer of ownership of the handle
  • Comparison operators to check whether two objects refer to the same handle
  • Additional operations such as swapping and resetting

The implementation shown here is a modified version of the handle class implemented by Kenny Kerr and published in the article Windows with C++ - C++ and the Windows API, MSDN Magazine, July 2011, https://msdn.microsoft.com/en-us/magazine/hh288076.aspx. Although the handle traits shown here refer to Windows handles, it should be fairly simple to write traits appropriate for other platforms.

template <typename Traits>
class unique_handle
{
using pointer = typename Traits::pointer;
pointer m_value;
public:
unique_handle(unique_handle const &) = delete;
unique_handle& operator=(unique_handle const &) = delete;

explicit unique_handle(pointer value = Traits::invalid()) noexcept
:m_value{ value }
{}

unique_handle(unique_handle && other) noexcept
: m_value{ other.release() }
{}

unique_handle& operator=(unique_handle && other) noexcept
{
if (this != &other)
reset(other.release());
return *this;
}

~unique_handle() noexcept
{
Traits::close(m_value);
}

explicit operator bool() const noexcept
{
return m_value != Traits::invalid();
}

pointer get() const noexcept { return m_value; }

pointer release() noexcept
{
auto value = m_value;
m_value = Traits::invalid();
return value;
}

bool reset(pointer value = Traits::invalid()) noexcept
{
if (m_value != value)
{
Traits::close(m_value);
m_value = value;
}
return static_cast<bool>(*this);
}

void swap(unique_handle<Traits> & other) noexcept
{
std::swap(m_value, other.m_value);
}
};

template <typename Traits>
void swap(unique_handle<Traits> & left, unique_handle<Traits> & right) noexcept
{
left.swap(right);
}

template <typename Traits>
bool operator==(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) noexcept
{
return left.get() == right.get();
}

template <typename Traits>
bool operator!=(unique_handle<Traits> const & left,
unique_handle<Traits> const & right) noexcept
{
return left.get() != right.get();
}

struct null_handle_traits
{
using pointer = HANDLE;
static pointer invalid() noexcept { return nullptr; }
static void close(pointer value) noexcept
{
CloseHandle(value);
}
};

struct invalid_handle_traits
{
using pointer = HANDLE;
static pointer invalid() noexcept { return INVALID_HANDLE_VALUE; }
static void close(pointer value) noexcept
{
CloseHandle(value);
}
};

using null_handle = unique_handle<null_handle_traits>;
using invalid_handle = unique_handle<invalid_handle_traits>;

With this handle type defined, we can rewrite the previous example in simpler terms, avoiding all those problems with handles not properly closed because of exceptions occurring that are not properly handled, or simply because developers forget to release resources when no longer needed. This code is both simpler and more robust:

void good_handle_example()
{
bool condition1 = false;
bool condition2 = true;

invalid_handle handle{
CreateFile(L"sample.txt",
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr) };

if (!handle) return;

if (condition1) return;

std::vector<char> buffer(1024);
unsigned long bytesRead = 0;
ReadFile(handle.get(),
buffer.data(),
buffer.size(),
&bytesRead,
nullptr);

if (condition2) return;

function_that_throws();
}
主站蜘蛛池模板: 泽库县| 永昌县| 津南区| 林州市| 琼海市| 襄城县| 海宁市| 厦门市| 津市市| 沛县| 隆化县| 台东县| 文昌市| 霸州市| 南溪县| 汝阳县| 外汇| 南岸区| 玛沁县| 汪清县| 苍溪县| 和顺县| 海口市| 华阴市| 同江市| 攀枝花市| 固安县| 凤城市| 鄂州市| 边坝县| 霸州市| 丰台区| 漳州市| 延庆县| 和政县| 本溪| 广宗县| 宁城县| 金华市| 新源县| 务川|