Чисто чтобы не забывать закрывать виндовые хендлы, набросал примитивный шаблон:
template<typename T>
class CallOnScopeLeave
{
typedef int (__stdcall *func_type)(T); // int & stdcall need for calling Win API
func_type Func;
T Param;
public:
CallOnScopeLeave(func_type func, T par) : Func(func), Param(par) {};
~CallOnScopeLeave()
{
Func(Param);
};
};
Используется, понятное дело, так:
HANDLE hFile = CreaterFile(...);
if (hFile == INVALID_HANDLE_VALUE)
return;
CallOnScopeLeave<HANDLE>(&CloseHandle, hFile);
// работа с хэндлом...
А вопрос такой. Наверняка эт не я один умный , и такие шаблоны уже существует. Линканите плз какой-нибудь более-менее известный и стандартный. (Без заточки под винду, как у меня, да и вообще хочется посмотреть на "тру" RAII-шаблон )
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Basil2, Вы писали:
B>Чисто чтобы не забывать закрывать виндовые хендлы, набросал примитивный шаблон:
B>
B>template<typename T>
B>class CallOnScopeLeave
B>{
B> typedef int (__stdcall *func_type)(T); // int & stdcall need for calling Win API
B> func_type Func;
B> T Param;
B>public:
B> CallOnScopeLeave(func_type func, T par) : Func(func), Param(par) {};
B> ~CallOnScopeLeave()
B> {
B> Func(Param);
B> };
B>};
B>
B>Используется, понятное дело, так:
B>
B>HANDLE hFile = CreaterFile(...);
B>if (hFile == INVALID_HANDLE_VALUE)
B> return;
B>CallOnScopeLeave<HANDLE>(&CloseHandle, hFile);
B>// работа с хэндлом...
B>
B>А вопрос такой. Наверняка эт не я один умный , и такие шаблоны уже существует. Линканите плз какой-нибудь более-менее известный и стандартный. (Без заточки под винду, как у меня, да и вообще хочется посмотреть на "тру" RAII-шаблон )
Сыроватый у тебя шаблон получился: CallOnScopeLeave не запрещает создание новых копий объекта, которые при своем уничтожении будут закрывать один и тот же хендл. Чтобы довести дело до логического завершения нужно либо запретить копирование для CallOnScopeLeave, либо корректно организовать подсчет ссылок и выполнять закрытие хендла с разрушением последнего объекта.
А из существующих, пожалуй, наиболее известными являются boost::scoped_ptr (монопольное владение) и boost::shared_ptr (совместное владение).
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Кодт, Вы писали:
D>>shared_ptr со заданным деструктором?
К>Только если даннай хэндл является указателем. А что делать с числами? Зверски кастить к void* и обратно?
К>Лучше написать и отполировать свой велосипед. К>Тем более, что не так уж там много нужно для полировки.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, denisko, Вы писали:
D>>shared_ptr со заданным деструктором?
К>Только если даннай хэндл является указателем. А что делать с числами? Зверски кастить к void* и обратно?
К>Лучше написать и отполировать свой велосипед. К>Тем более, что не так уж там много нужно для полировки.
Несколько возражений.
1) Семантически любой Хендл является указателем с флагами.Так что совесть зверский каст оправдает.
2) Можно принимать указатель на хендл, который валяется рядом стеке. Вероятность того, что он запорется, а содержимое shared/scoped ptr останется в целости и сохранности стремительно стремится.
3) Автор, судя по стилю, не сильно склонен заморачиваться с спецификацией исключений и explicitamи. А это вторые и третьи грабли после тех, которые укзазал rg45. Если они начнут срабатывать когда кодом будет пользоваться не только он один, его будут бить. Возможно, даже ногами.
Здравствуйте, rusted, Вы писали:
R>Здравствуйте, Кодт, Вы писали:
D>>>shared_ptr со заданным деструктором?
К>>Только если даннай хэндл является указателем. А что делать с числами? Зверски кастить к void* и обратно?
К>>Лучше написать и отполировать свой велосипед. К>>Тем более, что не так уж там много нужно для полировки.
R>Можно еще через вот такое умбо-юмбо: http://www.revergestudios.com/reblog/index.php?n=ReCode.SharedPtrHandles R>Хоть я и не хотел бы столкнуться с таким трюком в рабочем проекте, но сама идея интересная.
Ты не поверишь, это каноническое использование shared_ptr'a.
Здравствуйте, denisko, Вы писали:
R>>Можно еще через вот такое умбо-юмбо: http://www.revergestudios.com/reblog/index.php?n=ReCode.SharedPtrHandles R>>Хоть я и не хотел бы столкнуться с таким трюком в рабочем проекте, но сама идея интересная. D>Ты не поверишь, это каноническое использование shared_ptr'a.
С таким каноническим использованием появляется дополнительный уровень косвенности там, где он не нужен, а объем дополнительного кода не намного меньше, чем в случае своего специального маленького велосипедика.
Здравствуйте, denisko, Вы писали:
К>>>Только если даннай хэндл является указателем. А что делать с числами? Зверски кастить к void* и обратно?
К>>>Лучше написать и отполировать свой велосипед. К>>>Тем более, что не так уж там много нужно для полировки.
R>>Можно еще через вот такое умбо-юмбо: http://www.revergestudios.com/reblog/index.php?n=ReCode.SharedPtrHandles R>>Хоть я и не хотел бы столкнуться с таким трюком в рабочем проекте, но сама идея интересная. D>Ты не поверишь, это каноническое использование shared_ptr'a.
Не поверю. В случае с хэндлом исключена ситуация с циклическими ссылками, поэтому не надо предусматривать возможность реализации weak_ptr, поэтому нет необходимости держать shared_count в отдельном блоке памяти. Иными словами, если уж пришла в голову странная идея изваять обертку для хэндлов из смартпойнтера, делать ее лучше не из shared_ptr, a из intrusive_ptr — меньшее количество раз память выделять придется.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Тот кто сидит в пруду, Вы писали:
ТКС>Не поверю. В случае с хэндлом исключена ситуация с циклическими ссылками, поэтому не надо предусматривать возможность реализации weak_ptr, поэтому нет необходимости держать shared_count в отдельном блоке памяти.
Борьба с циклическими ссылками — не единственный способ применения weak_ptr. Например есть ещё signals2::slot.track()
Здравствуйте, Тот кто сидит в пруду, Вы писали:
D>>Ты не поверишь, это каноническое использование shared_ptr'a.
ТКС>Не поверю. В случае с хэндлом исключена ситуация с циклическими ссылками, поэтому не надо предусматривать возможность реализации weak_ptr, поэтому нет необходимости держать shared_count в отдельном блоке памяти. Иными словами, если уж пришла в голову странная идея изваять обертку для хэндлов из смартпойнтера, делать ее лучше не из shared_ptr, a из intrusive_ptr — меньшее количество раз память выделять придется.
shared_ptr поддерживает разные экземпляры deleter'a (stateful), тогда как intrusive_ptr — только 'один' (stateless), поэтому сравнивать на лучше/хуже не совсем корректно. Можно посмотреть на unique_ptr из C++0X, у которого есть возможность использования в двух вариантах — статически заданный deleter (один на все объекты), и вариант с ссылкой на него (каждый объект может иметь собственный deleter). Здесь есть реализация.
Здравствуйте, Basil2, Вы писали:
B>А вопрос такой. Наверняка эт не я один умный , и такие шаблоны уже существует. Линканите плз какой-нибудь более-менее известный и стандартный. (Без заточки под винду, как у меня, да и вообще хочется посмотреть на "тру" RAII-шаблон )
ТКС>Не поверю.
Написать свой делетер -- это не каноническое использование?
ТКС>В случае с хэндлом исключена ситуация с циклическими ссылками, поэтому не надо предусматривать возможность реализации weak_ptr, поэтому нет ТКС>необходимости держать shared_count в отдельном блоке памяти.
Аппетит приходит во время еды. Вполне вероятна ситуация, в которой надо управлять временем жизни некоторого разделяемого хендла, получить который от системы было довольно затратно. Разумно расширить наш велосипед и на эту область, а не делать новый. Далее, вероятна ситуация, когда вызов метода какого-нибудь объекта в скопе приведет к тому, что защищаемый хендл сохранится внутрях этого объекта. Это ошибка человека, который проектировал объект, но ошибка достаточно трудноуловимая (кто его знает , когда он этот хендл дернет) и, повторюсь, вероятная. Использование shared_ptr позволит такие ошибки словить достаточно просто. ТКС> делать ее лучш не из shared_ptr, a из intrusive_ptr — меньшее количество раз память выделять придется.
Тут даже соглашусь, с маленькой оговоркой -- "Евреи, не жалейте заварки".
Здравствуйте, denisko, Вы писали:
ТКС>>Не поверю. D>Написать свой делетер -- это не каноническое использование?
Использовать умный указатель для управления временем жизни сущностей, не имеющих семантики указателя — не каноническое использование.
ТКС>>В случае с хэндлом исключена ситуация с циклическими ссылками, поэтому не надо предусматривать возможность реализации weak_ptr, поэтому нет ТКС>необходимости держать shared_count в отдельном блоке памяти. D>Аппетит приходит во время еды. Вполне вероятна ситуация, в которой надо управлять временем жизни некоторого разделяемого хендла, получить который от системы было довольно затратно. Разумно расширить наш велосипед и на эту область, а не делать новый. Далее, вероятна ситуация, когда вызов метода какого-нибудь объекта в скопе приведет к тому, что защищаемый хендл сохранится внутрях этого объекта. Это ошибка человека, который проектировал объект, но ошибка достаточно трудноуловимая (кто его знает , когда он этот хендл дернет) и, повторюсь, вероятная. Использование shared_ptr позволит такие ошибки словить достаточно просто.
Код, иллюстрирующий указанную ситуацию, можете предложить? А я потом объясню, как там обойтись без shared_ptr А то софистикой заниматься лениво.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, denisko, Вы писали:
D>1) Семантически любой Хендл является указателем с флагами.Так что совесть зверский каст оправдает.
Необязательно. А если это куки какой-то сессии?
Куки обычно бывают
— указателями
— числами
— UIDами
— строками
В случае GUID сложно кастить его к указателю и обратно — разве что это будет честный указатель на new GUID. Впрочем, "евреи, не жалейте заварки".
D>2) Можно принимать указатель на хендл, который валяется рядом стеке. Вероятность того, что он запорется, а содержимое shared/scoped ptr останется в целости и сохранности стремительно стремится.
Это очевидная ситуация scope guard, так что scoped_ptr / unique_ptr наше всё.
Использование shared_ptr в данном случае избыточно и ошибкоопасно.
D>3) Автор, судя по стилю, не сильно склонен заморачиваться с спецификацией исключений и explicitamи. А это вторые и третьи грабли после тех, которые укзазал rg45. Если они начнут срабатывать когда кодом будет пользоваться не только он один, его будут бить. Возможно, даже ногами.
Но если заморочится, то это и будет та самая необходимая полировка.
Здравствуйте, denisko, Вы писали:
D>3) Автор, судя по стилю, не сильно склонен заморачиваться с спецификацией исключений и explicitamи. А это вторые и третьи грабли после тех, которые укзазал rg45. Если они начнут срабатывать когда кодом будет пользоваться не только он один, его будут бить. Возможно, даже ногами.
Хм, а зачем там спецификация исключений и explicit?!
Исключения не предполагаются; "throw()" скорее штука вредная; explicit имеет смысл для конструктора с одним аргументом.
Разве что запретить копирование стоит...
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, rg45, Вы писали:
R>А из существующих, пожалуй, наиболее известными являются boost::scoped_ptr (монопольное владение) и boost::shared_ptr (совместное владение).
Эти-то понятное дело. Но мой взгляд это форменное извращение — пихать в указатель, пусть и умный, объект указателем не являющийся, да еще и с дополнительной обкладкой в виде user-defined делетера. Я предполагал, что просто существует аналог моего шаблона, только отполированный (я, например, как ты справедливо заметил, не прикрутил запрет копирования).
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, jazzer, Вы писали:
B>>А вопрос такой. Наверняка эт не я один умный , и такие шаблоны уже существует. Линканите плз какой-нибудь более-менее известный и стандартный. (Без заточки под винду, как у меня, да и вообще хочется посмотреть на "тру" RAII-шаблон )
J>Boost.ScopeExit J>http://www.boost.org/libs/scope_exit/doc/html/index.html
Дык я, собственно, как на ScopeExit посмотрел, так сразу и понял что надо делать свое
ScopeExit некрасив, использует макросы и генерит скрытый код. А мне нужно тупо вызвать CloseHandle() по окончанию блока, и все. Я и сделал, фактически, тот же ScopeExit, но для _одной_ функции и потому куда как более простой. Однако мой шаблон заточен именно под виндовый CloseHandle(). В частности, в нем даже прописано что функция возвращает int (т.е. виндовый BOOL), т.к. именно это CloseHandle() и возвращает. Т.е. как минимум для универсальности надо добавить в параметры шаблона возвращаемое значение, как-то избавиться от __stdcall (или не надо?), запретить копирование.
Мне казалось, что кто-нибудь это уже проделал...
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Basil2, Вы писали:
B>Я предполагал, что просто существует аналог моего шаблона, только отполированный (я, например, как ты справедливо заметил, не прикрутил запрет копирования).
Существуют, конечно, и даже в public domain, на RSDN порыться если... Каждый core programmer просто обязан написать такую штуку.
(Только мне, если честно, сейчас не до грибов — искать. Помню, что уже не единожды было. Да и сам писал, с разной степенью кудрявости).