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

2014-11-24 12:03:42 · 作者: · 浏览: 1

对于 ScopeExit,以前有提到过(见《这种代码结构如何组织?goto or do…while(0)?》http://www.2cto.com/kf/201205/132632.html)。使用场景再简单提一下:
bool GenFile()
{
HANDLE hFile = CreateFile(_T("Test.txt"), GENERIC_WRITE, 0, NUL, CREATE_ALWAYS, 0, NULL);

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

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

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

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

CloseHandle(hFile);

return true;
}

如上面这部分代码,如果 if … 之类的流程持续下去(如注释部分),每个 return false 之前都得带上 CloseHandle(),非常累赘。因此,出现了类似的 ScopeExit。boost 里有一个 BOOST_SCOPE_EXIT,Loki 里面也有一个 ScopeGuard。
继续使用刚才的案例,BOOST_SCOPE_EXIT 用法:
bool GenFile()
{
HANDLE hFile = CreateFile(_T("Test.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

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

BOOST_SCOPE_EXIT((hFile))
{
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;
// }
//
// ...
//

return true;
}

这样,每个 return 之前再也不必背负 CloseHandle 的包袱。
Loki::ScopeGuard 的用法:
bool GenFile()
{
HANDLE hFile = CreateFile(_T("Test.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

if (hFile == INVALID_HANDLE_VALUE)
{

return false;
}

LOKI_ON_BLOCK_EXIT(CloseHandle, hFile);

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;
// }
//
// ...
//

return true;
}

从使用的简洁程度上看,Loki 更胜一筹。
另外,我们经常也遇到有条件的执行清理动作的情形,boost 和 Loki 都支持。先看 Loki 的使用案例:
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;
}

Loki::ScopeGuard sgDeleteFile = Loki::MakeGuard(DeleteFile, FILE_NAME);
LOKI_ON_BLOCK_EXIT(CloseHandle, hFile);

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;
// }
//
// ...
//

sgDeleteFile.Dismiss();

return true;
}

一开始,我们使用具名的 ScopeGuard,绑定了一个 DeleteFile(FILE_NAME) 的操作,到最后通过 Dismiss,让此操作不被执行。
相应地,boost 中,可以在进入 scope exit 之前设定一个变量,将此变量捕获入 scope exit,到最后给这个变量赋值,决定执不执行:
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;
}

bool bOK = false;

BOOST_SCOPE_EXIT((hFile)(&bOK))
{