Boost logo

Boost :

Subject: Re: [boost] very simple implementation of Andrei Alexandrescu's ScopeGuard idiom
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2012-09-03 23:26:52


On Mon, Sep 3, 2012 at 7:56 PM, Sergey Radionov <rsatom_at_[hidden]> wrote:
> 2012/9/4 Lorenzo Caminiti <lorcaminiti_at_[hidden]>:
>> On Mon, Sep 3, 2012 at 6:37 PM, Sergey Radionov <rsatom_at_[hidden]> wrote:
>>> 2012/9/4 Lorenzo Caminiti <lorcaminiti_at_[hidden]>:
>>>> On Mon, Sep 3, 2012 at 10:28 AM, Stewart, Robert <Robert.Stewart_at_[hidden]> wrote:
>>>>> Sergey Radionov wrote:
>>>>>>
>>>>>> https://github.com/RSATom/finally_execute/blob/master/finally
>>>>>> _execute.h
>>>>>>
>>>>>> based on http://www.drdobbs.com/cpp/generic-change-the-way-
>>>>>> you-write-excepti/184403758?pgno=2
>>>>>>
>>>>>> I know about http://sourceforge.net/projects/loki-lib/ but
>>>>>> with boost::bind this can be much simpler:
>>>>>> https://github.com/RSATom/finally_execute/blob/master/README
>>>>>>
>>>>>> I've used this in many of my projects, and found it very
>>>>>> usefull....
>>>>>>
>>>>>> don't understand, why this simple solution does not
>>>>>> contributed already to Boost by someone else... :)
>>>>>
>>>>> I created a version, too, but the usage is much simpler.
>>>>>
>>>>> Given the following:
>>>>>
>>>>> FILE * const file(fopen("test", "r"));
>>>>> if (file)
>>>>> {
>>>>> // A
>>>>> }
>>>>>
>>>>> Your usage, at A, is:
>>>>>
>>>>> finally_execute _(make_fin_exec(boost::bind(fclose, file)));
>>>>>
>>>>> Mine is:
>>>>>
>>>>> scope_guard _(fclose, file);
>>>>>
>>>>> As you can see, a little more complication in the component makes
>>>>> usage much simpler.
>>>>
>>>> Boost.ScopeExit allows you to "execute *arbitrary* code when the
>>>> enclosing scope exits". I don't see how you execute arbitrary code
>>>> with the above.
>>> Yep, but in 90% of cases I just need call one function on scope exit,
>>
>> OK if that's what you need but that's not the problem that ScopeExit
>> is trying to solve to "call a function on scope exit".
>>
>>> and in this case ScopeExit will be too cumbersome.
>>> little example:
>>>
>>> void foo()
>>> {
>>> FILE* f1 = fopen( "file1.bin", "rb");
>>> if( !f1 ) return;
>>> finally_execute close_file1 = make_fin_exec( boost::bind(fclose, f1) );
>>>
>>> FILE* f2 = fopen( "file2.bin", "rb");
>>> if( !f2 ) return;
>>> finally_execute close_file1 = make_fin_exec( boost::bind(fclose, f4) );
>>>
>>> FILE* f3 = fopen( "file3.bin", "rb");
>>> if( !f3 ) return;
>>> finally_execute close_file1 = make_fin_exec( boost::bind(fclose, f4) );
>>>
>>> FILE* f4 = fopen( "file4.bin", "rb");
>>> if( !f4 ) return;
>>> finally_execute close_file1 = make_fin_exec( boost::bind(fclose, f4) );
>>> }
>>>
>>> how it will look with ScopeExit?
>>
>> If you are willing to use macros (as ScopeExit does), maybe something like this:
>>
>> #define EXIT_CLOSE(f) \
>> BOOST_SCOPE_EXIT(&f) { \
>> fclose(f); \
>> } BOOST_SCOPE_EXIT_END
>>
>> void foo()
>> {
>> FILE* f1 = fopen( "file1.bin", "rb");
>> if( !f1 ) return;
>> EXIT_CLOSE(f1)
>>
>> FILE* f2 = fopen( "file2.bin", "rb");
>> if( !f2 ) return;
>> EXIT_CLOSE(f2)
>>
>> FILE* f3 = fopen( "file3.bin", "rb");
>> if( !f3 ) return;
>> EXIT_CLOSE(f3)
>>
>> FILE* f4 = fopen( "file4.bin", "rb");
>> if( !f4 ) return;
>> EXIT_CLOSE(f4)
>> }
>>
>>> what will be more clear?
>>
>> That depends on your application domain, maybe your developer's tema,
>> etc so it's your call.
> ok, let's use example more closer to real life:
>
> bool MoveFile(const char* from, const char* to)
> {
>
> FILE* f1 = fopen( from, "rb");
> if( !f1 ) return false;
> finally_execute close_file1 = make_fin_exec( boost::bind(fclose, f1) );
>
> FILE* f2 = fopen( to, "wb");
> if( !f2 ) return false;
> finally_execute close_file2 = make_fin_exec( boost::bind(fclose, f2) );
>
> finally_execute remove_file1 = make_fin_exec( boost::bind(remove, from) );
> finally_execute remove_file2 = make_fin_exec( boost::bind(remove, to) );
>
> /*do real move*/
>
> if( was_error ) {
> remove_file1.dismiss();
> return false;
> }
> else {
> remove_file2.dismiss();
> return true;
> }
>
> }

Maybe something like this but I couldn't fully follow the example
because I don't understand where was_error comes from (anyway,
not_error below would come from the same place...):

 #define EXIT_CALL_IF(cond, op, var) \
     BOOST_SCOPE_EXIT(&cond, &var) { \
         if(cond) op(var);
     } BOOST_SCOPE_EXIT_END

 bool MoveFile(const char* from, const char* to)
 {
    bool const true_ = true;

    FILE* f1 = fopen( from, "rb");
    if( !f1 ) return false;
    EXIT_CALL_IF(true_, fclase, f1)

    FILE* f2 = fopen( to, "wb");
    if( !f2 ) return false;
    EXIT_CALL_IF(true_, fclase, f2)

    EXIT_CALL_IF(was_error, remove, from)
    EXIT_CALL_IF(not_error, remove, to)

    /*do real move*/

    return not_error = !was_error;
 }

--Lorenzo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk