利用已有的bind构造ScopeExit (二)

2014-11-24 12:03:42 · 作者: · 浏览: 6
if (!bOK)
{
CloseHandle(hFile);
}
}
BOOST_SCOPE_EXIT_END

CString strData = _T("test");
DWORD dwToWrite = strData.GetLength() * sizeof(TCHAR);
DWORD dwWritten = 0;

if (!WriteFile(hFile, (LPCTSTR)strData, dwToWrite, &dwWritten, NULL) || dwWritten != dwToWrite)
{
return false;
}

// if (...)
// {
// return false;
// }
//
// ...
//

bOK = true;

return true;
}
注意,此处捕获 bOK 的时候才用指针的形式,以保证最后对此变量的修改能影响到 scope exit 内部。
好,预备知识简要介绍到这里。下面我们进入正题,实现一个类似的东西。
为啥要放在现在说这个事情呢?因为前几天刚弄了个 Bind,使用 Bind 实现 Scope Exit 比较简单。从 boost 和 Loki 的两个方案来看,个人更喜欢 Loki 的,boost 玩语法玩得太厉害。通常我们这种需求都是一两行代码(比如上面例子中的 CloseHandle,DeleteFile之类的),而 boost 方案是用来填写一大段代码的,一两行代码的情形下用起来不方便,重复的框架性代码就占了四行,有效功能才占一行,性价比太低了。
Loki 的实现中,有对于多个参数的处理,搞出了 ScopeGuardImpl0、ScopeGuardImpl1、ScopeGuardImpl2、…… 这在 Loki 中没有类似 boost::bind 的设施的情况下是必须的。这里插播一句,Loki 的 Bind 相当的不好用,只能绑定第一个,然后再绑定第一个,…,从而才能完成对所有参数的绑定。boost::bind 灵活多了。有了类似 boost::bind 的设施后,我们利用它山寨一个 Loki::ScopeGuard。当然,我现在是在写 xl::ScopeExit,当然会用 xl::Bind 去实现。本文暂不限定是 xl::Bind 还是 boost::bind。设个预编译开关吧:
#define USING_BOOST_BIND

#ifdef USING_BOOST_BIND
#define BOOST_BIND_ENABLE_STDCALL
#include
#define SCOPE_EXIT_BIND ::boost::bind
#else
#include
#define SCOPE_EXIT_BIND ::xl::Bind
#endif
高亮的那句是为了打开 boost::bind 对 __stdcall 调用约定的支持。然后抄 Loki 的 ScopeGuardImplBase:
class ScopeGuardImplBase
{
public:
ScopeGuardImplBase() : m_bDismissed(false)
{

}

ScopeGuardImplBase(ScopeGuardImplBase &that) :
m_bDismissed(that.m_bDismissed)
{
that.Dismiss();
}

~ScopeGuardImplBase()
{

}

protected:
template
static void StaticExecute(J &j)
{
if (!j.m_bDismissed)
{
j.Execute();
}
}

public:
void Dismiss() const
{
m_bDismissed = true;
}

private:
mutable bool m_bDismissed;
};

typedef const ScopeGuardImplBase& ScopeGuard;

Loki 在 j.Execute 中有 try … catch …,个人认为不该有(觉得 Loki::ScopeGuard 似乎不该在它自己里面 try … catch …
ScopeGuard 只是帮我们调用一个函数而已,至于这个函数是否有异常出来,它不该悄悄地把它吞了,而应该还我们本来面目,不知道是不是?可是为什么几乎所有介绍 ScopeGuard 的文章都说这 try … catch … 用得好呢?

),所以去掉。
然后对于 ScopeGuardImpl,我们抛开 0、1、2,使用一个超级简洁的实现:
template
class ScopeGuardImpl : public ScopeGuardImplBase
{
public:
ScopeGuardImpl(F fn) :
ScopeGuardImplBase(), m_fn(fn)
{

}

~ScopeGuardImpl()
{
StaticExecute(*this);
}

void Execute()
{
m_fn();
}

private:
F m_fn;
};
我们只需要一个参数,一个兼容函数签名“void ()”的可执行对象 F fn。我们保存这个 fn 直到析构的时候去执行它。
已经差不多了,为了使用简便,再抄一个 MakeGuard:
template
inline ScopeGuardImpl MakeGuard(F f)
{
return ScopeGuardImpl(f);
}

最后,定义一个可供匿名使用的宏 XL_ON_BLOCK_EXIT:
#define XL_CONN_(s, t) s##t
#define XL_CONN(s, t) XL_CONN_(s, t)
#define XL_ON_BLOCK_EXIT(...) ScopeGuard XL_CONN(sg, __LINE__) = MakeGuard(SCOPE_EXIT_BIND(__VA_ARGS__))
同 Loki 一样,我们使用行号作为“匿名”变量的命名。注意 MakeGuard 后的参数里,需要填写如整个 bind,这在具名使用的时候书写上会麻烦一点点。
好了,已实现完毕,我们再一次用前面的例子来使用它:
bool GenFile()
{
LPCTSTR FILE_NAME = _T("Test.txt");
HANDLE hFile = CreateFile(FILE_NAME, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

if (hFile == INVALID_HANDLE_VALUE)
{
return false;
}

ScopeGuard sgDeleteFile = MakeGuard(SCOPE_EXIT_BIND(DeleteFile, FILE_NAME));
XL_ON_BLOCK_EXIT(CloseHandle, hFile);

CString strData = _T("test");
DWORD dwToWrite