Prev: reasoning of ::std::cout (ostream) conversion w.r.t. plain, signed and unsigned chars...
Next: reasoning of ::std::cout (ostream) conversion w.r.t. plain, signed and unsigned chars...
From: DeMarcus on 9 Jan 2010 08:25 Hi! David Abrahams introduced the exception safety guarantees. http://www.boost.org/community/exception_safety.html In order to easier create exception safe code I have written a function code template (a mind template, *not* a C++ template) that I can give to colleagues, friends, you (if you want), and myself. In the code I have used Petru Marginean's and Andrei Alexandrescu's ScopeGuard. http://www.ddj.com/cpp/184403758 The function code template looks like this. SomeType function( SomeType argument ) { // Beginning of irreversible, throwing code. // Non-leaking code here gives Basic Guarantee [D.Abrahams]. // Beginning of reversible, throwing code. // Code starting here gives Strong Guarantee. exampleVector_.push_back( "Something" ); ScopeGuard guard1 = makeScopeGuard( exampleVector_, &std::vector<std::string>::pop_back ); exampleList_.push_back( "Something else" ); ScopeGuard guard2 = makeScopeGuard( exampleList_, &std::list<std::string>::pop_back ); // ... more code. Only the last operation does not need // a ScopeGuard. guard1.dismiss(); guard2.dismiss(); // Beginning of irreversible, non-throwing (non-failing) code. // Code only here gives No-throw Guarantee. return; } I would gladly hear your comments about it so I can improve it to be perfect. Please give your thoughts, first and foremost, about how to write good comments that will guide the programmer to fill in correct code at correct places. Also if you have ideas how tools like ScopeGuard can be used, that is welcome too. Thanks, Daniel -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Maxim Yegorushkin on 10 Jan 2010 02:35 On 10/01/10 01:25, DeMarcus wrote: > Hi! > > David Abrahams introduced the exception safety guarantees. > http://www.boost.org/community/exception_safety.html > > In order to easier create exception safe code I have written a function > code template (a mind template, *not* a C++ template) that I can give to > colleagues, friends, you (if you want), and myself. In the code I have > used Petru Marginean's and Andrei Alexandrescu's ScopeGuard. > http://www.ddj.com/cpp/184403758 > > The function code template looks like this. > > SomeType function( SomeType argument ) > { > // Beginning of irreversible, throwing code. > // Non-leaking code here gives Basic Guarantee [D.Abrahams]. > > // Beginning of reversible, throwing code. > // Code starting here gives Strong Guarantee. > > exampleVector_.push_back( "Something" ); > ScopeGuard guard1 = makeScopeGuard( exampleVector_, > &std::vector<std::string>::pop_back ); > > exampleList_.push_back( "Something else" ); > ScopeGuard guard2 = makeScopeGuard( exampleList_, > &std::list<std::string>::pop_back ); > > // ... more code. Only the last operation does not need > // a ScopeGuard. > > guard1.dismiss(); > guard2.dismiss(); > > // Beginning of irreversible, non-throwing (non-failing) code. > // Code only here gives No-throw Guarantee. > > return; > } > > > I would gladly hear your comments about it so I can improve it to be > perfect. Please give your thoughts, first and foremost, about how to > write good comments that will guide the programmer to fill in correct > code at correct places. Also if you have ideas how tools like ScopeGuard > can be used, that is welcome too. In the above code it may be easier to put rollback code in an exception handler: SomeType function( SomeType argument ) { enum { STAGE_0, STAGE_1, STAGE_2 } stage = STAGE_0; try { // do code exampleVector_.push_back( "Something" ); stage = STAGE_1; exampleList_.push_back( "Something else" ); stage = STAGE_2; // ... } catch(...) { // reverse order undo code switch(stage) { case STAGE_2: exampleList_.pop_back(); case STAGE_1: exampleVector_.pop_back(); default: break; } throw; } } This approach is a bit lower level, but, I think, it provides much greater flexibility than a scope guard. If scope guards are good enough for you, you might be interested in Boost.ScopeExit http://www.boost.org/doc/libs/1_41_0/libs/scope_exit/doc/html/scope_exit/tutorial.html -- Max [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Goran on 11 Jan 2010 04:59 On Jan 10, 8:35 pm, Maxim Yegorushkin <maxim.yegorush...(a)gmail.com> wrote: > On 10/01/10 01:25, DeMarcus wrote: > > > > > Hi! > > > David Abrahams introduced the exception safety guarantees. > >http://www.boost.org/community/exception_safety.html > > > In order to easier create exception safe code I have written a function > > code template (a mind template, *not* a C++ template) that I can give to > > colleagues, friends, you (if you want), and myself. In the code I have > > used Petru Marginean's and Andrei Alexandrescu's ScopeGuard. > >http://www.ddj.com/cpp/184403758 > > > The function code template looks like this. > > > SomeType function( SomeType argument ) > > { > > // Beginning of irreversible, throwing code. > > // Non-leaking code here gives Basic Guarantee [D.Abrahams]. > > > // Beginning of reversible, throwing code. > > // Code starting here gives Strong Guarantee. > > > exampleVector_.push_back( "Something" ); > > ScopeGuard guard1 = makeScopeGuard( exampleVector_, > > &std::vector<std::string>::pop_back ); > > > exampleList_.push_back( "Something else" ); > > ScopeGuard guard2 = makeScopeGuard( exampleList_, > > &std::list<std::string>::pop_back ); > > > // ... more code. Only the last operation does not need > > // a ScopeGuard. > > > guard1.dismiss(); > > guard2.dismiss(); > > > // Beginning of irreversible, non-throwing (non-failing) code. > > // Code only here gives No-throw Guarantee. > > > return; > > } > > > I would gladly hear your comments about it so I can improve it to be > > perfect. Please give your thoughts, first and foremost, about how to > > write good comments that will guide the programmer to fill in correct > > code at correct places. Also if you have ideas how tools like ScopeGuard > > can be used, that is welcome too. > > In the above code it may be easier to put rollback code in an exception > handler: > > SomeType function( SomeType argument ) > { > enum { STAGE_0, STAGE_1, STAGE_2 } stage = STAGE_0; > > try { // do code > exampleVector_.push_back( "Something" ); > stage = STAGE_1; > exampleList_.push_back( "Something else" ); > stage = STAGE_2; > // ... > } catch(...) { // reverse order undo code > switch(stage) { > case STAGE_2: exampleList_.pop_back(); > case STAGE_1: exampleVector_.pop_back(); > default: break; > } > throw; > } > > } > > This approach is a bit lower level, but, I think, it provides much > greater flexibility than a scope guard. (DeMarcus, I see no problem with what you wrote there). Maxim, your approach is indeed what happens with scope guard code, but done explicitly. The problem with it, IMO, is that it adds incidental complexity: "stage" enum, try/catch, switch, and throw. Scope guard only adds "dismiss" lines at end. I also think that it's beneficial to have scope guard close to the "guarded" code, for the purpose of having these related things close by. It is kinda backwards because it's code is executed at end, but hey, perfection is in the eye of beholder ( or something like that ;-) ). Goran. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: DeMarcus on 11 Jan 2010 07:17 > I also think that it's beneficial to have > scope guard close to the "guarded" code, for the purpose of having > these related things close by. It just hit me that ScopeGuards may be exception-unsafe! Look at this code. exampleVector_.push_back( "Something" ); ScopeGuard guard1 = makeScopeGuard( exampleVector_, &std::vector<std::string>::pop_back ); What if makeScopeGuard throws?! It won't in this particular example, but if the function to makeScopeGuard is a functor or if it takes parameters, any of these may throw when copied. Then we have done an irreversible push_back. Mustn't we reverse the order to the following? ScopeGuard guard1 = makeScopeGuard( exampleVector_, &std::vector<std::string>::pop_back ); exampleVector_.push_back( "Something" ); If so, then it's also wrong in the original article by Alexandrescu & Marginean. http://www.ddj.com/cpp/184403758 Or am I thinking wrong? -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Goran on 12 Jan 2010 01:33
On Jan 12, 1:17 am, DeMarcus <use_my_alias_h...(a)hotmail.com> wrote: > > I also think that it's beneficial to have > > scope guard close to the "guarded" code, for the purpose of having > > these related things close by. > > It just hit me that ScopeGuards may be exception-unsafe! > Look at this code. > > exampleVector_.push_back( "Something" ); > ScopeGuard guard1 = makeScopeGuard( exampleVector_, > &std::vector<std::string>::pop_back ); > > What if makeScopeGuard throws?! It won't in this particular example, but > if the function to makeScopeGuard is a functor or if it takes > parameters, any of these may throw when copied. Then we have done an > irreversible push_back. > > Mustn't we reverse the order to the following? > > ScopeGuard guard1 = makeScopeGuard( exampleVector_, > &std::vector<std::string>::pop_back ); > exampleVector_.push_back( "Something" ); > > If so, then it's also wrong in the original article by Alexandrescu & > Marginean.http://www.ddj.com/cpp/184403758 > > Or am I thinking wrong? In general, with exceptions, IMO thinking goes like this: anything can throw, except code that is __specially crafted not to__. Tpical examples: primitive type assignments, pointer arithmetic and assignment, non-throwing swap etc. And in that light, guard creation should be is written so that it is has no-throw guarantee[1]. But that is achieved through programmer's engagement. And that does mean that one must avoid copy constructors that might throw. Luckily, that is in practice not hard to do, for two reasons: 1. you will seldom have such parameters to scope guards, because what you typically want done are lower-level cleanup operations, 2. if you do, you will probably want them passed by reference (the ByRef function in the article), and that should exclude constructor calls. I don't like the idea of putting scope guard in front of the "guarded" code. For example, your guard will often use guarded code results, e.g. handle h = get_handle(params); ON_BLOCK_EXIT(&release_handle, h); do_work(); So if you are tempted to do the guard before, that would become: handle h=INVALID; ON_BLOCK_EXIT(&release_handle, ByRef(h)); h = get_handle(params); do_work(); Which is less "natural" than first version, requires ByRef, and requires that release_handle works OK with INVALID, which may or may not be the case. Goran. P.S. Perhaps the original article should contain a big warning about invoking copy constructors when making a guard. ;-) [1] http://en.wikipedia.org/wiki/Exception_handling#Exception_safety -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |