any interest in BOOST_FINALLY?

Is there an interest to write something like this int i = 2, j = 1; BOOST_FINALLY_BEGIN( (i)(j) ) { std::cerr << i + j << '\n'; } BOOST_FINALLY_END in a code to make sure that a code inside finally block is always executed when leaving a scope? Code is here: http://files.rsdn.ru/21403/finally.cpp Note that it depends on typeof. Preprocessed output is below: // Not thread-safe yet void* _finally_last_params = 0; int main() { int i = 2, j = 1; typedef typeof(i) _finally_typeof_46i; typedef typeof(j) _finally_typeof_46j; struct _finally_params_46 { _finally_typeof_46i & i ; _finally_typeof_46j & j ; } _finally_params_46 = { i, j }; _finally_last_params = &_finally_params_46; struct _finally_guard_46 { void* _finally_params; ~_finally_guard_46() { typedef struct _finally_params_46 params; params* p= static_cast<params*>(_finally_params); _finally_body(p -> i , p -> j , 0); } static void _finally_body( _finally_typeof_46i & i , _finally_typeof_46j & j , int) { // USER CODE HERE std::cerr << i + j << '\n'; } } _finally_guard_48 = { _finally_last_params }; } -- Alexander

On 10/11/05, Alexander Nasonov <alnsn-boost@yandex.ru> wrote:
Is there an interest to write something like this
int i = 2, j = 1;
BOOST_FINALLY_BEGIN( (i)(j) ) { std::cerr << i + j << '\n'; } BOOST_FINALLY_END
in a code to make sure that a code inside finally block is always executed when leaving a scope?
Personally, I prefer the ScopeGuard-styled version. I have code that makes the following type of code work, if there's interest: int i = 2, j = 1; BOOST_FINALLY( std::cerr << ( boost::lambda::var(i) + boost::lambda::var(j) ) << '\n' ); The idea being that the macro simply expands to a line-number-dependant identifier of a ScopeGuard-style type ( http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/alexandr.htm ) which, since it can never be dismissed, always calls the functor. The code itself is quite short, since it assumes that bind, lambda, or similar would be used to create the functor. With function<void(void)> it would be incredibly trivial, but, as described in the above article, the trick used avoids some otherwise-necessary virtual function calls. Also, I think it would be best if your code wrapped the code in the destructor in a try block to eat all exceptions thrown. ( Although your version does allow the user to write his own try block around the code directly, so it's not totally necessary. Of course, this could be done in the ScopeGuard-style version as well by providing a functor adaptor that eats exceptions, were it decided that always having the try block is unacceptable. ) Personally, I think that the exception-eating version should be default, in the interest of safety. Good compilers ought to remove them when they're obviously unnessesary anyways. Of course, the finally block is only really useful if the operation is nofail, but... - Scott McMurray

