Чисто чтобы не забывать закрывать виндовые хендлы, набросал примитивный шаблон:
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 просто обязан написать такую штуку.
(Только мне, если честно, сейчас не до грибов — искать. Помню, что уже не единожды было. Да и сам писал, с разной степенью кудрявости).
Здравствуйте, Basil2, Вы писали:
B>Здравствуйте, jazzer, Вы писали:
B>>>А вопрос такой. Наверняка эт не я один умный , и такие шаблоны уже существует. Линканите плз какой-нибудь более-менее известный и стандартный. (Без заточки под винду, как у меня, да и вообще хочется посмотреть на "тру" RAII-шаблон )
J>>Boost.ScopeExit J>>http://www.boost.org/libs/scope_exit/doc/html/index.html
B>Дык я, собственно, как на ScopeExit посмотрел, так сразу и понял что надо делать свое
B>ScopeExit некрасив, использует макросы и генерит скрытый код. А мне нужно тупо вызвать CloseHandle() по окончанию блока, и все. Я и сделал, фактически, тот же ScopeExit, но для _одной_ функции и потому куда как более простой. Однако мой шаблон заточен именно под виндовый CloseHandle(). В частности, в нем даже прописано что функция возвращает int (т.е. виндовый BOOL), т.к. именно это CloseHandle() и возвращает. Т.е. как минимум для универсальности надо добавить в параметры шаблона возвращаемое значение, как-то избавиться от __stdcall (или не надо?), запретить копирование.
B>Мне казалось, что кто-нибудь это уже проделал...
Здравствуйте, Basil2, Вы писали:
B>Здравствуйте, jazzer, Вы писали:
B>>>А вопрос такой. Наверняка эт не я один умный , и такие шаблоны уже существует. Линканите плз какой-нибудь более-менее известный и стандартный. (Без заточки под винду, как у меня, да и вообще хочется посмотреть на "тру" RAII-шаблон )
J>>Boost.ScopeExit J>>http://www.boost.org/libs/scope_exit/doc/html/index.html
B>Дык я, собственно, как на ScopeExit посмотрел, так сразу и понял что надо делать свое
B>ScopeExit некрасив, использует макросы и генерит скрытый код.
К сожалению, без этого пока что не обойтись из-за ограничений языка, если мы хотим иметь возможность исполнять при выходе произвольный код.
В С++0х будет легче.
B>А мне нужно тупо вызвать CloseHandle() по окончанию блока, и все. Я и сделал, фактически, тот же ScopeExit, но для _одной_ функции и потому куда как более простой.
Пока тебе достаточно просто звать функцию без параметров — да, Boost.ScopeExit будет оверкиллом.
Но как только тебе понадобится нечто посложнее (например, та же функция, но срабатывающая в зависимости от флага — стандартная ситуация) — ты проклянешь все, пытаясь собрать функтор при помощи Boost.Bind/Boost.Lambda. Не говоря уже о читабельности того, что у тебя получится, в сравнении с Boost.ScopeExit.
B>Мне казалось, что кто-нибудь это уже проделал...
shared_ptr, я же дал ссылку на их страничку, посвященную именно этому.
Здравствуйте, denisko, Вы писали:
К>>Лучше написать и отполировать свой велосипед. К>>Тем более, что не так уж там много нужно для полировки. D>Несколько возражений. D>1) Семантически любой Хендл является указателем с флагами.Так что совесть зверский каст оправдает.
Возвращаемый AcquireCredentialsHandle даже по размеру больше указателя.
нет, если shared_ptr использовать, то стоит биндить хендл в удалитель.
void FreeHandle(int h);
int main()
{
int handle = 3;
boost::shared_ptr<void const> p("", boost::bind(FreeHandle, handle));
}
D>2) Можно принимать указатель на хендл, который валяется рядом стеке. Вероятность того, что он запорется, а содержимое shared/scoped ptr останется в целости и сохранности стремительно стремится.
ничего не понял.
D>3) Автор, судя по стилю, не сильно склонен заморачиваться с спецификацией исключений и explicitamи. А это вторые и третьи грабли после тех, которые укзазал rg45. Если они начнут срабатывать когда кодом будет пользоваться не только он один, его будут бить. Возможно, даже ногами.
explicit конечно нужен. а спецификация исключений — нет.
Здравствуйте, jazzer, Вы писали:
J>Пока тебе достаточно просто звать функцию без параметров — да, Boost.ScopeExit будет оверкиллом. J>Но как только тебе понадобится нечто посложнее (например, та же функция, но срабатывающая в зависимости от флага — стандартная ситуация) — ты проклянешь все, пытаясь собрать функтор при помощи Boost.Bind/Boost.Lambda. Не говоря уже о читабельности того, что у тебя получится, в сравнении с Boost.ScopeExit.
Для "посложнее" я просто напишу новый конструктор, который будет принимать ф-цию с двумя параметрами
B>>Мне казалось, что кто-нибудь это уже проделал... J>shared_ptr, я же дал ссылку на их страничку, посвященную именно этому.
Здравствуйте, Basil2, Вы писали:
B>Чисто чтобы не забывать закрывать виндовые хендлы, набросал примитивный шаблон:
B>А вопрос такой. Наверняка эт не я один умный , и такие шаблоны уже существует. Линканите плз какой-нибудь более-менее известный и стандартный. (Без заточки под винду, как у меня, да и вообще хочется посмотреть на "тру" RAII-шаблон )
Здравствуйте, Тот кто сидит в пруду, Вы писали:
ТКС>Код, иллюстрирующий указанную ситуацию, можете предложить? А я потом объясню, как там обойтись без shared_ptr А то софистикой заниматься лениво.
Пожалте.
void someFunction()
{
ResourceGuard someResource;
anotherManFunction(someResource);//вот тут товарищь кешировал у себя значение, хранимое
// в someResource и через некоторое время начинал себя странно вести.
// Предложи код, который бы мог отследить незаконный захват ресурса товарищем,
//если товарищ имеет использовать и клонировать только обертку но не сам ресурс.
}
Здравствуйте, denisko, Вы писали:
D>Здравствуйте, Тот кто сидит в пруду, Вы писали:
ТКС>>Код, иллюстрирующий указанную ситуацию, можете предложить? А я потом объясню, как там обойтись без shared_ptr А то софистикой заниматься лениво. D>Пожалте. D>
D>void someFunction()
D>{
D>ResourceGuard someResource;
D>anotherManFunction(someResource);//вот тут товарищь кешировал у себя значение, хранимое
D> // в someResource и через некоторое время начинал себя странно вести.
D> // Предложи код, который бы мог отследить незаконный захват ресурса товарищем,
D> //если товарищ имеет использовать и клонировать только обертку но не сам ресурс.
D>}
D>
Если товарищ запасает голый хэндл, то shared_ptr с закрывателем хэндла ему все равно не поможет (т.к. защищаемый хэндл все равно закроется при разрушении someResource). Если же он пытается сохранить ResourceGuard, то ситуация просто не попадает под определение "когда вызов метода какого-нибудь объекта в скопе приведет к тому, что защищаемый хендл сохранится внутрях этого объекта". Так что извольте другой пример.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Alexander G, Вы писали:
B>>Зачем там нужен explicit?
AG>Чтобы избежать неявного конструирования из хендла, владение которым не должно передаваться.
Как вы представляете неявное конструирование от конструктора с _двумя_ аргументами??
Или я что-то недопонимаю в С++?..
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.