The code was something to this effect (roughly)....
mouse.setCursor(HOUR_GLASS); // change state
try {
doSomeTimeConsumingWork( );
}
__finally { // non standard extension
mouse.setCursor(NORMAL_POINTER); //reset state
}
Clearly there is no resrouce acquisistion/disposal going on here. Just
change some state , do some work, and then reset the state.
And here RAII can be put to work (and ScopeGuard comes to mind), but it is
not at all natural to use destructors for doing such things. That
immediately feels more like a hack.... and hides the intent of the code
somewhere behind the scenes.
Also it seems useful in cases where you want to guarantee the restoration of
an invariant.
-Roshan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Microsoft Visual C++ family of compilers provides exception handling in
C (as opposed to C++) through compiler extensions and OS facilities
called Structured Exception Handling (SEH). These extensions are not
disabled when compiling C++ code. Nevertheless, they do not have much
use in C++, as documentation for MSVC clearly states. But for C code,
these are as much valuable as "try ... finally" for Java. I suppose that
you found this code in C project or project (partly) ported from C, or
code written by someone accustomed to this (quite old) exception
handling mechanism and/or not familiar with RAII.
Having said that, there are situations when this mechanism has good use
also in C++ code directed on Windows platform - that is because some OS
specific functions might throw SEH exception in well-defined conditions
(examples in documentation of InitializeCriticalSection, MapViewOfFile
etc).
> Clearly there is no resrouce acquisistion/disposal going on here. Just
> change some state , do some work, and then reset the state.
> And here RAII can be put to work (and ScopeGuard comes to mind), but
> it is not at all natural to use destructors for doing such things.
> That immediately feels more like a hack.... and hides the intent of
> the code somewhere behind the scenes.
RAII would not be hack at all, at least for me. You acquire resource
(cursor) and then release it (reset cursor to normal state) when you are
done.
B.
mouse.setCursor(HOUR_GLASS); // change state
try {
doSomeTimeConsumingWork( );
}
__finally { // non standard extension
mouse.setCursor(NORMAL_POINTER); //reset state
}
Funny you mention this as an example. In MFC, there is a class called
CWaitCursor that uses RAII exactly for that puropse.
> The code was something to this effect (roughly)....
>
> mouse.setCursor(HOUR_GLASS); // change state
> try {
> doSomeTimeConsumingWork( );
> }
> __finally { // non standard extension
> mouse.setCursor(NORMAL_POINTER); //reset state
> }
>
>
> Clearly there is no resrouce acquisistion/disposal going on here.
Just
> change some state , do some work, and then reset the state.
> And here RAII can be put to work (and ScopeGuard comes to mind), but
it is
> not at all natural to use destructors for doing such things.
I think this is a very good example to use an object. It has several
advantages. You write only 1 line instead of 6. The code becomes
something like this:
{
HourClassCursor hcc;
doSomeTimeConsumingWork();
}
And not to forget. After you've inserted a lot of your code snipets in
different functions you later discover that this doesn't nest. And now
you have to find all places and change them to increment/decrement a
counter and only switch the cursor when leaving/reaching 0. With RAII
you only have to adjust the constructor/destructor to fix your code.
Okay, this would also be true if you use functions to do the actual
counting/switching. But if you are not careful, you end up having some
unmatched function calls and your mouse cursor always stays like a hour
glass. This effect is impossible with RAII.
Joerg
I find that code much harder to follow than:
const cursort old_cursor = mouse.setCursor(HOUR_GLASS);
ON_BLOCK_EXIT_OBJ(mouse,&mouse_t::setCursor,old_cursor);
this is 2 lines, instead of 7.
You can even do it in a single line, if that floats your boat, if set
setCursor returns the old cursor:
ON_BLOCK_EXIT_OBJ(mouse,&mouse_t::setCursor,mouse.setCursor(HOUR_GLASS));
Both of these code snippets are far superior to the try/finally in
readability. Also, generally a "finally" block gets run no matter what,
even if "doSomeTimeConsumingWork" were to throw and exception. But, if an
exception were thrown, how do you know that you can reliably put the cursor
back? At least with RAII, if the exception is caught, the resource is
guaranteed to be restored (object destructor is called). If the exception
is not caught, it is undefined whether it will run, or not. On my platform,
it currently does not.
joshua lehrer
factset research systems
NYSE:FDS
This unqualified assertion makes it sound as if this is objectively
true when it is subjective. I find a finally clause far more intuitive
and readable than a side-effect of a destructor for an object defined
at the top of a (possibly lengthy) function.
Cheers,
Dave
The problem with the code above is that the two logically paired actions
are spread across the code. It's the exact kind of example that
ScopeGuard puts to shame, because with ScopeGuard (and ON_BLOCK_EXIT)
you can put a current and future action together, as a single unit.
Andrei
[...]
>>Clearly there is no resrouce acquisistion/disposal going on
>>here. Just change some state , do some work, and then reset
>>the state. And here RAII can be put to work (and ScopeGuard
>>comes to mind), but it is not at all natural to use
>>destructors for doing such things.
>>That immediately feels more like a hack.... and hides the
>>intent of the code somewhere behind the scenes.
> RAII would not be hack at all, at least for me. You acquire
> resource (cursor) and then release it (reset cursor to normal
> state) when you are done.
"'Tis but thy name that is my enemy." (Romeo and Juliette,
II.2, l.38). There are no resources involved, and resource
acquision isn't being used for initialization. But what is
commonly called RAII is certainly what is called for. It sure
smells sweet, even if we don't call it a rose.
IMHO, the only reason that it feels like a hack is because the
pattern is so poorly named. I regularly use RAII for things
where no resources are involved, and just as often for cases
where the resource acquisition does not take place in the
initialization.
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Using a finally block is helpful when:
1) the cleanup code needs to access local variables in the function
2) it's a one use issue; i.e. when making a class just for one use would
wind up cluttering the code
For example, suppose we want to open a file, do some processing, then make
sure it gets closed:
void foo()
{
...
FILE *fp = fopen( ... );
if (fp)
try
{
....
}
finally
{
fclose(fp);
}
}
Yes, I could wrap fp in a class with a destructor, and I would if I was
going to use it more than once. But if it's just a one shot deal, the above
is simpler and more direct. Ideally, the language should support both RAII
objects and try-finally.
>>I know that using the finally block (in Java or C#) to dispose
>>resources is considered inferiror to RAII. However I recently
>>came accross a little C++ example that used some compiler's
>>__finally language extension that made me rethink there might
>>be some value in finally...sometimes.
> Using a finally block is helpful when:
> 1) the cleanup code needs to access local variables in the function
That is (at least in my mind) one of the major motivations for
"lambda classes" (or whatever you want to call them). While
there's been a great deal of discussion concerning the possible
syntax and the various uses, I think that all of the suggestions
have in common an ability to locally define a class or a
function in which you can access local variables.
Given such a class, it's pretty trivial to define something
like:
cleanup { some code .. } ;
as a contraction for an instance of a lambda class with a
destructor consisting of the code in the {...}.
> 2) it's a one use issue; i.e. when making a class just for one
> use would wind up cluttering the code
Agreed. On the other hand, in current C++, not making the class
means duplicating the code.
IMHO, the functionality is definitly useful. The question is
just how to achieve it. Personally, I find that something like
the cleanup verb, above, fits better into the overall scheme of
things in C++.
> For example, suppose we want to open a file, do some
> processing, then make sure it gets closed:
> void foo()
> {
> ...
> FILE *fp = fopen( ... );
> if (fp)
> try
> {
> ....
> }
> finally
> {
> fclose(fp);
> }
> }
> Yes, I could wrap fp in a class with a destructor, and I would
> if I was going to use it more than once. But if it's just a
> one shot deal, the above is simpler and more direct. Ideally,
> the language should support both RAII objects and try-finally.
The problem with all such examples is that they take an obvious
and frequent case. One so obvious and frequent that it
justifies a library class to handle it. But I totally agree in
principal; there will always be one-of cases where the explicit
class is just so much verbage. On the other hand, I rather
prefer:
FILE *fp = fopen( ... ) ;
if ( fp != NULL ) {
cleanup { fclose( fp ) ; }
}
Except that it's still a bad example, since in the normal case,
I'd certainly want to handle errors in the fclose, and would
thus naturally write something along the lines of:
if ( fp != NULL ) {
try {
...
if ( fclose( fp ) != 0 ) {
// handle errors...
}
} catch ( ... ) {
fclose( fp ) ; // can't do much about errors here,
// but it doesn't matter, the
// operation is hosed anyway.
throw ;
}
}
(Except, of course, I'd actually be using fstream, so the
try/catch would be unnecessary. Unless I was outputting and
wanted to delete the corrupted file I'd just generated.)
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Oops! ScopeGuard/ON_BLOCK_EXIT is logically (almost) equivalent to
try/finally. The 'finally clause' is merely written before the
statements, not below. I case of ScopeGuard the user must even call a
second logically paired action (confusingly called 'dismiss'). In both
cases (try/finally and ScopeGuard) it's the user's responsibility to
set up the release of a resource. In both cases resource management is
not encapsulated.
Best regards,
Roland Pibinger
As you point out, in a lengthy function, try/finally puts the "undo"
operation is far away from your "do" operation. If you change your
operation at the top, you may forget that you need to scroll all the way
down to the finally block to fix the undo operation. Sure, in a SIMPLE
example such as:
do();
try {
} finally {
undo();
}
it is obvious. But, consider when there is lots of code between try and
finally. If you keep related operations next to eachother, there is less
chance that you will make a mistake later on down the road when modifying
the code.
Also, someone who looks at your code won't need to ask if you are cleaning
up your resource, as the resource is acquired and disposition is set-up on
the very next line. With try/finally one needs to scroll way down (given a
large function) to see if you are releasing the resource properly, or at
all.
joshua lehrer
factset research systems
NYSE:FDS
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
This is correct. However, people will not take the time to write custom
classes, so, scopeguard is a compromise.
joshua lehrer
factset research systems
NYSE:FDS
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
"Roland Pibinger" <rpb...@yahoo.com> wrote in message
news:4279ea1...@news.utanet.at...
> On 4 May 2005 20:16:32 -0400, "Andrei Alexandrescu (See Website
For
> Email)" <SeeWebsit...@moderncppdesign.com> wrote:
> >
> >The problem with the code above is that the two logically
paired actions
> >are spread across the code. It's the exact kind of example
that
> >ScopeGuard puts to shame, because with ScopeGuard (and
ON_BLOCK_EXIT)
> >you can put a current and future action together, as a single
unit.
>
> Oops! ScopeGuard/ON_BLOCK_EXIT is logically (almost) equivalent
to
> try/finally. The 'finally clause' is merely written before the
> statements, not below.
I have created a small extension to ScopeGuard that we have been
using locally for some time, called "finally" (the implementation
details shall be left to the imagination of the reader for the
moment). The following type of code compiles and executes
perfectly, using no non-standard language extensions. "finally"
is in extensive production use throughout our system.
using namespace boost;
mouse.setCursor(HOUR_GLASS);
finally(bind(&MouseType::setCursor, ref(mouse), NORMAL_POINTER))
{
doSomeTimeConsumingWork();
}
This provides exactly the same semantics as ON_BLOCK_EXIT, but I
like the stylistic look and feel better (esp. consistent use of
bind() to avoid the need for ON_BLOCK_EXIT vs.
ON_BLOCK_EXIT_OBJ).
I also have a similar wrapper for named ScopeGuards that can be
.Dismiss()ed, which I call GuardRegion - GuardRegion(name,
funcobj) { stuff(); name.Dismiss(); }
"Roshan Naik" <som...@somehwere.com> wrote in message
news:J50ee.5377$HL2....@newsread3.news.pas.earthlink.net...
>
> mouse.setCursor(HOUR_GLASS); // change state
> try {
> doSomeTimeConsumingWork( );
> }
> __finally { // non standard extension
> mouse.setCursor(NORMAL_POINTER); //reset state
> }
>
I feel that the RAII-based prefix form of finally() offers
benefits over this form of exception-handling - aside from
portability and potential reduction in duplication of code, there
are aspects of system debugging (in cases of abnormal termination
due to unhandled exceptions) which are often complicated by the
presence of such __finally constructs. RAII-based cleanup
handlers do not usually encounter the same difficulties. OpenVMS
(on which win32's SEH exception-handling mechanism was based) has
a similar construct called FINALLY - we have long discouraged the
use of FINALLY by developers in favor of RAII-style exception
handling, due to these issues.
--
Hillel Y. Sims
hsims AT factset.com
I have a suggestion for a way to improve ScopeGuard and bring it's interface
more in line with boost::bind.
Using scopeguard's native object creation factories are superior to mixing
scopeguard and boost::bind because boost::bind requires dynamic memory
allocation and a virtual function call, scopeguard does not. Thus, I'd
prefer to use ScopeGuard's factory methods, rather than a mix of ScopeGuard
and boost::bind.
However, ScopeGuard strays from boost::bind in two ways:
1] boost::bind does not require special "OBJ" macros; ScopeGuard does.
2] boost::bind passes the method pointer first, then the "this" pointer,
then the arguments. ScopeGuard passes the object by reference, then the
method pointer, then the arguments by copy. This mixing of reference and
copy confuses people.
Both of these can be solved by adding additional object factory methods to
ScopeGuard.h. Here, I supply the two-argument versions, but a similar
pattern can easily be followed for the zero-argument and one-argument
versions:
template <typename Ret, typename P1, typename P2, class Obj1, class Obj2>
inline ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1,P2),P1,P2>
MakeScopeGuard(Ret(Obj2::*memFun)(P1,P2), Obj1 *obj, P1 p1, P2 p2)
{
return
ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1,P2),P1,P2>::MakeObjScopeGuard(*obj,m
emFun, p1,p2);
}
template <typename Ret, typename P1, typename P2, class Obj1, class Obj2>
inline ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1,P2)const,P1,P2>
MakeScopeGuard(Ret(Obj2::*memFun)(P1,P2)const, Obj1 *obj, P1 p1, P2 p2)
{
return
ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1,P2)const,P1,P2>::MakeObjScopeGuard(*
obj,memFun,p1,p2);
}
Usage:
old: ON_BLOCK_EXIT_OBJ(s,&string::clear);
new: ON_BLOCK_EXIT(&string::clear,&s);
Implementation details:
Two functions need to be supplied, one for const-members and one for
non-const members.
Also, these functions are templated both on the object pointer type supplied
(Obj1) AND on the method pointer's object-type (Obj2). This allows you to
pass in a pointer to a subclass, while passing in a pointer to a method on a
base. While the compiler normally would do this cast automatically, it will
not, in this case, as the other non-object templated factory methods would
be better matches without any casting, and would fail to compile.
Each factory method remains a simple cover function to creating an
ObjScopeGuardImplN. The factory method's sole purpose remains doing the
template argument deduction.
I have added these factory methods to my local version of ScopeGuard, and
thought I would share with the group.
joshua lehrer
factset research systems
NYSE:FDS
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
That looks great, but why do you need ref(mouse) as opposed to mouse? I
think the default should be reference, and only if you need a copy you'd
say value(mouse). Or does that come with the generality of the construct?
Andrei
What do you think about the following?
FILE* fp = fopen("xyz","r");
if (fp != 0) {
finally(bind<int>(fclose, fp)) {
process_file(fp);
}
}
I've just copy/pasted that from a source file that compiled and
linked successfully.
>
> Except that it's still a bad example, since in the normal case,
> I'd certainly want to handle errors in the fclose, and would
> thus naturally write something along the lines of:
>
> if ( fp != NULL ) {
> try {
> ...
> if ( fclose( fp ) != 0 ) {
> // handle errors...
> }
> } catch ( ... ) {
> fclose( fp ) ; // can't do much about errors
here,
> // but it doesn't matter, the
> // operation is hosed anyway.
> throw ;
> }
> }
>
> (Except, of course, I'd actually be using fstream, so the
> try/catch would be unnecessary. Unless I was outputting and
> wanted to delete the corrupted file I'd just generated.)
>
What if the filename itself has also been corrupted when you make
that call to remove(filename)? Besides, isn't fclose() a no-throw
operation?
--
Hillel Y. Sims
hsims AT factset.com
I think spreading the code gratuitously is an important detail. For
small idioms, I've noticed that things like where the logically
consistent pieces are, and what the level of syntactic baroqueness is,
do matter a lot.
> I case of ScopeGuard the user must even call a
> second logically paired action (confusingly called 'dismiss').
Nonono. "Suggestively" called dismiss. :o)
Note that try/finally doesn't have any dismissing mechanism, so it's
unfair to mention that as a disadvantage of ScopeGuard. For try/finally,
the user must maintain their own flag. (They might call it 'dismissed'.
:o)) So try/finally is more of a poor man's version of ON_BLOCK_EXIT
(with the disadvantage that it spreads away code that should be
together, but with the advantage that it allows arbitrary code in the
finally block), which in turn is a particular use of the dismissible
ScopeGuard.
> In both
> cases (try/finally and ScopeGuard) it's the user's responsibility to
> set up the release of a resource. In both cases resource management is
> not encapsulated.
It's not, but let's not forget you don't pay the price of writing the
capsule.
Would I recommend ScopeGuard instead of smart pointers? No. Would I
recommend it over writing a little class of limited utility? Yes. Would
I prefer it to try/finally even if I had the latter? Often. Use judgment
and the proper tool for the job.
Andrei
> However I recently came accross a little C++ example that used some
> compiler's __finally language extension
> that made me rethink there might be some value in
finally...sometimes.
>
> The code was something to this effect (roughly)....
>
> mouse.setCursor(HOUR_GLASS); // change state
> try {
> doSomeTimeConsumingWork( );
> }
> __finally { // non standard extension
> mouse.setCursor(NORMAL_POINTER); //reset state
> }
>
>
> Clearly there is no resrouce acquisistion/disposal going on here.
Just
> change some state , do some work, and then reset the state.
> And here RAII can be put to work (and ScopeGuard comes to mind), but
it is
> not at all natural to use destructors for doing such things.
It will seem perfectly natural after a bit of practice. And this sort
of thing almost never happens exactly once in an application. In your
example there are seven lines of code for controlling the cursor,
wrapped around the one line the programmer really wanted to write. How
many times are you willing to cut-and-paste those seven lines to save
writing one ten-line class? How many times are you willing to read
those seven lines on your way to finding the interesting bits that do
some time consuming work?
For me, the answer is about zero. I would create the SetWaitCursor
class immediately, because I don't want the real purpose of the code
obscured by all that noise about setting the cursor back and forth.
I think ideally, the language would support mechanisms that allow the
user to define block_cleanup code a la:
void foo()
{
...
FILE *fp = fopen( ... );
block_cleanup { fclose(fp); }
if (fp)
....
}
that is, the ability to specify code to be executed upon a scope's exit.
Refinenents would specify code to be executed upon normal or abnormal
(exceptional) leaving of the scope. (To clarify, fclose(NULL) has no
effect.)
A lot of code involving robustness in face of exceptions would be vastly
simplified using such idioms. On the other hand, it would be a pity to
burden the language proper with such syntax and semantics, so the ideal
language would embed enough growth mechanisms so as to allow things in
the block_cleanup family to be implemented as libraries.
Andrei
I do agree with this. Scope guard does have a downside of looking ugly and
finally block
has the problem that you mention above.
Another problem with finally is that if a exception is active when finally
block was invoked and then some operation
inside finally also throws an exception then language must decide to discard
one of the
two exceptions. Java discards the former. So thats a dirty little corner for
language designers.
I hope someday we will find a better alternative to both.
- Roshan
You call Dismiss() not to set up the release of a resource, but to
dictate that the release should be skipped, don't you? You set up some
action just in case something else fails and the control goes out of
scope, and when you see it succeeded you explicitly skip the action. I
don't agree to say that the resource management is not encapsulated in
this case; you cannot forget to release the resource.
--
Seungbeom Kim
Beg to differ... but thats not a motivation for lambdas.
Using lambda instead of a finally block is like having a solution in hand
and
looking for a problem.
>
> Given such a class, it's pretty trivial to define something
> like:
>
> cleanup { some code .. } ;
>
> as a contraction for an instance of a lambda class with a
> destructor consisting of the code in the {...}.
Any reason to beleive that is more suitable than a finally block for this
task?
And lambdas are more _likely_ (i dont know the proposed semantics as of now
if there is any)
going to be more verbose than that. If its a lambda function (in future C++)
you would have to define paremater and return types...
void (void ) { some code; } // anonymous lambda function...
and if its a lambda class you would most likely need more
struct { void operator() (void) { some code;} } // anonymous lambda class
In either case you would also need to have a name if its not anonymous.
readability goes out the window... just put another try/catch inside the
"some code"
and see how readable it looks as compared to a finally block.
- Roshan
Ok, I doubt one of us is going to convince the other that one form is
more readable than the other. :)
I would say that your critique of try/finally is flawed. What you're
suggesting is that someone could alter the contents of a try block (I
assume putting the Do() op before the try block in your example was a
typo) and not pay attention to the rammifications of this change on the
finally block (or indeed catch blocks) that follow. Such action
strikes me as so negligent on behalf of the programmer that it's not a
compelling example of a problem with try/finally.
Cheers,
Dave
"Andrei Alexandrescu" <SeeWebsit...@moderncppdesign.com>
wrote in message news:IG12v...@beaver.cs.washington.edu...
> Hillel Y. Sims wrote:
> > using namespace boost;
> >
> > mouse.setCursor(HOUR_GLASS);
> > finally(bind(&MouseType::setCursor, ref(mouse),
NORMAL_POINTER))
> > {
> > doSomeTimeConsumingWork();
> > }
> >
> > This provides exactly the same semantics as ON_BLOCK_EXIT,
but I
> > like the stylistic look and feel better (esp. consistent use
of
> > bind() to avoid the need for ON_BLOCK_EXIT vs.
> > ON_BLOCK_EXIT_OBJ).
>
> That looks great, but why do you need ref(mouse) as opposed to
mouse? I
> think the default should be reference, and only if you need a
copy you'd
> say value(mouse). Or does that come with the generality of the
construct?
>
>
That is part of the bind() semantics -- it takes all arguments by
copy (due to issues with C++ and "forwarding constructors"); use
ref/cref wrappers to forward references. (Alternatively, a
pointer to the object (eg, "&mouse") may be passed, with the same
results.)
--
Hillel Y. Sims
hsims AT factset.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Beg to differ... but thats not a motivation for lambdas.
Using lambda instead of a finally block is like having a solution in hand
and
looking for a problem.
>
> Given such a class, it's pretty trivial to define something
> like:
>
> cleanup { some code .. } ;
>
> as a contraction for an instance of a lambda class with a
> destructor consisting of the code in the {...}.
Any reason to beleive that is more suitable than a finally block for this
task?
And lambdas are more _likely_ (i dont know the proposed semantics as of now
if there is any)
going to be more verbose than that. If its a lambda function (in future C++)
you would have to define paremater and return types...
void (void ) { some code; } // anonymous lambda function...
and if its a lambda class you would most likely need more
struct { void operator() (void) { some code;} } // anonymous lambda class
In either case you would also need to have a name if its not anonymous.
readability goes out the window... just put another try/catch inside the
"some code"
and see how readable it looks as compared to a finally block.
- Roshan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Because all other parameters are passed by copy, and boost::bind passes the
object by copy. It is too hard to remember that yor ScopeGuard behaves in a
different way. See my post under this thread.
-josh
Agreed, but then the same can be said about manual resource management
and many other bugs: forgetting to free() a block, fclose() a file etc.
on some execution path is negligent on behalf of the programmer and not
a problem with free() or fclose(). Indeed, it's not a problem with
those, but instead there is a problem with the underlying idiom.
I, for one, am convinced that cleanup { ... } block inserted next to the
corresponding "create mess" code is a superior idiom to which
try/finally doesn't hold a candle. In fact I am so convinced, I'm not
even deterred by the fact that it's not present in today's popular
languages. :o) Maybe in 15 years it will be common, and people will look
at weaker idioms attempting to do the same thing with the contempt they
look now at the all-declarations-at-the-top-of-each-function idiom.
In fact, at the risk of breaking my NDA, here's a fragment from the WUJ
magazine, year 2020: "Old die-hard vets still use the awkward
try/finally idiom to take care of simple function-level cleanup. This
relic of Java still shows itself up in old code that you might need to
maintain. It's officially deprecated, but now in the days of pluggable
compilers and modularly extensible languages, you can use pretty much
any language construct you want. (Besides, who cares about the standards
when Cortexsoft supports something.) In short, finally does pretty much
what cleanup { ... } does, except that it puts everything exactly in the
wrong place and adds baroque syntax on top, to the end of thoroughly
messing up the logic of the function it contaminates."
Andrei
No. It is not a typo. You can not configure the undo() to 'finally' run,
unless you are sure that it ran successfully. The only way to do this is
start the try block AFTER you are 100% sure that do() completed succesfully
and did not throw. If "do()" were to throw, you do not want "undo()" to
run because "do()" never completed.
This is yet another example of objects being superior to try/finally. The
language guarantees that object's destructors won't get run unless the
object was constructed successfully. If you had an object:
struct doer {
doer() { do(); }
~doer() { undo(); }
};
Then you are sure that undo() will only be called if do() did not throw. If
you wrote the code with try/finally blocks, it is possible for someone to
write it incorrectly, in the way that you point out:
try {
do();
} finally {
undo();
}
In this code, if "do();" throws, "undo();" will, incorrectly, be called.
> finally block (or indeed catch blocks) that follow. Such action
> strikes me as so negligent on behalf of the programmer that it's not a
> compelling example of a problem with try/finally.
>
********
"Write code not so that it can be maintained correctly, but so that it is
harder to maintain it incorrectly."
********
By using a try/finally, you are making it too easy for a programmer to be
negligent. By using ScopeGuard, or a stack based object, you encourage
proper maintenance.
Am I negligent if I make a mistake modifying your code, when you write
spaghetti code like this:
do(1);
try {
do(2);
try {
do(3);
try {
foo();
} finally {
undo(3);
}
} finally {
undo(2);
}
} finally {
undo(1);
}
BTW, is the above code correct? Is there a nesting bug? Isn't it easier to
review the code if you see this instead:
{
do(1);
ON_BLOCK_EXIT(undo,1);
do(2);
ON_BLOCK_EXIT(undo,2);
do(3);
ON_BLOCK_EXIT(undo,3);
foo();
}
joshua lehrer
factset research systems
NYSE:FDS
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
But 'commit()' would be a more intuitive name.
>You set up some
>action just in case something else fails and the control goes out of
>scope, and when you see it succeeded you explicitly skip the action. I
>don't agree to say that the resource management is not encapsulated in
>this case; you cannot forget to release the resource.
You (the user) must set up some action. Therefore resource management
is not encapsulated. I have yet to see a convincing example where you
have so many different resource types in a program that you cannot
afford to encapsulate each.
Best wishes,
Roland Pibinger
I believe that particular case is already handled by:
if (boost::shared_ptr<FILE> fp( fopen( ... ), fclose )) {
// ...
}
> Using a finally block is helpful when:
>
> 1) the cleanup code needs to access local variables in the function
>
> 2) it's a one use issue; i.e. when making a class just for one use would
> wind up cluttering the code
In the example you gave, we only need one local variable, fp, and the
cleanup is already encapsulated down to a simple function call. So
something rather generic like shared_ptr is enough.
I imagine there are more complicated cases where this won't work, but I
distrust complicated code. Maybe it would be better to simplify it.
-- Dave Harris, Nottingham, UK.
> >> 1) the cleanup code needs to access local variables in the
function
> > That is (at least in my mind) one of the major motivations
> > for "lambda classes" (or whatever you want to call them).
> > While there's been a great deal of discussion concerning the
> > possible syntax and the various uses, I think that all of
> > the suggestions have in common an ability to locally define
> > a class or a function in which you can access local
> > variables.
> Beg to differ... but thats not a motivation for lambdas.
> Using lambda instead of a finally block is like having a
> solution in hand and looking for a problem.
I don't see why. What you want is code that executes when the
scope ends. Creating an unnamed object (with local scope, not
temporary) whose destructor contains the code is the obvious
solution; it is, in fact, the standard C++ solution. A lambda
object just makes accessing local variables easier.
> > Given such a class, it's pretty trivial to define something
> > like:
> > cleanup { some code .. } ;
> > as a contraction for an instance of a lambda class with a
> > destructor consisting of the code in the {...}.
> Any reason to beleive that is more suitable than a finally
> block for this task?
Yes. The cleanup code is where it should be, not somewhere down
at the bottom of the block where a maintenance programmer
doesn't see it, and doesn't update it when necessary.
> And lambdas are more _likely_ (i dont know the proposed
> semantics as of now if there is any) going to be more verbose
> than that. If its a lambda function (in future C++) you would
> have to define paremater and return types...
> void (void ) { some code; } // anonymous lambda function...
> and if its a lambda class you would most likely need more
> struct { void operator() (void) { some code;} } // anonymous lambda
class
> In either case you would also need to have a name if its not
> anonymous.
There was quite a long thread about lambdas here not too long
ago. One thing was clear -- whatever the formal semantics
adopted (class or function), there was a demand that the
"frequent" uses be simple, and not too verbose. There's still
no formal proposal that I'm aware of, but I think that there is
a strong consensus that you shouldn't have to write a full class
declaration (or function definition) when all you want is a
single function and the compiler is capable of deducing the
required types.
> readability goes out the window... just put another try/catch
> inside the "some code" and see how readable it looks as
> compared to a finally block.
Just as readable as putting a try/catch block inside a finally
block, no?
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> > FILE *fp = fopen( ... ) ;
> > if ( fp != NULL ) {
> > cleanup { fclose( fp ) ; }
> > }
> What do you think about the following?
> FILE* fp = fopen("xyz","r");
> if (fp != 0) {
> finally(bind<int>(fclose, fp)) {
> process_file(fp);
> }
> }
> I've just copy/pasted that from a source file that compiled
> and linked successfully.
Really? Using which compiler and which libraries? (I find it
very misleading, for example, that bind<int> can be used with a
FILE*. Supposing an appropriate finally, bind<FILE*> might not
be too bad, but I still prefer the syntax of cleanup -- a simple
inline function.)
> > Except that it's still a bad example, since in the normal
> > case, I'd certainly want to handle errors in the fclose, and
> > would thus naturally write something along the lines of:
> > if ( fp != NULL ) {
> > try {
> > ...
> > if ( fclose( fp ) != 0 ) {
> > // handle errors...
> > }
> > } catch ( ... ) {
> > fclose( fp ) ; // can't do much about errors
> here,
> > // but it doesn't matter, the
> > // operation is hosed anyway.
> > throw ;
> > }
> > }
> > (Except, of course, I'd actually be using fstream, so the
> > try/catch would be unnecessary. Unless I was outputting and
> > wanted to delete the corrupted file I'd just generated.)
> What if the filename itself has also been corrupted when you
> make that call to remove(filename)?
And why would the filename be corrupted? Anymore than anything
else, of course -- if lower level code writes through an
uninitialized pointer, there's not much you can do to protect
yourself.
> Besides, isn't fclose() a no-throw operation?
One would hope so, given that it is a C function:-). And of
course, if it wasn't, I'd be more careful about using it in a
catch block.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
First: many things that are not obviously resources, are.
Cursor-changes included. However, it would be nice to have a
better-understood term for the set of entities bound by
initialization/cleanup contracts or pre- and postconditions. Inanouts?
;-)
Second: Finally / block cleanup / (insert variation here) is dangerous
for two essential but related reasons:
1. We're back to that age-old problem of forgetting that cleanup is
required.
2. (Perhaps controversial?) Cleanup for an entity is implied by its
initialization; a transaction making use of an entity has no business
explicitly dictating its finalization. By way of analogy, we don't
write "now switch off the stove" when writing recipes. As
initialization and cleanup are logically associated, it seems sensible
that they should be spacially associated as well, whereas code making
heavy use of "finally" tends to be afflicted with a cluttering of
concerns.
Just in case (2) isn't clear, what I'm saying is that it makes no sense
for a function making use of an object (of whatever sort) to contain
code relating to its initialization and finalization, contractual
inanout obligations, etc. These topics are presumably not the main
focus of the function. The point of a high-level language is to
abstract; to move away from the nit-picking detail required in
instructing the machine. RAII provides a rather good mechanism for
doing so; "finally" is, IMHO, a step backwards.
Regards
David Turner
FWIW: I think I prefer your slightly longer name than my
"cleanup".
> Refinenents would specify code to be executed upon normal or
> abnormal (exceptional) leaving of the scope. (To clarify,
> fclose(NULL) has no effect.)
That's an interesting idea. More generally, it might be
interesting to be able to distinguish in a destructor whether it
was called normally, or because of stack unwinding. I've been
using a special function for this (commit), and most of the
time, it's not a bother, but I can think of cases where it might
be.
> A lot of code involving robustness in face of exceptions would
> be vastly simplified using such idioms. On the other hand, it
> would be a pity to burden the language proper with such syntax
> and semantics, so the ideal language would embed enough growth
> mechanisms so as to allow things in the block_cleanup family
> to be implemented as libraries.
Would be nice.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> >You call Dismiss() not to set up the release of a resource,
> >but to dictate that the release should be skipped, don't you?
> But 'commit()' would be a more intuitive name.
That would depend on the context, I think. But I'm not sure
"intuitive" would be the word I would choose: more usual, or
more expected, perhaps.
In the end, it is a question of view point. In Andrei's
ScopeGuard, you define finalization actions, then say dismiss to
say you don't need them. In a traditional transaction, you
carry out a number of actions, then call commit to say you want
to make them permanent.
> >You set up some action just in case something else fails and
> >the control goes out of scope, and when you see it succeeded
> >you explicitly skip the action. I don't agree to say that the
> >resource management is not encapsulated in this case; you
> >cannot forget to release the resource.
> You (the user) must set up some action. Therefore resource
> management is not encapsulated. I have yet to see a
> convincing example where you have so many different resource
> types in a program that you cannot afford to encapsulate each.
It's not a question of not being able to afford to encapsulate
each. It's more a question of handling a couple of one-of cases
easily. If you find yourself handling the same resource several
times with a ScopeGuard, then I do think you're abusing it -- if
you're doing the same thing several times, there must be an
underlying concept which needs abstracting.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
It's hard to show you an example because obviously that would be too
large. :o) Suffice to say that Petru and I are getting praising emails
for ScopeGuard more than for any other article I've written (truth be
told, that might just as well mean that the others are terrible), and
that people I've worked long ago have consistently brought ScopeGuard
as one of their first tools whenever they moved to new companies.
Andrei
This specific problem - setting/resetting the cursor - is a wonderful
example of how using a class is a far superior approach. If the cursor
was a local setting of your code snippet this would really boil down to
personal taste. However the cursor is a portion of the application's
global state that needs to be modified throughout the GUI subsystem.
This means that if within your doSomeTimeConsumingWork() someone else
applies your idiom the cursor would be reset too early. On the other
hand it is very easy to provide your class with a static counter which
is increased in the constructor and decreased in the destructor; the
cursor is then changed only when the counter changes from 0 to 1 and
viceversa.
>From this point it wouldn't be to difficult to modify again to keep a
static stack of cursor shapes so as to be able to handle more than to
of them.
Not only this is a problem that deserves a custom class, but it's also
one class that should be in every GUI framework.
Cheers,
Nicola Musatti
Not only that, but as I pointed out in another message, in this case
the Copy&Paste solution doesn't work.
> For me, the answer is about zero. I would create the SetWaitCursor
> class immediately, because I don't want the real purpose of the code
> obscured by all that noise about setting the cursor back and forth.
This is an important point: the perfect candidate for RAII is an
activity that is conceptually at a lower level than the current
function.
Cheers,
Nicola Musatti
This is not strictly correct. boost::function requires dynamic memory
allocation and an indirect call. boost::bind itself does none of these. You
can use the ScopeGuard const& technique to implement
ON_BLOCK_EXIT( arbitrary-function-object );
which then allows you to use boost::bind to create the arbitrary function
object. Then you can create an alias
ON_BLOCK_EXIT_(( boost-bind-args ));
if you like. :-)
>Roland Pibinger wrote:
>> I case of ScopeGuard the user must even call a
>> second logically paired action (confusingly called 'dismiss').
>
>Nonono. "Suggestively" called dismiss. :o)
Recently I saw a ScopeGuard implementation that hat a commit() instead
of the dismiss(). IMO, a more intuitive name.
>> In both
>> cases (try/finally and ScopeGuard) it's the user's responsibility to
>> set up the release of a resource. In both cases resource management is
>> not encapsulated.
>
>It's not, but let's not forget you don't pay the price of writing the
>capsule.
>Would I recommend ScopeGuard instead of smart pointers? No. Would I
>recommend it over writing a little class of limited utility? Yes.
But how many different resources must be managed in a typical program
so that you cannot afford to encapsulate them? Any examples? IMO,
resource acquisition/release should always be encapsulated.
Best regards,
Roland Pibinger