Boost logo

Boost :

Subject: [boost] D-style scope guards?
From: Martin Christiansson (martin.christiansson_at_[hidden])
Date: 2010-09-17 03:06:05


Hi,

I have used the good old scopeguard in the past and recently done some experiments with the Boost.ScopeExit library. Still I feel that the clean scope statements in D is missing.

It will not be possible to do an equally clean solution without having the same features in the language itself, but I could reach as far as this example that reuses the ScopeExit library:

void foo()
{ GUARDED_SCOPE_BEGIN
    
    GUARDED_SCOPE_EXIT() {
        cout << "Returning from foo()" << endl;
    } GUARDED_SCOPE_EXIT_END

    GUARDED_SCOPE_SUCCESS() {
        cout << "foo(): No Errors occurred!" << endl;
    } GUARDED_SCOPE_SUCCESS_END

    GUARDED_SCOPE_FAILURE() {
        cout << "foo(): Exception caught!" << endl;
    } GUARDED_SCOPE_FAILURE_END
    
    GUARDED_SCOPE_END
}

The GUARDED_SCOPE_BEGIN macro declares one boolean guard to handle return from several levels of nested scopes and one boolean to guard the current scope from exceptions (will be shadowed in nested scopes).
GUARDED_SCOPE_END will set the exception_thrown guard to false before that variable is used by the GUARDED_SCOPE_*() destructors. The latter is actually reused ScopeExit code with an added wrapper with a single if-statement.
GUARDED_SCOPE_EXIT() will allways run on scope exit. GUARDED_SCOPE_SUCCESS() will only run if the function is exited with wrapper for return keyword (GUARDED_SCOPE_RETURN). The wrapper must be used since it updates the hidden guard variables. Using plain "return" will behave as if exception was thrown.
GUARDED_SCOPE_FAILURE() will run when exception is thrown.

There is also a separate GUARDED_SCOPE_NESTED_BEGIN that is supposed to be used for all nested scopes. A reference is kept to the return_called variable in the top-level-scope (variable declared by GUARDED_SCOPE_BEGIN).

The biggest issue here is that any extra closing curly bracket inserted between GUARDED_SCOPE_*-definitions and GUARDED_SCOPE_END will cause destructors to run before update of guard variable. Could happen if someone thinks it "looks better" without understanding how stuff works. That kind of error will behave as if exception was thrown.

Is this something to investigate further? Messy or potentially useful? Suggestions for improvements?
I've appended the contents of my small test program at the end of this email.
Can be tested by inserting GUARDED_SCOPE_RETURN() and "throw exception();".

Regards,
/Martin

-----------

#include <iostream>
#include <exception>

#include <boost/scope_exit.hpp>

using namespace std;

#define GUARDED_SCOPE_BEGIN bool return_called = false; { bool& return_called_ref = return_called; bool exception_thrown = true;
#define GUARDED_SCOPE_NESTED_BEGIN { bool& return_called_ref = return_called; bool exception_thrown = true;
#define GUARDED_SCOPE_END exception_thrown = false; (void) return_called_ref; }

#define GUARDED_SCOPE_RETURN(rc) { return_called_ref = true; return rc; }

#define GUARDED_SCOPE_EXIT(Seq) BOOST_SCOPE_EXIT(Seq)
#define GUARDED_SCOPE_EXIT_END BOOST_SCOPE_EXIT_END

#define GUARDED_SCOPE_SUCCESS(Seq) BOOST_SCOPE_EXIT( (&return_called_ref) (&exception_thrown) Seq) { if(return_called_ref || !exception_thrown)
#define GUARDED_SCOPE_SUCCESS_END } BOOST_SCOPE_EXIT_END

#define GUARDED_SCOPE_FAILURE(Seq) BOOST_SCOPE_EXIT( (&return_called_ref) (&exception_thrown) Seq) { if(!return_called_ref && exception_thrown)
#define GUARDED_SCOPE_FAILURE_END } BOOST_SCOPE_EXIT_END

void foo()
{ GUARDED_SCOPE_BEGIN
    
    GUARDED_SCOPE_EXIT() {
        cout << "Returning from foo()" << endl;
    } GUARDED_SCOPE_EXIT_END

    GUARDED_SCOPE_SUCCESS() {
        cout << "foo(): No Errors occurred!" << endl;
    } GUARDED_SCOPE_SUCCESS_END

    GUARDED_SCOPE_FAILURE() {
        cout << "foo(): Exception caught!" << endl;
    } GUARDED_SCOPE_FAILURE_END
    
    do { GUARDED_SCOPE_NESTED_BEGIN

        GUARDED_SCOPE_FAILURE() {
            cout << "Inner scope: Exception caught!" << endl;
        } GUARDED_SCOPE_FAILURE_END

        GUARDED_SCOPE_SUCCESS() {
            cout << "Inner scope: No Errors occurred!" << endl;
        } GUARDED_SCOPE_SUCCESS_END

        GUARDED_SCOPE_END
    } while(0);

    GUARDED_SCOPE_END
}

int main() {

  try {
    foo();
  } catch(exception& e) {
    cout << "main(): Caught exception: " << e.what() << endl;
    return -1;
  }

    cout << "main(): No Errors occurred!" << endl;
    return 0;
}


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