Мне очень понравилась его идея, но только он не привел исходников — видимо, по его мнению, любой желающий может сам реализовать эту кучу байндеров.
Последнее время мне много приходится работать с legacy кодом, поэтому проблема стала для меня крайне острой.
Будучи ленивым по натуре, я хотел использовать что-либо готовое. Но готового ScopeGuard я не мог найти.
Решение — паразитировать на boost::bind. Преимущество данного решения в том, что не нужно реализовывать кучу байндеров вручную, причем для разных calling conventions, — все уже давно реализовано в boost::bind.
////////////////////////////////////////////////////////////////////////////////////////////////
// scope_guard.hpp#pragma once
#include <boost/bind.hpp>
////////////////////////////////////////////////////////////////////////////////////////////////namespace util
{
////////////////////////////////////////////////////////////////////////////////////////////////namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////class scope_guard_base
{
public:
void commit() const
{
do_rollback_ = false;
}
protected:
scope_guard_base()
: do_rollback_(true)
{}
mutable bool do_rollback_;
};
////////////////////////////////////////////////////////////////////////////////////////////////template<class R, class F, class A>
class scope_guard_impl : public scope_guard_base
{
private:
typedef boost::_bi::bind_t<R, F, A> binder;
public:
explicit scope_guard_impl(const binder& b)
: rollback_(b)
{}
~scope_guard_impl()
{
if(do_rollback_)
rollback_();
}
private:
binder rollback_;
};
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace detail
////////////////////////////////////////////////////////////////////////////////////////////////typedef const detail::scope_guard_base& scope_guard;
template<class R, class F, class A>
inline detail::scope_guard_impl<R, F, A> make_guard(const boost::_bi::bind_t<R, F, A>& b)
{
return detail::scope_guard_impl<R, F, A>(b);
}
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace util
////////////////////////////////////////////////////////////////////////////////////////////////
Пример использования:
////////////////////////////////////////////////////////////////////////////////////////////////
// test.cpp#include <windows.h>
#include <boost/ref.hpp>
#define BOOST_BIND_ENABLE_STDCALL
#include"scope_guard.hpp"////////////////////////////////////////////////////////////////////////////////////////////////using util::scope_guard;
using util::make_guard;
////////////////////////////////////////////////////////////////////////////////////////////////void test_1()
{
void* p = 0;
// передадим ссылку на p, отложим вызов free(p)
scope_guard g(make_guard(boost::bind(&free, boost::ref(p))));
p = malloc(0x10);
// здесь произойдет ~g === free(p)
}
////////////////////////////////////////////////////////////////////////////////////////////////void test_2()
{
void *p = 0;
p = malloc(0x10); // (1)
// передадим текущее значение p, отложим вызов free(p)
scope_guard g1(make_guard(boost::bind(&free, p)));
p = malloc(0x10); // (2)
// передадим текущее значение p, отложим вызов free(p)
scope_guard g2(make_guard(boost::bind(&free, p)));
p = malloc(0x10); // (3)
// передадим текущее значение p, отложим вызов free(p)
scope_guard g3(make_guard(boost::bind(&free, p)));
// здесь произойдет ~g3 === free(p), p == (3)
// здесь произойдет ~g2 === free(p), p == (2)
// здесь произойдет ~g1 === free(p), p == (1)
}
////////////////////////////////////////////////////////////////////////////////////////////////void test_3()
{
::CoInitialize(0);
// отложим вызов ::CoUninitialize()
scope_guard g1(make_guard(boost::bind(&::CoUninitialize)));
// здесь произойдет ~g1 === ::CoUninitialize()
}
////////////////////////////////////////////////////////////////////////////////////////////////void test_4()
{
void* p = ::HeapAlloc(::GetProcessHeap(), 0, 0x10);
// отложим вызов ::HeapFree(::GetProcessHeap(), 0, p)
scope_guard g1(make_guard(boost::bind(&::HeapFree, ::GetProcessHeap(), 0, p))); // (1)
// здесь произойдет ~g1 === ::HeapFree(::GetProcessHeap(), p), ::GetProcessHeap() == (1), p == (1)
}
////////////////////////////////////////////////////////////////////////////////////////////////int main()
{
test_1();
test_2();
test_3();
test_4();
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////
Вах ! Однако меня несколько коробит повторяющаяся последовательность "make_guard(boost::bind(" и почти всегда совершенно ненужные переменные g1,g2. А вот как бы добавить макросов, чтобы можно было писать
Однако боюсь, что иного варианта кроме написания make_guard_ex для количества аргументов [1...N] нет... Зато пользователи (и кода и гуарда) будут довольны !
Здравствуйте, MaximE, Вы писали:
ME>Решение — паразитировать на boost::bind. Преимущество данного решения в том, что не нужно реализовывать кучу байндеров вручную, причем для разных calling conventions, — все уже давно реализовано в boost::bind.
Вумное и вполне логичное решение — мои аплодисменты.
Вот только есть вопросец: а зачем делать так ?
template<class R, class F, class A>
class scope_guard_impl : public scope_guard_base
{
private:
typedef boost::_bi::bind_t<R, F, A> binder;//<-- вот так?
Не проще ли сделать вот так?
template<class F>
class scope_guard_impl : public scope_guard_base
{
private:
typedef F binder;//<-- вот так?
Ведь все что требуется от типа binder, это чтобы он поддерживал выражение " rollback_() "; То есть, чтобы он был функтором без параметров. Лично я не никакого вижу смысла ограничивать выбор функторов только функторами базирующимися на шаблоне boost::_bi::bind_t .
Функция make_guard в этом случае будет выглядеть так:
Так это будет работать с любыми байндерами и любыми функторами которые не требуют параметров.
И, самое главное, никаких зависимостей от boost или еще какой библиотеки
Что же касается неудобства написания выражения scope_guard g = make_guard(boost::bind( ............. )), то эта проблема никуда не делась
Вариантов ее решения вижу два:
— Или макросы, как предложено Александреску (но имхо это коряво, да и клэшаться они любят, падлы)
— Или писать туеву хучу оверлоадов для make_guard() как это сделано для функции boost::bind() (см. файлы boost/bind/bind_cc.hpp и boost/bind/bind_mf_cc.hpp)
Имхо, легче лишний раз написать "make_guard(boost::bind...."
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Создатели boost/bind поместили определения шаблонов bind() в отдельные файлы и реюзают их для различных calling conventions с помощью препроцессора. Благодаря этому мы можем сделать следующий идеологически верный шаг в ленивой реализации scope_guard.
Грубо говоря, делаем что-то вроде #define boost::bind make_guard, включаем файлы с определением bind, и затем #undef boost::bind. Таким вместо того чтобы писать так
scope_guard g = make_guard( boost::bind( f, arg ) );
теперь можно писать так
scope_guard g = make_guard( f, arg );
Реализованы make_guard-байндеры для свободных функций и для функций членов, константных и неконстантных. Поддержка calling conventions такая же как в boost::bind. Make_guard для функциональных объектов не реализован, ну да не больно оно надо для скопгардов.
С правовой точки зрения с таким реюзом бустовских файлов все чисто. Плохо то, что мы используем файлы только-для-внутреннего-пользования-бустом, которые вобщем-то могут быть изменены в следующей версии буста.
Вот обновленный исходник scope_guard.h:
////////////////////////////////////////////////////////////////////////////////////////////////
// scope_guard.hpp#pragma once
#include <boost/bind.hpp>
////////////////////////////////////////////////////////////////////////////////////////////////namespace util {
////////////////////////////////////////////////////////////////////////////////////////////////namespace detail {
////////////////////////////////////////////////////////////////////////////////////////////////class scope_guard_base
{
public:
void dismiss() const
{
do_rollback_ = false;
}
protected:
scope_guard_base(const scope_guard_base& other)
: do_rollback_(other.do_rollback_)
{
other.dismiss();
}
scope_guard_base()
: do_rollback_(true)
{}
mutable bool do_rollback_;
};
////////////////////////////////////////////////////////////////////////////////////////////////template<class R, class F, class A>
class scope_guard_impl : public scope_guard_base
{
public:
scope_guard_impl(F f, A const& a)
: rollback_(f, a)
{}
~scope_guard_impl()
{
if(do_rollback_)
rollback_();
}
private:
boost::_bi::bind_t<R,F,A> rollback_;
};
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace detail
////////////////////////////////////////////////////////////////////////////////////////////////typedef detail::scope_guard_base const& scope_guard;
////////////////////////////////////////////////////////////////////////////////////////////////
// create make_guard() definitions by preprocessing boost::bind() sourcesnamespace detail {
using boost::_bi::list0;
using boost::_bi::list_av_1;
using boost::_bi::list_av_2;
using boost::_bi::list_av_3;
using boost::_bi::list_av_4;
using boost::_bi::list_av_5;
using boost::_bi::list_av_6;
using boost::_bi::list_av_7;
using boost::_bi::list_av_8;
using boost::_bi::list_av_9;
} // namespace detail#undef BOOST_BIND // it seems like boosters forgot about it#define BOOST_BIND make_guard
#define _bi detail
#define bind_t scope_guard_impl
#define _mfi boost::_mfi
////////////////////////////////////////////////////////////////////////////////////////////////
// quotation from boost/bind.hpp
// Copyright (c) 2001, 2002 Peter Dimov and Multi Media Ltd.
// Copyright (c) 2001 David Abrahams
// function pointers#define BOOST_BIND_CC
#define BOOST_BIND_ST
#include <boost/bind/bind_cc.hpp>
#undef BOOST_BIND_CC
#undef BOOST_BIND_ST
#ifdef BOOST_BIND_ENABLE_STDCALL
#define BOOST_BIND_CC __stdcall
#define BOOST_BIND_ST
#include <boost/bind/bind_cc.hpp>
#undef BOOST_BIND_CC
#undef BOOST_BIND_ST
#endif
#ifdef BOOST_BIND_ENABLE_FASTCALL
#define BOOST_BIND_CC __fastcall
#define BOOST_BIND_ST
#include <boost/bind/bind_cc.hpp>
#undef BOOST_BIND_CC
#undef BOOST_BIND_ST
#endif
#ifdef BOOST_BIND_ENABLE_PASCAL
#define BOOST_BIND_ST pascal
#define BOOST_BIND_CC
#include <boost/bind/bind_cc.hpp>
#undef BOOST_BIND_ST
#undef BOOST_BIND_CC
#endif// member function pointers#define BOOST_BIND_MF_NAME(X) X
#define BOOST_BIND_MF_CC
#include <boost/bind/bind_mf_cc.hpp>
#undef BOOST_BIND_MF_NAME
#undef BOOST_BIND_MF_CC
#ifdef BOOST_MEM_FN_ENABLE_STDCALL
#define BOOST_BIND_MF_NAME(X) X##_stdcall
#define BOOST_BIND_MF_CC __stdcall
#include <boost/bind/bind_mf_cc.hpp>
#undef BOOST_BIND_MF_NAME
#undef BOOST_BIND_MF_CC
#endif
#ifdef BOOST_MEM_FN_ENABLE_FASTCALL
#define BOOST_BIND_MF_NAME(X) X##_fastcall
#define BOOST_BIND_MF_CC __fastcall
#include <boost/bind/bind_mf_cc.hpp>
#undef BOOST_BIND_MF_NAME
#undef BOOST_BIND_MF_CC
#endif// end quotation boost/bind.hpp ///////////////////////////////////////////////////////////////#undef BOOST_BIND
#undef _bi
#undef bind_t
#undef _mfi
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace util
////////////////////////////////////////////////////////////////////////////////////////////////
ЗЫ И кто там говорил, что в бустовские заголовки в самом бусте включаются только так #include "some", а не так #include <some> ?
Здравствуйте, MaximE, Вы писали:
А>>Вах ! Однако меня несколько коробит повторяющаяся последовательность "make_guard(boost::bind(" ...
ME>Да, меня это тоже несколько подплющивает
Господа, о чем это вы?! А как же приведенный в той статье и в исходника макрос ON_EXIT???
А>>... и почти всегда совершенно ненужные переменные g1,g2. А вот как бы добавить макросов, чтобы можно было писать
ME>А вот про ненужные переменные я даже не задумывался.
В вышеупомянутом макросе и решается проблема создания "безымянных" переменных...
Re[10]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, MaximE, Вы писали:
ME>Да, можно избавиться от одной пары скобок, применив copy initialization (заменить scoped_guard s(make_guard(...)), на scoped_guard s = make_guard(...)), но это пробемы не решает, т.к. у нас два вызова функций ( make_guard(bind(...)) ) и, соответственно, нам нужно закрыть две скобки.
Здравствуйте, MaximE, Вы писали:
ME>Думаю, многие знакомы с ScopeGuard.
ME>Мне очень понравилась его идея, но только он не привел исходников — видимо, по его мнению, любой желающий может сам реализовать эту кучу байндеров.
ME>Последнее время мне много приходится работать с legacy кодом, поэтому проблема стала для меня крайне острой.
ME>Будучи ленивым по натуре, я хотел использовать что-либо готовое. Но готового ScopeGuard я не мог найти.
ME>Решение — паразитировать на boost::bind. Преимущество данного решения в том, что не нужно реализовывать кучу байндеров вручную, причем для разных calling conventions, — все уже давно реализовано в boost::bind.
Здравствуйте, Дмитрий Наумов, Вы писали:
ДН>Здравствуйте, MaximE, Вы писали:
ME>>Не знаю ME>>Меня без макросов вполне устраивает.
ДН>Позвольте угадаю сей до боли знакомый (по себе самому) эффект — так понравилась статья, что до конца не дочитал — бросился использовать! А оказалось, что там какие то еще макросы приведены, да и то, только в приложенном коде... Так?
Оригинальную статью я читал давно, и помнил, что там упоминались макросы, но до последнего времени я не использовал scope_guard вообще — не было необходимости. Понадобилось — я сразу подумал про boost::bind — всего делов-то засунуть вызов в деструктор.
Здравствуйте, MaximE, Вы писали:
ME>Думаю, многие знакомы с ScopeGuard.
ME>Мне очень понравилась его идея, но только он не привел исходников — видимо, по его мнению, любой желающий может сам реализовать эту кучу байндеров.
Здравствуйте, alexkro, Вы писали:
A>Здравствуйте, MaximE, Вы писали:
ME>>Думаю, многие знакомы с ScopeGuard.
ME>>Мне очень понравилась его идея, но только он не привел исходников — видимо, по его мнению, любой желающий может сам реализовать эту кучу байндеров.
A>Почему не привел? В этом архиве они (ftp://ftp.cuj.com/pub/2000/cujdec2000.zip).
Посыпаю голову пеплом — не мог найти
Но все равно с boost::bind как-то милее сердцу
Re[2]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, MaximE, Вы писали:
<...>>
А>Вах ! Однако меня несколько коробит повторяющаяся последовательность "make_guard(boost::bind(" ...
Да, меня это тоже несколько подплющивает
А>... и почти всегда совершенно ненужные переменные g1,g2. А вот как бы добавить макросов, чтобы можно было писать
А вот про ненужные переменные я даже не задумывался.
[]
А>Однако боюсь, что иного варианта кроме написания make_guard_ex для количества аргументов [1...N] нет... Зато пользователи (и кода и гуарда) будут довольны !
Отличная идея.
Re[4]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, Дмитрий Наумов, Вы писали:
ДН>Здравствуйте, MaximE, Вы писали:
А>>>Вах ! Однако меня несколько коробит повторяющаяся последовательность "make_guard(boost::bind(" ...
ME>>Да, меня это тоже несколько подплющивает
ДН>Господа, о чем это вы?! А как же приведенный в той статье и в исходника макрос ON_EXIT???
А>>>... и почти всегда совершенно ненужные переменные g1,g2. А вот как бы добавить макросов, чтобы можно было писать
ME>>А вот про ненужные переменные я даже не задумывался.
ДН>В вышеупомянутом макросе и решается проблема создания "безымянных" переменных...
Здравствуйте, Дмитрий Наумов, Вы писали:
ДН>Здравствуйте, MaximE, Вы писали:
А>>>Вах ! Однако меня несколько коробит повторяющаяся последовательность "make_guard(boost::bind(" ...
ME>>Да, меня это тоже несколько подплющивает
ДН>Господа, о чем это вы?! А как же приведенный в той статье и в исходника макрос ON_EXIT???
Здесь нам придется завернуть два вызова функций, поэтому если далать макрос как в оригинальной статье, то его использование будеть выглядеть примерно так:
ON_BLOCK_EXIT(::CoUninitialize)); // <--- закрыть две скобки
Не очень элегантно, да?
Re[5]: Ленивый ScopeGuard (не путать с голубцами!)
и тогда, насколько я понимаю открывающая скобка перед util:make_guard не нужна... Возможно, правда, это все связано с BOOST_PP_CAT — я, к сожалению, не сильный знаток буста, так что звиняйте если не прав.
Re[9]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, Дмитрий Наумов, Вы писали:
ДН>Здравствуйте, MaximE, Вы писали:
ME>>Здравствуйте, MaximE, Вы писали:
ME>>>Если мы напишем подобный макрос:
ДН>Ну или даже если нам очень захотелось по своему переписать: ДН>
ДН>и тогда, насколько я понимаю открывающая скобка перед util:make_guard не нужна...
Да, можно избавиться от одной пары скобок, применив copy initialization (заменить scoped_guard s(make_guard(...)), на scoped_guard s = make_guard(...)), но это пробемы не решает, т.к. у нас два вызова функций ( make_guard(bind(...)) ) и, соответственно, нам нужно закрыть две скобки.
Re[11]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, Дмитрий Наумов, Вы писали:
ДН>Здравствуйте, MaximE, Вы писали:
ME>>Да, можно избавиться от одной пары скобок, применив copy initialization (заменить scoped_guard s(make_guard(...)), на scoped_guard s = make_guard(...)), но это пробемы не решает, т.к. у нас два вызова функций ( make_guard(bind(...)) ) и, соответственно, нам нужно закрыть две скобки.
ДН>Но и открыть две, разве не так? ДН>
ДН>ON_BLOCK_EXIT( bind(...) );
ДН>
ДН>Имхо, выглядит очень даже обычно и прилично...
Если не пытаться загнать bind внутрь макроса, то да
Re[12]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, Дмитрий Наумов, Вы писали:
ДН>Здравствуйте, MaximE, Вы писали:
ME>>Если не пытаться загнать bind внутрь макроса, то да
ДН>К такой ошеломляющей "наглости" я не был готов... Bind...внутри макроса...Победа нокаутом ДН> ДН>А если не секрет — неужели так надо его именно внутрь запихнуть?
Не знаю
Меня без макросов вполне устраивает.
Re[14]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, MaximE, Вы писали:
ME>Не знаю ME>Меня без макросов вполне устраивает.
Позвольте угадаю сей до боли знакомый (по себе самому) эффект — так понравилась статья, что до конца не дочитал — бросился использовать! А оказалось, что там какие то еще макросы приведены, да и то, только в приложенном коде... Так?
Re[2]: Ленивый ScopeGuard (не путать с голубцами!)
TMH>template<class F>
TMH>class scope_guard_impl : public scope_guard_base
TMH>{
TMH>private:
TMH>typedef F binder;//<-- вот так?
TMH>
TMH>Ведь все что требуется от типа binder, это чтобы он поддерживал выражение " rollback_() "; То есть, чтобы он был функтором без параметров. Лично я не никакого вижу смысла ограничивать выбор функторов только функторами базирующимися на шаблоне boost::_bi::bind_t .
Да, точно. Просто меня точила мысль именно с boost::bind, а про другие функторы я и не думал
TMH>Функция make_guard в этом случае будет выглядеть так:
TMH>
TMH>Так это будет работать с любыми байндерами и любыми функторами которые не требуют параметров. TMH>И, самое главное, никаких зависимостей от boost или еще какой библиотеки
Отлично!
TMH>Что же касается неудобства написания выражения scope_guard g = make_guard(boost::bind( ............. )), то эта проблема никуда не делась
TMH>Вариантов ее решения вижу два: TMH>- Или макросы, как предложено Александреску (но имхо это коряво, да и клэшаться они любят, падлы) TMH>- Или писать туеву хучу оверлоадов для make_guard() как это сделано для функции boost::bind() (см. файлы boost/bind/bind_cc.hpp и boost/bind/bind_mf_cc.hpp)
TMH>Имхо, легче лишний раз написать "make_guard(boost::bind...."
Я пришел к тому же выводу .
Re[2]: Ленивый ScopeGuard (не путать с голубцами!)
Здравствуйте, TepMuHyc, Вы писали: TMH>Что же касается неудобства написания выражения scope_guard g = make_guard(boost::bind( ............. )), то эта проблема никуда не делась
TMH>Вариантов ее решения вижу два: TMH>- Или макросы, как предложено Александреску (но имхо это коряво, да и клэшаться они любят, падлы) TMH>- Или писать туеву хучу оверлоадов для make_guard() как это сделано для функции boost::bind() (см. файлы boost/bind/bind_cc.hpp и boost/bind/bind_mf_cc.hpp)
TMH>Имхо, легче лишний раз написать "make_guard(boost::bind...."
Есть третий вариант с использованием auto_ptr или boost::function но оно слишком тяжеловесно. В принципе, там где есть alloca() можно наверно как-нибудь извратится и обойтись без динамического выделения памяти, но ...
Здравствуйте, MaximE, Вы писали:
ME>Думаю, многие знакомы с ScopeGuard.
ME>Мне очень понравилась его идея, но только он не привел исходников — видимо, по его мнению, любой желающий может сам реализовать эту кучу байндеров.
ME>Последнее время мне много приходится работать с legacy кодом, поэтому проблема стала для меня крайне острой.
ME>Будучи ленивым по натуре, я хотел использовать что-либо готовое. Но готового ScopeGuard я не мог найти.
ME>Решение — паразитировать на boost::bind. Преимущество данного решения в том, что не нужно реализовывать кучу байндеров вручную, причем для разных calling conventions, — все уже давно реализовано в boost::bind.
<cool code skipped>
А еще можно попробовать присосаться заодно и к wrapper'у, предложенному Строуструпом, для обертки функций пре- и постдействиями.
Тогда вообще мощный механизм получится... Надо пообмозговать...
I took the ScopeGuard implementation by Andrei Alexandrescu and
reimplemented it using Boost.Function and Boost.Bind. I was able to
implement it with a single class and one helper function per supported
signature. It is really useful. If anyone is interested, I can send it to
them or post it to the list.
Необходимо обязательно добавить конструктор копирования с передачей владения, иначе после копирования деструкторы копий и оригиналов гардов выполнят roll_back_ более одного раза.