2007年7月12日 星期四

自己做個 ScopeGuard

試了 Loki 的 ScopeGuard 後,我覺得可以用 boost 來做到同樣的功能,就自己練習看看。我覺得 Loki 版的有兩點不太好用:

1. 針對 function 與 member function 需要用不同的呼叫(MakeGuard 與 MakeObjectGuard ).
2. 目前它使用的 functor 只接受到三個參數,不夠方便.

自己用 boost 試做,後來發現可以做的很簡單,不需要搞 ScopeGuard 繼承體系,也不需要用 MakeGuard 與 MakeObjectGuard 這類的 template function 來包裝。
原始碼如下:



class MyScopeGuard : private boost::noncopyable
{
mutable bool bDismissed;
boost::function func;

public:
MyScopeGuard(function f) : bDismissed(false), func(f){}
void Dismiss () const throw() { bDismissed = true; }
~MyScopeGuard() throw()
{
if ( !bDismissed && !func.empty() )
func();
}
};

使用如下:

void Decrement(int& i) { --i; }
void foo::bar()
{
// ......
++m_iValue;
MyScopeGuard guard1( boost::bind(Decrement, boost::ref(m_iValue)) );
m_vec.push_back(i);
MyScopeGuard guard2( boost::bind(&vector::pop_back, &m_vec) );
// ...... may throw

guard1.Dismiss();
guard2.Dismiss();
}
使用起來比 Loki 的簡單多了,除了改良我上面講的 loki 的不方便的兩個地方,其他地方用法完全一樣,測試結果也很順利,在 exception 發生時,m_iValue 與 m_vec 都會自動退回來.


那麼效率呢? 以我的電腦 ( P4 2.8G Hyper threading) 測的結果如下:

1. 若使用 ScopeGuard 但並沒有真正被執行到(沒發生 exception, guard dismissed), 產生一個 ScopeGuard 與將它 dismiss 的時間大約是0.08ms.
2. 若使用 ScopeGuard 並且讓它真的執行(不 dismiss),所花費的時間仍然是大約0.08ms.
3. 不使用 ScopeGuard,僅測試一次 throw 並 catch 所花費的時間,大約是75ms.

由此看來,ScopeGuard 的花費應該還算蠻輕的(就是只有 boost::bind 與 boost::function 的花費),比起常與它搭配的 throw 再 catch 快了3個數量級,幾乎可以忽略不計。


註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2006/09/04 17:42。

沒有留言: