C++ обёртка для HANDLE

Пару лет назад я пользовался вот такой C++ обёрткой для HANDLE (часть методов и обработка ошибок опущены):

/// @brief  A generic policy-based wrapper for a handle value
template <typename T, typename PolicyT>
class
{
public:
    /// @brief  Construct a wrapper from a raw value
    HandleT(const T& _handle = PolicyT::null()):
        m_handle(_handle)
    {}

    /// @brief  Duplicate a handle
    HandleT(const HandleT& _right):
        m_handle(PolicyT::copy(_right.get()))
    {}

    ~HandleT()
    {
        release();
    }

    /// @brief  Duplicate a handle
    HandleT& operator=(const HandleT& _right)
    {
        release();
        m_handle = PolicyT::copy(_right.get());

        return *this;
    }

    /// @brief  Close the enclosed handle
    void
    release()
    {
        PolicyT::release(m_handle);
        m_handle = PolicyT::null();
    }

private:
    T m_handle;
};

Идея была – написать класс, который:

  1. Автоматически освобождает ресурсы при выходе из области видимости;

  2. Обеспечивает легкость копирования описателей;

  3. Умеет работать с различными типами описателей.

Чтобы удовлетворить третий пункт базовые операции были вынесены в отдельные классы “политик” (policy classes). Политика стандартного Win32 HANDLE выглядела так (обработка ошибок опущена):

/// @brief Handle pocily for Win32 HANDLE
struct
{
    static HANDLE null()
    {
        return NULL;
    }

    /// @brief  Release a handle
    static void release(const HANDLE& _handle)
    {
        if (_handle)
        {
            ::CloseHandle(_handle);
        }
    }

    /// @brief  Make a copy of the given handle
    static HANDLE copy(const HANDLE& _handle)
    {
        HANDLE retval = NULL;

        if (_handle != NULL)
        {
            DuplicateHandle(
                GetCurrentProcess(),
                _handle,
                GetCurrentProcess(),
                &retval,
                0,
                FALSE,
                DUPLICATE_SAME_ACCESS);
        }

        return retval;
    }
};

И, наконец, тип класса обертки объявлялся так:

/// @brief  A wrapper for Win32 HANDLE
typedef HandleT<HANDLE, HandlePolicy> Handle;

Таким образом, класс Handle соответствовал двум оставшимся требованиям и был, надо сказать, весьма удобен в использовании. Тем не менее, эта реализация обладала одним существенным недостатком. Каким? Для копирования описателя использовался функция DuplicateHandle. Что не так с DuplicateHandle? DuplicateHandle - слишком мощная и тяжеловесная операция для простого копирования описателя. С++ класс, по определению, используется в рамках одного приложения. DuplicateHandle – умеет копировать описатели между процессами и в силу этого её реализация относительно сложна. Описатели процесса организованы в таблицу и добавление описателя в таблицу довольно дорогая операция. Не говоря уже о том, что DuplicateHandle делает вызов в ядро, что тоже не добавляет скорости. В результате, приложение, интенсивно копирующее описатели, генерирует многочисленные вызовы DuplicateHandle, которые, в общем-то, не нужны.

Каким образом это можно исправить? Довольно легко. Достаточно вместо HANDLE использовать “умный” указатель на HANDLE со счётчиком ссылок. Тогда копирование описателя будет эквивалентно копированию указателя - т.е. копированию 4 или 8 байт и инкременту счетчика.

comments powered by Disqus