Думаю, многие знакомы с
ScopeGuard.
Мне очень понравилась его идея, но только он не привел исходников — видимо, по его мнению, любой желающий может сам реализовать эту кучу байндеров.
Последнее время мне много приходится работать с 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;
}
////////////////////////////////////////////////////////////////////////////////////////////////