Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r50718 - in trunk/libs/scope_exit: doc example
From: Alexander.Nasonov_at_[hidden]
Date: 2009-01-21 18:51:17


Author: nasonov
Date: 2009-01-21 18:51:17 EST (Wed, 21 Jan 2009)
New Revision: 50718
URL: http://svn.boost.org/trac/boost/changeset/50718

Log:
ScopeExit documentation.
Added:
   trunk/libs/scope_exit/doc/
   trunk/libs/scope_exit/doc/Jamfile.v2 (contents, props changed)
   trunk/libs/scope_exit/doc/scope_exit.qbk (contents, props changed)
   trunk/libs/scope_exit/example/
   trunk/libs/scope_exit/example/world.cpp (contents, props changed)

Added: trunk/libs/scope_exit/doc/Jamfile.v2
==============================================================================
--- (empty file)
+++ trunk/libs/scope_exit/doc/Jamfile.v2 2009-01-21 18:51:17 EST (Wed, 21 Jan 2009)
@@ -0,0 +1,8 @@
+
+# Copyright 2006 Alexander Nasonov.
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+using quickbook ;
+
+boostbook standalone : scope_exit.qbk ;

Added: trunk/libs/scope_exit/doc/scope_exit.qbk
==============================================================================
--- (empty file)
+++ trunk/libs/scope_exit/doc/scope_exit.qbk 2009-01-21 18:51:17 EST (Wed, 21 Jan 2009)
@@ -0,0 +1,475 @@
+[library Boost.ScopeExit
+ [copyright 2006-2009 Alexander Nasonov]
+ [purpose execute arbitrary code at scope exit]
+ [license
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ <ulink url="http://www.boost.org/LICENSE_1_0.txt">
+ http://www.boost.org/LICENSE_1_0.txt
+ </ulink>)
+ ]
+ [authors [Nasonov, Alexander]]
+ [category utility]
+ [id scope_exit]
+ [dirname scope_exit]
+]
+
+[/ Images ]
+
+[def _note_ [$images/note.png]]
+
+[/ Links ]
+
+[def _scope_exit_ [@index.html ScopeExit]]
+[def _Tutorial_ [@scope_exit/tutorial.html Tutorial]]
+[def _Reference_ [@scope_exit/ref.html Reference]]
+[def _lambda_ [@../../libs/lambda/index.html Boost.Lambda]]
+[def _typeof_ [@../../libs/typeof/index.html Boost.Typeof]]
+[def _typeof_emulation_ [@../../libs/typeof/index.html typeof emulation]]
+[def _typeof_REGISTER_TYPE_ [@../../libs/typeof/refe.html#typeof.regtype REGISTER_TYPE]]
+[def _typeof_REGISTER_TEMPLATE_ [@../../libs/typeof/refe.html#typeof.regtemp REGISTER_TEMPLATE]]
+[def _pp_ [@../../libs/preprocessor/index.html Boost.Preprocessor]]
+[def _pp_seq_ [@../../libs/preprocessor/index.html Boost.Preprocessor sequence]]
+[def _ptr_container_ [@../../libs/ptr_container/doc/ptr_container.html Boost Pointer Container Library]]
+[def _multi_index_ [@../../libs/multi_index/doc/index.html Boost Multi-Index Containers Library]]
+[def _scope_guard_ [@http://www.ddj.com/dept/cpp/184403758 ScopeGuard]]
+[def _D_ [@http://www.digitalmars.com/d/index.html D]]
+[def _D_scope_exit_ [@http://www.digitalmars.com/d/statement.html#ScopeGuardStatement scope(exit)]]
+[def _D_scope_failure_ [@http://www.digitalmars.com/d/statement.html#ScopeGuardStatement scope(failure)]]
+[def _D_scope_success_ [@http://www.digitalmars.com/d/statement.html#ScopeGuardStatement scope(success)]]
+[def _RAII_ [@http://www.research.att.com/~bs/glossary.html#Gresource-acquisition-is-initialization RAII]]
+[def _strong_guarantee_ [@http://www.research.att.com/~bs/glossary.html#Gstrong-guarantee strong guarantee]]
+
+[section:intro Introduction]
+
+Nowadays, every C++ developer is familiar with _RAII_ technique.
+It binds resource acquisition and release to initialization and
+destruction of a variable that holds the resource. But there are
+times when writing a special class for such variable is not worth
+the effort.
+
+This is when _scope_exit_ macro comes into play. You put resource
+acquisition directly in your code and next to it you write a code
+that releases the resource.
+
+Read _Tutorial_ to find out how to write programs with
+_scope_exit_ or jump straight to the _Reference_ section.
+
+[endsect]
+
+[section:tutorial Tutorial]
+
+Imagine that you want to make many modifications to data members
+of the `World` class in the `World::addPerson` function.
+You start with adding a new `Person` object to a vector of persons:
+
+ void World::addPerson(Person const& person) {
+ bool commit = false;
+ m_persons.push_back(person); // (1) direct action
+
+Some operation down the road may throw an exception and all changes
+to involved objects should be rolled back. This all-or-nothing semantic
+is also known as _strong_guarantee_.
+
+In particular, last added person must be deleted from `m_persons` when
+the function throws. All you need is to define a delayed action (release
+of a resource) right after the direct action (resource acquisition):
+
+ void World::addPerson(Person const& aPerson) {
+ bool commit = false;
+ m_persons.push_back(aPerson); // (1) direct action
+ BOOST_SCOPE_EXIT( (&commit)(&m_persons) )
+ {
+ if(!commit)
+ m_persons.pop_back(); // (2) rollback action
+ } BOOST_SCOPE_EXIT_END
+
+ // ... // (3) other operations
+
+ commit = true; // (4) turn all rollback actions into no-op
+ }
+
+The block below point `(1)` is a _scope_exit_ declaration.
+Unlike point `(1)`, an execution of the _scope_exit_ body will be
+delayed until the end of the current scope. In this case it will be
+executed either after point `(4)` or on any exception.
+
+The _scope_exit_ declaration starts with `BOOST_SCOPE_EXIT` macro
+invocation which accepts _pp_seq_ of captured variables. If a capture
+starts with the ampersand sign `&`, a reference to the captured variable
+will be available inside the _scope_exit_ body; otherwise, a copy of the
+variable will be made after the point `(1)` and only the copy will be
+available inside the body.
+
+In the example above, variables `commit` and `m_persons` are passed by
+reference. This is a most common case but passing a variable by value is
+sometimes useful as well.
+
+Consider a more complex case where `World::addPerson` can save intermediate
+states at some points and roll back to the last saved state. You can
+use `Person::m_evolution` to store a version of changes and increment it
+to cancel all rollback actions associated with those changes.
+
+If you pass a current value of `m_evolution` stored in the `checkpoint`
+variable by value, it will be remain unchanged until the end of scope
+and you can compare it with the final value of the `m_evolution`.
+If the latter wasn't incremented since you saved it, the rollback action
+inside the block should be executed:
+
+ void World::addPerson(Person const& aPerson) {
+ m_persons.push_back(aPerson);
+
+ // This block must be no-throw
+ Person& person = m_persons.back();
+ Person::evolution_t checkpoint = person.m_evolution;
+
+ BOOST_SCOPE_EXIT( (checkpoint)(&person)(&m_persons) )
+ {
+ if(checkpoint == person.m_evolution)
+ m_persons.pop_back();
+ } BOOST_SCOPE_EXIT_END
+
+ // ...
+
+ checkpoint = ++person.m_evolution;
+
+ // Assign new id to the person
+ World::id_t const prev_id = person.m_id;
+ person.m_id = m_next_id++;
+ BOOST_SCOPE_EXIT( (checkpoint)(&person)(&m_next_id)(prev_id) )
+ {
+ if(checkpoint == person.m_evolution) {
+ m_next_id = person.m_id;
+ person.m_id = prev_id;
+ }
+ } BOOST_SCOPE_EXIT_END
+
+ // ...
+
+ checkpoint = ++person.m_evolution;
+ }
+
+Full code listing can be found in [@../example/world.cpp world.cpp].
+
+[endsect]
+
+[section:alternatives Alternatives]
+
+[h3 try-catch]
+
+This is an example of using a badly designed `File` class. An
+instance of `File` doesn't close a file in a destructor, a programmer
+is expected to call the `close` member function explicitly.
+
+ File passwd;
+ try {
+ passwd.open("/etc/passwd");
+ // ...
+ passwd.close();
+ }
+ catch(...) {
+ log("could not get user info");
+ if(passwd.is_open())
+ passwd.close();
+ throw;
+ }
+
+Note the following:
+
+* the `passwd` object is defined outside of the `try` block because
+this object is required inside the `catch` block to close the file,
+* the `passwd` object is not fully constructed until after the `open`
+member function returns, and
+* if opening throws, the `passwd.close()` should not be called,
+hence the call to `passwd.is_open()`.
+
+_scope_exit_ doesn't have any of these problems:
+
+ try {
+ File passwd("/etc/passwd");
+ BOOST_SCOPE_EXIT( (&passwd) ) {
+ passwd.close();
+ } BOOST_SCOPE_EXIT_END
+ // ...
+ }
+ catch(...) {
+ log("could not get user info");
+ throw;
+ }
+
+[h3 RAII]
+
+_RAII_ is absolutely perfect for the `File` class introduced above.
+Use of properly designed `File` class would look like:
+
+ try {
+ File passwd("/etc/passwd");
+ // ...
+ }
+ catch(...) {
+ log("could not get user info");
+ throw;
+ }
+
+However, using RAII to build up a _strong_guarantee_ could introduce
+a lot of non-reusable _RAII_ types. For example:
+
+ m_persons.push_back(person);
+ pop_back_if_not_commit pop_back_if_not_commit_guard(commit, m_persons);
+
+The `pop_back_if_not_commit` class is either defined out of the scope or
+as a local class:
+
+ class pop_back_if_not_commit {
+ bool m_commit;
+ std::vector<Person>& m_vec;
+ // ...
+ ~pop_back_if_not_commit() {
+ if(!m_commit)
+ m_vec.pop_back();
+ }
+ };
+
+In some cases _strong_guarantee_ can be accomplished with standard utilities:
+
+ std::auto_ptr<Person> spSuperMan(new Superman);
+ m_persons.push_back(spSuperMan.get());
+ spSuperMan.release(); // m_persons successfully took ownership.
+
+or with specialized containers such as _ptr_container_ or
+_multi_index_.
+
+[h3 _scope_guard_]
+
+Imagine that you add a new currency rate:
+
+ bool commit = false;
+ std::string currency("EUR");
+ double rate = 1.3326;
+ std::map<std::string, double> rates;
+ bool currency_rate_inserted =
+ rates.insert(std::make_pair(currency, rate)).second;
+
+and then continue a transaction. If it cannot be completed, you erase
+the currency from `rates`. This is how you can do this with _scope_guard_
+and _lambda_:
+
+ using namespace boost::lambda;
+
+ ON_BLOCK_EXIT(
+ if_(currency_rate_inserted && !_1) [
+ bind(
+ static_cast<
+ std::map<std::string,double>::size_type (std::map<std::string,double>::*)(std::string const&)
+ >(&std::map<std::string,double>::erase)
+ , &rates
+ , currency
+ )
+ ]
+ , boost::cref(commit)
+ );
+
+ // ...
+
+ commit = true;
+
+Note that
+
+* Boost.lambda expressions are hard to write correctly, for example,
+overloaded function must be explicitly casted, as demonstrated in
+this example,
+* condition in `if_` expression refers to `commit` variable indirectly
+through `_1` placeholder,
+* setting a breakpoint inside `if_[ ... ]` requires in-depth knowledge
+of _lambda_ and debugging techniques.
+
+This code will look much better with native lambda expressions proposed
+for C++0x:
+
+ ON_BLOCK_EXIT(
+ [currency_rate_inserted, &commit, &rates, &currency]() -> void
+ {
+ if(currency_rate_inserted && !commit)
+ rates.erase(currency);
+ }
+ );
+
+With _scope_exit_ you can simply do
+
+ BOOST_SCOPE_EXIT( (currency_rate_inserted)(&commit)(&rates)(&currency) )
+ {
+ if(currency_rate_inserted && !commit)
+ rates.erase(currency);
+ } BOOST_SCOPE_EXIT_END
+
+ // ...
+
+ commit = true;
+
+[h3 C++0x]
+
+In future releases _scope_exit_ will take advantages of C++0x features.
+
+* Passing capture list as _pp_seq_ will be replaced with a traditional
+macro invocation style:
+ BOOST_SCOPE_EXIT(currency_rate_inserted, &commit, &rates, &currency)
+ {
+ if(currency_rate_inserted && !commit)
+ rates.erase(currency);
+ } BOOST_SCOPE_EXIT_END
+
+* `BOOST_SCOPE_EXIT_END` will be replaced with a semicolon:
+ BOOST_SCOPE_EXIT(currency_rate_inserted, &commit, &rates, &currency)
+ {
+ if(currency_rate_inserted && !commit)
+ rates.erase(currency);
+ };
+
+* Users will be able to capture local variables implicitly with lambda
+capture defaults `&` and `=`:
+ BOOST_SCOPE_EXIT(&, currency_rate_inserted)
+ {
+ if(currency_rate_inserted && !commit)
+ rates.erase(currency);
+ };
+
+* It will be possible to capture `this` pointer.
+
+[h3 The D Programming Language]
+
+_ScopeExit_ is similar to _D_scope_exit_ feature built
+into the _D_ programming language.
+
+A curious reader may notice that the library doesn't implement
+_D_scope_success_ and _D_scope_failure_ of the _D_ language.
+Unfortunately, it's not possible in C++ because failure or success
+condition cannot be determined by calling `std::uncaught_exception`.
+It's not a big problem, though. These two constructs can be
+expressed in terms of _D_scope_exit_ and a `bool commit` variable
+as explained in _Tutorial_. Refer to
+[@http://www.gotw.ca/gotw/047.htm Guru of the Week #47]
+for more details about `std::uncaught_exception` and if it has
+any good use at all.
+
+[endsect]
+
+[section:compilers Supported Compilers]
+
+The library should be usable on any compiler that supports _typeof_
+except
+
+* MSVC 7.1 and 8.0 fail to link if a function with _scope_exit_
+is included by multiple translation units.
+* GCC 3.3 can't compile _scope_exit_ inside a template. See
+[@http://lists.boost.org/Archives/boost/2007/02/116235.php this thread]
+for more details.
+
+The author tested the library on GCC 3.3, 3.4, 4.1, 4.2 and Intel 10.1.
+
+[endsect]
+
+[section:conf Configuration]
+
+Normally, no configuration is required for the library but
+note that the library depends on _typeof_ and you may want
+to configure or enforce _typeof_emulation_.
+
+[endsect]
+
+[section:ref Reference]
+
+[h3 BOOST_SCOPE_EXIT]
+
+A _scope_exit_ declaration has the following synopsis:
+
+ #include <boost/scope_exit.hpp>
+
+ BOOST_SCOPE_EXIT ( scope-exit-capture-list )
+ function-body
+ BOOST_SCOPE_EXIT_END
+
+where
+
+ scope-exit-capture-list:
+ ( scope-exit-capture )
+ scope-exit-capture-list ( scope-exit-capture )
+
+ scope-exit-capture:
+ identifier
+ &identifier
+
+The _scope_exit_ declaration schedules an execution of `scope-exit-body`
+at the end of the current scope. The `scope-exit-body` statements are
+executed in the reverse order of _scope_exit_ declarations in the given
+scope. The scope must be local.
+
+Each `identifier` in `scope-exit-capture-list` must be a valid name
+in enclosing scope and it must appear exactly once in the list.
+If a `scope-exit-capture` starts with the ampersand sign `&`, the
+corresponding `identifier` will be available inside `scope-exit-body`;
+otherwise, a copy of it will be made at the point of _scope_exit_
+declaration and that copy will be available inside `scope-exit-body`.
+
+Only identifiers listed in `scope-exit-capture-list`, static variables,
+`extern` variables and functions, and enumerations from the enclosing
+scope can be used inside the `scope-exit-body`.
+
+[note `this` pointer is not an identifier and cannot be passed to
+` scope-exit-capture-list`.]
+
+The _scope_exit_ uses _typeof_ to determine types of
+`scope-exit-capture-list` elements. In order to compile code in
+_typeof_emulation_ mode, all types should be registered with
+_typeof_REGISTER_TYPE_ or _typeof_REGISTER_TEMPLATE_ macros,
+or appropriate _typeof_ headers should be included.
+
+[h3 BOOST_SCOPE_EXIT_TPL]
+
+This macro is a workaround for various versions of gcc. These compilers
+don't compile _scope_exit_ declaration inside function templates. As a
+workaround, the `_TPL` suffix should be appended to `BOOST_SCOPE_EXIT`.
+
+The problem boils down to the following code:
+
+ template<class T> void foo(T const& t) {
+ int i = 0;
+ struct Local {
+ typedef __typeof__(i) typeof_i;
+ typedef __typeof__(t) typeof_t;
+ };
+ typedef Local::typeof_i i_type;
+ typedef Local::typeof_t t_type;
+ }
+
+ int main() { foo(0); }
+
+This can be fixed by adding `typename` in front of `Local::typeof_i` and
+`Local::typeof_t`.
+
+See also [@http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37920 GCC bug 37920].
+
+[note Although `BOOST_SCOPE_EXIT_TPL` has the same suffix as the
+`BOOST_TYPEOF_TPL`, it doesn't follow a convention of the _typeof_.]
+
+[endsect]
+
+[section:acknowledge Acknowledge]
+
+(in chronological order)
+
+Maxim Yegorushkin for sending me a code where he used a local struct
+to clean up resources.
+
+Andrei Alexandrescu for pointing me to _D_scope_exit_ construct of
+the D programming language.
+
+Pavel Vozenilek and Maxim Yanchenko for reviews of early drafts of
+the library.
+
+Steven Watanabe for his valuable ideas.
+
+Jody Hagins for good comments that helped to significantly improve the documentation.
+
+Richard Webb for testing the library on MSVC compiler.
+
+[endsect]

Added: trunk/libs/scope_exit/example/world.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/scope_exit/example/world.cpp 2009-01-21 18:51:17 EST (Wed, 21 Jan 2009)
@@ -0,0 +1,114 @@
+// Copyright Alexander Nasonov 2009
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <vector>
+#include <ostream>
+
+#include <boost/foreach.hpp>
+#include <boost/scope_exit.hpp>
+
+// The following is required for typeof emulation mode:
+#include <boost/typeof/typeof.hpp>
+#include <boost/typeof/std/vector.hpp>
+#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()
+
+class World;
+class Person
+{
+ friend class World;
+public:
+ typedef unsigned int id_t;
+ typedef unsigned int evolution_t;
+
+ Person()
+ : m_id(0)
+ , m_evolution(0)
+ {}
+
+ friend std::ostream& operator<<(std::ostream& o, Person const& p)
+ {
+ return o << "Person(" << p.m_id << ", " << p.m_evolution << ')';
+ }
+private:
+ id_t m_id;
+ evolution_t m_evolution;
+};
+
+BOOST_TYPEOF_REGISTER_TYPE(Person)
+
+class World
+{
+public:
+ typedef unsigned int id_t;
+
+ World()
+ : m_next_id(1)
+ {}
+ void addPerson(Person const& aPerson);
+
+ friend std::ostream& operator<<(std::ostream& o, World const& w)
+ {
+ o << "World(" << w.m_next_id << ", {";
+ BOOST_FOREACH(Person const& p, w.m_persons)
+ {
+ o << ' ' << p << ',';
+ }
+ return o << "})";
+ }
+private:
+ id_t m_next_id;
+ std::vector<Person> m_persons;
+};
+
+BOOST_TYPEOF_REGISTER_TYPE(World)
+
+void World::addPerson(Person const& aPerson) {
+ m_persons.push_back(aPerson);
+
+ // This block must be no-throw
+ Person& person = m_persons.back();
+ Person::evolution_t checkpoint = person.m_evolution;
+
+ BOOST_SCOPE_EXIT( (checkpoint)(&person)(&m_persons) )
+ {
+ if(checkpoint == person.m_evolution)
+ m_persons.pop_back();
+ } BOOST_SCOPE_EXIT_END
+
+ // ...
+
+ checkpoint = ++person.m_evolution;
+
+ // Assign new id to the person
+ World::id_t const prev_id = person.m_id;
+ person.m_id = m_next_id++;
+ BOOST_SCOPE_EXIT( (checkpoint)(&person)(&m_next_id)(prev_id) )
+ {
+ if(checkpoint == person.m_evolution) {
+ m_next_id = person.m_id;
+ person.m_id = prev_id;
+ }
+ } BOOST_SCOPE_EXIT_END
+
+ // ...
+
+ checkpoint = ++person.m_evolution;
+}
+
+#include <iostream>
+
+int main()
+{
+ Person adam, eva;
+ std::cout << adam << '\n';
+ std::cout << eva << '\n';
+
+ World w;
+ w.addPerson(adam);
+ w.addPerson(eva);
+ std::cout << w << '\n';
+}
+


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk