Request for review: scope_exit

I uploaded version 0.01 of scope_exit. It doesn't have tests and a documentation yet (only README.scope_exit) but I'm pretty sure I'll finish them before the review. The library can be downloaded from http://tinyurl.com/y58k33 I attached README.scope_exit. Please review it. There are some problems that may be interested to other people. One related to boost.typeof and another to faster mt code (spin-locks, lock-free whatever). -- Alexander Nasonov http://nasonov.blogspot.com The will of the people is the only legitimate foundation of any government, and to protect its free expression should be our first object. Thomas Jefferson This quote is generated by: /usr/pkg/bin/curl -L http://tinyurl.com/veusy \ | sed -e 's/^document\.write(.//' -e 's/.);$//' \ -e 's/<[^>]*>//g' -e 's/^More quotes from //' \ | fmt | tee ~/.signature-quote

Perhaps I'm missing something (in which case it might help if you stated your goals more clearly), but I'm not sure it needs to be quite so complicated. Here's what I've done, in the past. First, a usage example: glob_t result_glob; BOOST_FINALLY__BEGIN { globfree( &result_glob ); } BOOST_FINALLY__END int err = glob( pattern.c_str(), flags_glob, NULL, &result_glob ); if (err == 0) { ... return result; } else throw CGlobError( "Glob error" ); // Whether we exited via throw or return, globfree() was // called, avoiding a possible memory leak. The implementation is simply: #define BOOST_FINALLY__BEGIN BOOST_FINALLY__BEGIN__INTERNAL1( __LINE__ ) #define BOOST_FINALLY__BEGIN__INTERNAL1( uniq ) BOOST_FINALLY__BEGIN__INTERNAL2( uniq ) #define BOOST_FINALLY__BEGIN__INTERNAL2( uniq ) \ struct boost_finally_##uniq \ { \ ~boost_finally_##uniq() \ { //! Ends an BOOST_FINALLY block (see BOOST_FINALLY__BEGIN, for details). #define BOOST_FINALLY__END BOOST_FINALLY__END__INTERNAL1( __LINE__ ) #define BOOST_FINALLY__END__INTERNAL1( uniq ) BOOST_FINALLY__END__INTERNAL2( uniq ) #define BOOST_FINALLY__END__INTERNAL2( uniq ) \ } \ } boost_finally_inst_##uniq UNUSED; Where UNUSED is defined as (for gcc): #define UNUSED __attribute__ ((__unused__)) And the explanation: The basic idea is to use the destructor of an instance of a local struct as the mechanism through which the "at scope exit" code gets invoked. In order to facilitate multiple such blocks in the same scope, type and variable names are made unique by appending the source file line number to both names. To avoid compiler warnings that these variables are unused, GCC's 'unused' attribute is applied. For simplicity, I just used my own definition, here. I should note that this was designed for use in functions, only. Also, there are some cases in which I've found this unsatisfactory, and have defined my own scoped_ptr-inspired templates that, upon destruction, call a user-specified function. Matt Alexander Nasonov wrote:
I uploaded version 0.01 of scope_exit. It doesn't have tests and a documentation yet (only README.scope_exit) but I'm pretty sure I'll finish them before the review.
The library can be downloaded from http://tinyurl.com/y58k33 I attached README.scope_exit. Please review it. There are some problems that may be interested to other people. One related to boost.typeof and another to faster mt code (spin-locks, lock-free whatever).
------------------------------------------------------------------------
Boost Candiate library scope_exit, version 0.01
Overview
The library provides BOOST_SCOPE_EXIT macro similar to D's scope(exit) feature [D-ScopeBlock].
Syntax
{ BOOST_SCOPE_EXIT ( scope-exit-arg-pp-seq ) scope-exit-body } direct-declarator;
scope-exit-body: function-body function-try-block
The scope-exit-arg-pp-seq is Boot.Preprocessor sequence of identifiers that can be used inside scope-exit-body. The direct-declarator is declarator-id followed by optional argument of type boost::scope_exit_group (inside brackets or after the equal sign).
This construct executes scope-exit-body at the close of the current scope.
For example, the following code prints classical "Hello, World":
{ std::string hello("Hello"), world("World");
{ BOOST_SCOPE_EXIT( (hello)(world) ) { std::cout << hello << ", " << world << "!\n"; }} _;
// other code ... } // ^ "Hello, World\n" is printed at this point
Note that the _ is a direct-declarator. Similar to Prolog or Haskell, it means that we don't care about a name of this variable. Unlike those languages, if we had two scope(exit) blocks in one scope, we would have to use a different name for the second block.
The code above relies on non-standard __thread or __declspec(thread) specifiers. To get rid of these, you can pass scope_exit_group to a block:
boost::scope_exit_group se_group;
{ BOOST_SCOPE_EXIT( (se_group)(hello)(world) ) { std::cout << hello << ", " << world << "!\n"; }} se_block(se_group);
The se_group should be passed to scope-exit-arg-pp-seq AND as an initializer to se_block. Copy initialization is acceptable too (that is, se_block = se_group). More than one block can be associated with one scope_exit_group object. Execution of all associated blocks can be canceled with scope_exit_group::cancel() function. The scope_exit_group object can be declared const, in this case cancel can't be called.
The D language has also scope(failure) and scope(success) but std::uncaught_exception cannot be used to detect a failure or a success.
Testing
It is recommended to use a compiler with native typeof. Emulation is tested successfully on VC 7.1, VC 8.0 and Intel for Linux 8.1.038. Gcc 3.4.6 doesn't compile ./libs/scope_exit/example/all.cpp in emulation mode unless all function templates are commented out. Intel for Linux 8.1.038 and gcc 3.4.6 compile all examples in native mode.
References
[D-ScopeBlock] http://www.digitalmars.com/d/statement.html#ScopeGuardStatement
Problems
1. gcc 3.4 doesn't compile this code
template<class T> void foo(T const& t) { struct X { typedef __typeof__(t) type; }; typedef X::type type; } int main() { foo(0); }
This can be fixed by adding typename before X::type. Until it gets fixed, gcc 3.4 users should use BOOST_SCOPE_EXIT_TMPL in function templates.
2. Emulation is dependent context is not supported. Probably it's not worth having BOOST_SCOPE_EXIT_TPL and wait for typeof acceptance by the standard.
3. Use spin-locks instead of OS locking primitives when BOOST_SCOPE_EXIT_NO_THREAD_STATIC_KEYWORD is defined.
4. Improve pthreads and Win32 code fragments.
Files
./README.scope_exit ./boost/scope_exit.hpp ./libs/scope_exit/example/all.cpp ./libs/scope_exit/example/hello.cpp
------------------------------------------------------------------------
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Matt Gruenke wrote:
First, a usage example:
glob_t result_glob;
BOOST_FINALLY__BEGIN { globfree( &result_glob ); } BOOST_FINALLY__END
BEGIN/END variant of BOOST_SCOPE_EXIT exits but I like it less because POSSIBLY SMALL code IS SURROUNDED by capital letters: BOOST_SCOPE_EXIT_BEGIN( (result_glob) ) { globfree( &result_glob ); } BOOST_SCOPE_EXIT_END; It's strange that your code compiles because $ tail -n 8 finally.cpp int main() { int a, b; BOOST_FINALLY__BEGIN { a + b; } BOOST_FINALLY__END } $ g++ finally.cpp finally.cpp: In destructor `main()::boost_finally_25::~boost_finally_25()': finally.cpp:26: error: use of `auto' variable from containing function finally.cpp:24: error: `int a' declared here finally.cpp:26: error: use of `auto' variable from containing function finally.cpp:24: error: `int b' declared here So, you can use only global or static variables inside the block.
The basic idea is to use the destructor of an instance of a local struct as the mechanism through which the "at scope exit" code gets invoked. In order to facilitate multiple such blocks in the same scope, type and variable names are made unique by appending the source file line number to both names.
SCOPE_EXIT is based on the same idea.
I should note that this was designed for use in functions, only. Also, there are some cases in which I've found this unsatisfactory, ...
Can you be more specific about these? -- Alexander Nasonov http://nasonov.blogspot.com Take away love and our earth is a tomb. Robert Browning This quote is generated by: /usr/pkg/bin/curl -L http://tinyurl.com/veusy \ | sed -e 's/^document\.write(.//' -e 's/.);$//' \ -e 's/<[^>]*>//g' -e 's/^More quotes from //' \ | fmt | tee ~/.signature-quote

FWIW, another interface could be a class like : // untested class scope_guard { public: typedef boost::function<void()> func_type; explicit scope_guard() { } explicit scope_guard(const func_type& f) { add_function(f); } ~scope_guard() { for(int i = 0; i < m_functions.size(); ++i) m_functions[i](); } void add_function(const func_type& f) { m_functions.push_back(f); } // Could be used to cancel something void clear() { m_functions.clear(); } private: std::vector<func_type> m_functions; }; This, used along with boost::bind would make your example become smth like : glob_t result_glob; scope_guard guard(boost::bind(&globfree, &result_glob)); /* throw or return here */ And you could add more than one function call to do with the add_function() member... of course it's more limited than what your interface offers, but I think the idea is worth mentionning. Credits goes to Eelis (irc) for the idea. Philippe

Philippe Vaucher wrote:
FWIW, another interface could be a class like :
// untested class scope_guard ...
See also <boost/multi_index/detail/scope_guard.hpp>.
This, used along with boost::bind would make your example become smth like :
glob_t result_glob; scope_guard guard(boost::bind(&globfree, &result_glob)); /* throw or return here */
Try to construct a guard for more complex case: void foo(std::map<char,int>& codes, char symbol, int code) { bool cancel = codes.insert(std::make_pair(symbol, code)).second; { BOOST_SCOPE_EXIT( (cancel)(codes)(symbol) ) { if(cancel) codes.erase(symbol); }} _; // ... cancel = false; // All is fine at this point, commit. } Problem 1: if. You should use boost::lambda. Problem 2: overloaded erase. I don't think that many developers will wrap erase. They need straitforward solution that doesn't require opening lambda docs, looking for _1->* or bind constructs, trying to figure out why _1->* &std::map<char,int>::erase doesn't work and so on. Problem 3: you can set breakpoint at codes.erase(symbol) line but how do you set it in scope_guard (conditional breakpoint in ~scope_guard with this == &guard stops even if cancel == false)? -- Alexander Nasonov http://nasonov.blogspot.com Float like a butterfly, sting like a bee. Muhammad Ali This quote is generated by: /usr/pkg/bin/curl -L http://tinyurl.com/veusy \ | sed -e 's/^document\.write(.//' -e 's/.);$//' \ -e 's/<[^>]*>//g' -e 's/^More quotes from //' \ | fmt | tee ~/.signature-quote
participants (3)
-
Alexander Nasonov
-
Matt Gruenke
-
Philippe Vaucher