I have mentioned ScopeExit before (see how to organize this code structure? Goto or do... While (0)? Http://www.bkjia.com/kf/201205/132632.html ). The usage scenario is briefly described as follows:
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;
}
As shown in the above Code, if... And so on (such as the comments). Every return to false must be followed by CloseHandle (), which is very cumbersome. Therefore, ScopeExit is similar. In boost, there is a BOOST_SCOPE_EXIT, And Loki also has a ScopeGuard.
Continue to use the previous case. BOOST_SCOPE_EXIT usage:
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;
}
In this way, the CloseHandle burden is no longer needed before each return.
Loki: ScopeGuard usage:
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;
}
In terms of simplicity, Loki is superior.
In addition, we often encounter conditional cleanup operations, which are supported by both boost and Loki. First look at the use case of 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;
}
At the beginning, we used the named ScopeGuard to bind a DeleteFile (FILE_NAME) operation, and finally passed Dismiss to prevent this operation from being executed.
Correspondingly, in boost, you can set a variable before entering the scope exit, capture the variable into the scope exit, and assign a value to the variable at the end, and decide not to execute the variable:
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 ))
{
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;
}
Note: the pointer is used only when bOK is captured here to ensure that the last modification to this variable affects the internal scope exit.
Good. Here is a brief introduction to the preparatory knowledge. Next we will go to the topic to implement something similar.
Why do we need to talk about this now? Because I just got a Bind a few days ago, it is relatively simple to use Bind to implement Scope Exit. From the two schemes of boost and Loki, I personally prefer Loki, and boost plays a very bad role in syntax. Generally, we need one or two lines of code (for example, CloseHandle and DeleteFile in the above example), while the boost solution is used to fill in a large piece of code, in the case of one or two lines of code, it is inconvenient to use. The repeated framework code occupies four lines, and only one line of valid functions is occupied. The cost-effectiveness is too low.
Loki's implementation involves processing multiple parameters and produces ScopeGuardImpl0, ScopeGuardImpl1, ScopeGuardImpl2 ,...... This is required when Loki does not have a facility similar to boost: bind. Here we can insert a sentence: Loki's Bind is quite difficult to use. You can only Bind the first one, and then Bind the first one ,..., To bind all parameters. Boost: bind is more flexible. With a facility similar to boost: bind, we use it to copy a Loki: ScopeGuard. Of course, I am writing xl: ScopeExit. Of course, xl: Bind will be used for implementation. This article does not limit whether it is xl: Bind or boost: bind. Set a pre-compilation switch:
# Define USING_BOOST_BIND
# Ifdef USING_BOOST_BIND
# Define BOOST_BIND_ENABLE_STDCALL
# Include <boost/bind. hpp>
# Define SCOPE_EXIT_BIND: boost: bind
# Else
# Include <xl/Meta/xlBind. h>
# Define SCOPE_EXIT_BIND: xl: Bind
# Endif
The highlighted clause is used to enable the support for the _ stdcall call Convention of boost: bind. Then copy the ScopeGuardImplBase of Loki:
Class ScopeGuardImplBase
{
Public:
ScopeGuardImplBase (): m_bDismissed (false)
{
}
ScopeGuardImplBase (ScopeGuardImplBase & that ):
M_bDismissed (that. m_bDismissed)
{
That. Dismiss ();
}
~ ScopeGuardImplBase ()
{
}
Protected:
Template <typename J>
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 has try… in j. Execute... Catch ..., I personally think there shouldn't be (I think Loki: ScopeGuard doesn't seem like it should be in its own try... Catch...
ScopeGuard only helps us call a function. As to whether this function is abnormal, it should not swallow it quietly, but should return it to our original face. Do you know? But why do almost all the articles about ScopeGuard Talk About This try... Catch... How easy is it to use?
), So remove it.
Then for ScopeGuardImpl, we put aside 0, 1, and 2 and use a super concise implementation:
Template <typename F>
Class ScopeGuardImpl: public ScopeGuardImplBase
{
Public:
ScopeGuardImpl (F fn ):
ScopeGuardImplBase (), m_fn (fn)
{
}
~ ScopeGuardImpl ()
{
StaticExecute (* this );
}
Void Execute ()
{
M_fn ();
}
Private:
F m_fn;
};
We only need one parameter, an executable object F fn compatible with the function signature "void. We save this fn until the Destructor executes it.
It's almost the same. For ease of use, copy another MakeGuard:
Template <typename F>
Inline ScopeGuardImpl <F> MakeGuard (F f)
{
Return ScopeGuardImpl <F> (f );
}
Finally, define a macro XL_ON_BLOCK_EXIT for anonymous use:
# 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 __))
Like Loki, we use the row number as the name of the "anonymous" variable. Note that the entire bind must be filled in the parameter after MakeGuard, which may cause a little trouble when using the name.
Well, the implementation has been completed. We will use the previous example again:
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 = 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;
}
The highlighted part is more troublesome than Loki. If it is clear that boost: bind or xl: Bind is used, write it:
ScopeGuard sgDeleteFile = MakeGuard (boost: bind (DeleteFile, FILE_NAME ));
Or
ScopeGuard sgDeleteFile = MakeGuard (xl: Bind (DeleteFile, FILE_NAME ));
(The difference is that if you bind a member function, the object pointer of boost: Bind is in the second parameter, and xl: bind is in the first parameter .)
Now, write it here. Please advise.
At this point, the syntaxes used in xlLib are almost the same. Next we will focus on functional things.
From stream comics