Создание HANDLE RAII-совместимого с использованием shared_ptr с пользовательским средством удаления - PullRequest
10 голосов
/ 13 октября 2009

Недавно я опубликовал общий вопрос о RAII на SO . Однако у меня все еще есть проблемы с реализацией моего примера HANDLE.

A HANDLE является typedeffed до void * in windows.h. Следовательно, правильное определение shared_ptr должно быть

std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle);

Пример 1 CreateToolhelp32Snapshot: возвращает HANDLE и работает.

const std::tr1::shared_ptr<void> h
    (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle);

Поскольку я использую void в определении (каков правильный путь?), Возникают проблемы, когда я пытаюсь вызвать еще несколько команд winapi с этим указателем. Они функционально работают, но безобразны, и я уверен, что должно быть лучшее решение.

В следующих примерах h - указатель, который был создан с помощью определения вверху.

Пример 2 OpenProcessToken: последний аргумент - PHANDLE. средний уродливый с актерами.

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
    (PHANDLE)&h);

Пример 3 Process32First: первый аргумент - HANDLE. ДЕЙСТВИТЕЛЬНО некрасиво.

Process32First(*((PHANDLE)&h), &pEntry);

Пример 4 Простое сравнение с константой HANDLE. ДЕЙСТВИТЕЛЬНО некрасиво.

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ }

Как правильно создать правильный shared_ptr для HANDLE?

Ответы [ 4 ]

9 голосов
/ 14 октября 2009

Пример 1 в порядке

Пример 2 неверен. При слепом приведении к PHANDLE логика shared_ptr обойдется. Вместо этого должно быть что-то вроде этого:

HANDLE h;
OpenProcessToken(...., &h);
shared_ptr<void> safe_h(h, &::CloseHandle);

или, чтобы присвоить существующему shared_ptr:

shared_ptr<void> safe_h = ....
{
  HANDLE h;
  OpenProcessToken(...., &h);
  safe_h.reset(h, &::CloseHandle);
}//For extra safety, limit visibility of the naked handle

или создайте свою собственную безопасную версию OpenProcessToken, которая возвращает совместно используемый дескриптор вместо взятия PHANDLE:

// Using SharedHandle defined at the end of this post
SharedHandle OpenProcess(....)
{
    HANDLE h = INVALID_HANDLE_VALUE;
    ::OpenProcessToken(...., &h);
    return SharedHandle(h);
}

Пример 3: Нет необходимости брать эти объезды. Это должно быть хорошо:

Process32First(h.get(), ...);

Пример 4: опять нет объезда:

if (h.get() == INVALID_HANDLE){...}

Чтобы сделать вещи лучше, вы можете ввести что-то вроде:

typedef shared_ptr<void> SharedHandle;

или еще лучше, если все дескрипторы должны быть закрыты с помощью CloseHandle (), создайте класс SharedHandle, обертывающий shared_ptr и автоматически предоставляющий правильное средство удаления:

// Warning: Not tested. For illustration purposes only
class SharedHandle
{
public:
  explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){};
  HANDLE get()const{return m_Handle.get();}

  //Expose other shared_ptr-like methods as needed
  //...

private:
  shared_ptr<void> m_Handle;
};
3 голосов
/ 02 февраля 2012

Не используйте для этого shared_ptr, используйте ATL :: CHandle.

Вот почему:

  • Когда вы видите CHandle, вы знаете, что это оболочка RAII для ручки.
  • Когда вы видите shared_ptr<void>, вы не знаете, что это такое.
  • CHandle не делит право владения (однако в некоторых случаях вам может потребоваться совместное владение).
  • CHandle - это стандарт для стека разработки Windows.
  • CHandle более компактный, чем shared_ptr<void> с пользовательским удалением (меньше печатания / чтения).
2 голосов
/ 14 октября 2009
1 голос
/ 02 февраля 2012

Вот моя альтернатива, которая довольно хороша, за исключением того, что вам нужно разыменовывать всегда после .get() и требуется функтор или лямбда:

template<typename HandleType, typename Deleter>
std::shared_ptr<HandleType> make_shared_handle(HandleType _handle, Deleter _dx)
{
    return std::shared_ptr<HandleType>(new HandleType(_handle), _dx);
}

тогда:

auto closeHandleDeleter = [](HANDLE* h) { ::CloseHandle(*h); };
std::shared_ptr<HANDLE> sp = make_shared_handle(a_HANDLE, closeHandleDeleter);
f_that_takes_handle(*sp.get());

что мне больше всего нравится в этом, так это то, что нет никакой дополнительной работы, чтобы иметь доступ к этому:

std::weak_ptr<HANDLE> wp = sp; // Yes. This could make sense in some designs.

и, конечно, вспомогательная функция работает с любыми типами дескрипторов лайков.

...