Scott McMurray writes:
Personally, I prefer the ScopeGuard-styled version. I have code that makes the following type of code work, if there's interest: int i = 2, j = 1; BOOST_FINALLY( std::cerr << ( boost::lambda::var(i) + boost::lambda::var(j) ) << '\n' );
How do you implement more complex finally block? FILE* files[10] = {}; BOOST_FINALLY_BEGIN( (files) ) { for(int i = 0; i < 10; ++i) if(files[i]) ::fclose(files[i]); } BOOST_FINALLY_END for(int i = 0; i < 10; ++i) files[i] = ::fopen(get_nth_filename(i), "r"); [skiped]
Also, I think it would be best if your code wrapped the code in the destructor in a try block to eat all exceptions thrown. ( Although your version does allow the user to write his own try block around the code directly, so it's not totally necessary. Of course, this could be done in the ScopeGuard-style version as well by providing a functor adaptor that eats exceptions, were it decided that always having the try block is unacceptable. ) Personally, I think that the exception-eating version should be default, in the interest of safety.
I prefer not hiding catch(...) from a user. BTW, I can modify the code to allow throw-spec before open brace BOOST_FINALLY_BEGIN( (files) ) throw() { for(int i = 0; i < 10; ++i) if(files[i]) ::fclose(files[i]); } BOOST_FINALLY_END Also, there is also function-try-block in C++: BOOST_FINALLY_BEGIN( (files) ) try { for(int i = 0; i < 10; ++i) if(files[i]) ::fclose(files[i]); } catch(...) {} BOOST_FINALLY_END -- Alexander

Alexander Nasonov <alnsn-boost@yandex.ru> writes:
How do you implement more complex finally block?
FILE* files[10] = {};
BOOST_FINALLY_BEGIN( (files) ) { for(int i = 0; i < 10; ++i) if(files[i]) ::fclose(files[i]); } BOOST_FINALLY_END
for(int i = 0; i < 10; ++i) files[i] = ::fopen(get_nth_filename(i), "r");
Boost::lambda has for loops, neh?
I prefer not hiding catch(...) from a user.
Practically speaking, destructors are much better than catch(...) for finally-like operations on many platforms. Several of them have a similar problem to Windows, where catch(...) catches asynchronous exceptions.
BTW, I can modify the code to allow throw-spec before open brace
BOOST_FINALLY_BEGIN( (files) ) throw() { for(int i = 0; i < 10; ++i) if(files[i]) ::fclose(files[i]); } BOOST_FINALLY_END
Also, there is also function-try-block in C++:
BOOST_FINALLY_BEGIN( (files) ) try { for(int i = 0; i < 10; ++i) if(files[i]) ::fclose(files[i]); } catch(...) {} BOOST_FINALLY_END
I don't know what your plans for using a function-try-block are, but AFAICT that construct is only useful for exception translation during data member initialization. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On 10/11/05, David Abrahams <dave@boost-consulting.com> wrote:
Boost::lambda has for loops, neh?
Exactly. ( http://boost.org/doc/html/lambda/le_in_details.html#lambda.lambda_expression... for more details ) That being said, would it be a wise design choice to need large amounts of code in a FINALLY block?
Practically speaking, destructors are much better than catch(...) for finally-like operations on many platforms. Several of them have a similar problem to Windows, where catch(...) catches asynchronous exceptions.
I meant using them inside the destructor call to prevent any exceptions from escaping the destructor. And lambda continues to impress--I hadn't realised that lambda already has the try_catch functor magic ( that I mentioned above ) already implemented =) - Scott McMurray

me22 <me22.ca <at> gmail.com> writes:
On 10/11/05, David Abrahams <dave <at> boost-consulting.com> wrote:
Boost::lambda has for loops, neh?
Exactly. ( http://boost.org/doc/html/lambda/le_in_details.html#lambda.
lambda_expressions_for_control_structures
for more details )
I'm still waiting for a demonstration of its use. Then I can say: Now, set a breakpoint at line N (I can't tell exact number without and example)
That being said, would it be a wise design choice to need large amounts of code in a FINALLY block?
It should be executed anyway. You have a choice to put it in a function or in several functions (dtor is a function too). In an ideal world, all objects do cleanup themself and you simply write exception-neutral code. And you don't need finally blocks at all. -- Alexander

David Abrahams <dave <at> boost-consulting.com> writes:
Alexander Nasonov writes:
FILE* files[10] = {};
BOOST_FINALLY_BEGIN( (files) ) { for(int i = 0; i < 10; ++i) if(files[i]) ::fclose(files[i]); } BOOST_FINALLY_END
Boost::lambda has for loops, neh?
Can someone rewrite my example using lambda and post it here?
I prefer not hiding catch(...) from a user.
Practically speaking, destructors are much better than catch(...) for finally-like operations on many platforms. Several of them have a similar problem to Windows, where catch(...) catches asynchronous exceptions.
We were talking about catch(...) inside a destructor. How can you compare which one is better if both are in use? Scott McMurray wrote:
Also, I think it would be best if your code wrapped the code in the destructor in a try block to eat all exceptions thrown.
I don't know what your plans for using a function-try-block are, but AFAICT that construct is only useful for exception translation during data member initialization. It's only to eliminate extra {} around try/catch block. If I throw away a requirement for throw-spec after BOOST_FINALLY_BEGIN, I can add these additional {} to BOOST_FINALLY_BEGIN/BOOST_FINALLY_END macros. Then function-try-block would become try-block. -- Alexander

Alexander Nasonov <alnsn-boost@yandex.ru> writes:
I prefer not hiding catch(...) from a user.
Practically speaking, destructors are much better than catch(...) for finally-like operations on many platforms. Several of them have a similar problem to Windows, where catch(...) catches asynchronous exceptions.
We were talking about catch(...) inside a destructor. How can you compare which one is better if both are in use?
Oh, I didn't realize that. Thanks. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Alexander Nasonov"
Is there an interest to write something like this
int i = 2, j = 1;
BOOST_FINALLY_BEGIN( (i)(j) ) { std::cerr << i + j << '\n'; } BOOST_FINALLY_END
Yes. While Scope Guard with lambda could be used in for simpler examples for more complex situations your solution looks better. Is compilation time bearable when your tool is used extensively? /Pavel
participants (4)
-
Alexander Nasonov
-
David Abrahams
-
me22
-
Pavel Vozenilek