
Hello. On 1-Apr-07, at 5:38 PM, Alexander Nasonov wrote:
Hans Larsen wrote:
Hello,
Better is such a big word. You still have to say in which way it is better; readability, efficiency, simplicity, something else? ;-) Readability and simplicity. Compare
std::FILE* f = NULL;
try { f = std::fopen("/etc/passwd", "r"); // ... } finally { if(f) std::fclose(f); }
with
std::FILE* f = std::fopen("/etc/passwd", "r"); scope(exit) { if(f) std::fclose(f); }
// ...
Even without D's scope(exit) syntax it looks appealing
std::FILE* f = std::fopen("/etc/passwd", "r"); BOOST_SCOPE_EXIT(f, (f)) { if(f) std::fclose(f); } BOOST_SCOPE_EXIT_END(f)
Better than finally, yes. I thought you were saying better than my solution ;-)
I admit my solution is not perfect, but it does have certain advantages...
First, you seem to use boost/typeof/typeof.hpp, which I failed to find in the boost version I currently use (stable 1.33.1). Check it out from cvs or wait for 1.34.
Yes, I figured that out :) I'll need to keep a separate copy of those if it is really needed...
I didn't find the documentation in the full doc, only the BoostBook. It's not in a distribution but you can generate it or read online at http://194.6.223.221/~nasonov/scope_exit-0.02/libs/scope_exit/doc/ html/
Why do you need that library and is it absolutely necessary? Because I saw so much exception unaware code.
I don't see any case of valid exceptions in the use of this library. Please point one.
I don't think it should from the concepts necessary to put the thing together. All you need, after all, is a destructor to call the code, and some variable passing (Boost.Bind is very wonderful).
Bost.Bind and Boost.Lambda are very useful but it's hard to compete with a plain old C++ code. Try to express the 'if(f) std::fclose(f)', for example.
With my code? finally f; f( if_then( ref(f), bind(std::fclose, ref(f)) ) ); I admit this solution is less readable than yours, but it's still possible using bind.
Also, I'm not a big fan of macros, even more when using IDs in them, I think they reduce readability. Me too. But this macro can significantly increase a quality of code.
I prefer compilers to pre- compilers, if you know what i mean. In your case, adding a scope inside the SCOPE_XXX makes for more indentation, which is not necessarily A Good Thingie. SCOPE_EXIT, like _if_ or _while_, controls an execution of a code block.
I suggest at least putting those brackets inside the SCOPE_XXX defines. This is all, of course, a matter of taste. I personally like brackets because they make a small chuck of code surrounded by BIG MACROS more visible. It's also easier to jump between brackets (% in vim) and go to the opening bracket([{ in vim). But if many people don't like brackets, it's a trivial change ...
Agreed. Although I don't normally use vim, I know it can be useful (although you could script it ;-).
BTW, what happens if I typo the ID or just use the same in two different places (either in the same scope or inlined scopes).
Typo:
hello.cpp: In function `int main()': hello.cpp:18: error: `boost_scope_exit_struct_typo' was not declared in this sco pe hello.cpp:18: error: expected `;' before "const" hello.cpp:18: error: `boost_scope_exit_typo' was not declared in this scope
Duplicate id:
real_world.cpp:134: error: redefinition of `struct World::addPerson (const Person &)::rollback_boost_scope_exit_nested_typeofold_id' real_world.cpp:121: error: previous definition of `struct World::addPerson(const Person&)::rollback_boost_scope_exit_nested_typeofold_id'
Believe me, I don't like id argument either but I couldn't come up with a better solution.
Why not put the code inside the macro? Example (same as your hello world): #define MY_BOOST_EXIT( w, x, y ) BOOST_SCOPE_EXIT(w, x) \ y \ BOOST_SCOPE_EXIT_END(w) int main() { try { std::string hello("Hello"), world("World"); MY_BOOST_EXIT( _id, (hello)(world), { std::clog << world << ", " << hello << "!\n"; } ) // other code } catch(...) {} /* ^ "World, Hello\n" is printed at this point */ } This way the id is used once. I'm trying to figure out a way to not use the id at all (when constructing local structs). If I find something I'll let you know :)
My solution is CopyConstructable. Many things become interesting when you can copy a chain of commands. It is also bidirectionnal. With a flag at construction, we could invert the order of execution. We could also reorder it in the middle (cannot think of a real use for this, though).
BTW, did you check my code? You never told me what you thought of it... I looked at it quickly and noticed dynamic allocations.
This code
template< class T > void operator()( const T& t ) { vec.push_back( new helper_t<T>(t) ); }
is not exception aware.
You can rewrite it like this: std::auto_ptr< helper_t<T> > p(new helper_t<T>(t) ); vec.push_back( p.get() ); p.release();
mmmh... I fail to see the difference. There is really two cases where exceptions may occur: - operator new( size_t ) throws an exception (Out-Of-Memory). No memory is allocated. - push_back reallocs the vector and operator new( size_t ) throws an exception (Out-Of-Memory). Strong guarantee occurs... (?) Otherwise, my constructor will not throw an exception (by design). If I forget something, point it out to me. I really fail to see how your code is more exception aware than mine, but I'm no exception expert. Have a nice day, Hans
-- Alexander Nasonov http://nasonov.blogspot.com
To be pleased with one`s limits is a wretched state. -- Johann Wolfgang von Goethe --
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 _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/ listinfo.cgi/boost