Ленивый ScopeGuard (не путать с голубцами!)
От: MaximE Великобритания  
Дата: 21.09.03 00:11
Оценка: 199 (20)
Думаю, многие знакомы с 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;
}

////////////////////////////////////////////////////////////////////////////////////////////////
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.