Посмотрел я на
МаксимовЕАвтор: MaximE
Дата: 21.09.03
и Александресков scope_guard и подумал, что неплохо бы иметь в нем 2 метода:
— dismiss(), который отменяет вызов функтора (впрочем, это есть в вышеназванных реализациях)
— commit(), который вызывает функтор еще до того как дело дойдет до деструктора.
Придумалось вот такое решение:
class scope_guard_base {
scope_guard_base& operator=(const scope_guard_base&);
protected:
typedef void (*commit_fn)(const scope_guard_base*);
mutable commit_fn m_commit; //функция коммита - устанавливается потомком
scope_guard_base(commit_fn f)
:m_commit(f)
{}
scope_guard_base(const scope_guard_base& rhs)
:m_commit(rhs.m_commit)
{ rhs.m_commit = 0; }
public:
~scope_guard_base()
{
try{
if( m_commit )
m_commit( this );
}catch(...){
// некузяво бросаться исключениями из деструктора...
}
}
void dismiss() const
{ m_commit = 0; }
void commit() const
{
if( !m_commit ) return;
commit_fn fn = m_commit;
m_commit = 0;//коммитимся прямо сейчас - деструктору делать нечего.
fn( this );
}
};
template<class F> class scope_guard_impl
:public scope_guard_base
{
static void do_commit(const scope_guard_base* g);
F m_f;
public:
scope_guard_impl(const F& f)
:scope_guard_base(do_commit)
,m_f(f)
{}
};
template<class F>
void scope_guard_impl<F>::do_commit(const scope_guard_base* g)
{
((scope_guard*)g)->m_f();
}
//все остальное как у MaximE и Александреску
Итак, плюсы:
— появился метод commit()
А теперь минусы:
— функция scope_guard_impl::do_commit() никак не может стать inline (компилятор не даст) потому что на нее имеется указатель. В принципе, это не так уж и страшно (особенно, если ее сделать __fastcall) — кода она негенерит считанные байты — но неприятный осадок все равно остается