diff options
Diffstat (limited to 'src/utility/pushvar.h')
-rw-r--r-- | src/utility/pushvar.h | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/utility/pushvar.h b/src/utility/pushvar.h new file mode 100644 index 00000000..793c0ca6 --- /dev/null +++ b/src/utility/pushvar.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2003-2007, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scopevar.h + * @author John Wiegley + * @date Sun May 6 20:10:52 2007 + * + * @brief Adds a facility to C++ for handling "scoped executions". + * + * There are sometimes cases where you would like to guarantee that + * something happens at the end of a scope, such as calling a function + * to close a resource for you. + * + * The common idiom for this has become to write a helper class whose + * destructor will call that function. Of course, it must then be + * passed information from the calling scope to hold onto as state + * information, since the code within the class itself has no access + * to its points of use. + * + * This type of solution is cumbersome enough that it's sometimes + * avoided. Take calling pthread_mutex_unlock(pthread_mutex_t *) for + * example. A typical snippet of safe C++ code might look like this: + * + * @code + * void foo(pthread_mutex_t * mutex) { + * if (pthread_mutex_lock(mutex) == 0) { + * try { + * // Do work that requires the mutex to be locked; then... + * pthread_mutex_unlock(mutex); + * } + * catch (std::logic_error& exc) { + * // This is an exception we actually handle, and then exit + * pthread_mutex_unlock(mutex); + * } + * catch (...) { + * // These are exceptions we do not handle, but still the + * // mutex must be unlocked + * pthread_mutex_unlock(mutex); + * throw; + * } + * } + * } + * @endcode + * + * The alternative to this, as mentioned above, is to create a helper + * class named pthread_scoped_lock, which might look like this: + * + * @code + * class pthread_scoped_lock : public boost::noncopyable { + * pthread_mutex_t * mutex; + * public: + * explicit pthread_scoped_lock(pthread_mutex_t * locked_mutex) + * : mutex(locked_mutex) {} + * ~pthread_scoped_lock() { + * pthread_mutex_unlock(mutex); + * } + * }; + * @endcode + * + * Although this helper class is just as much work as writing the code + * above, it only needs to be written once. Now the access code can + * look like this: + * + * @code + * void foo(pthread_mutex_t * mutex) { + * if (pthread_mutex_lock(mutex) == 0) { + * pthread_scoped_lock(mutex); + * try { + * // Do work that requires the mutex to be locked + * } + * catch (std::logic_error& exc) { + * // This is an exception we actually handle, and then exit + * } + * } + * } + * @endcode + * + * But what if it could be even easier? That is what this file is + * for, to provide a scopevar<> class which guarantees execution + * of arbtirary code after a scope has terminated, without having to + * resort to custom utility classes. It relies on boost::bind to + * declare pending function calls. Here it what the above would look + * like: + * + * @code + * void foo(pthread_mutex_t * mutex) { + * if (pthread_mutex_lock(mutex) == 0) { + * scopevar<void> unlock_mutex + * (boost::bind(pthread_mutex_unlock, mutex)); + * try { + * // Do work that requires the mutex to be locked + * } + * catch (std::logic_error& exc) { + * // This is an exception we actually handle, and then exit + * } + * } + * } + * @endcode + * + * The advantage here is that no helper class ever needs to created, + * and hence no bugs from such helper classes can creep into the code. + * The single call to boost::bind creates a closure binding that will + * be invoked once the containing scope has terminated. + * + * Another kind of scopevar is useful for setting the values of + * variables to a predetermined value upon completion of a scope. + * Consider this example: + * + * @code + * bool foo_was_run; + * + * void foo() { + * scopevar<bool&> set_success((_1 = true), foo_was_run); + * // do some code, and make sure foo_was_run is set to true + * // once the scope is exited -- however this happens. + * } + * @endcode + * + * In this case, the Boost.Lambda library is used to create an + * anonymous functor whose job is to set the global variable + * `foo_was_run' to a predetermined value. + * + * Lastly, there is another helper class, `scoped_variable' whose job + * is solely to return variables to the value they had at the moment + * the scoped_variable class was instantiated. For example, let's say + * you have a `bar' variable that you want to work on, but you want to + * guarantee that its value is restored upon exiting the scope. This + * can be useful in recursion, for "pushing" and "popping" variable + * values during execution, for example: + * + * @code + * std::string bar = "Hello"; + * void foo() { + * scoped_variable<std::string> restore_bar(bar); + * bar = "Goodbye"; + * // do work with the changed bar; it gets restored upon exit + * } + * @endcode + * + * As a shortcut, you can specify the new value for the pushed + * variable as a second constructor argument: + * + * @code + * std::string bar = "Hello"; + * void foo() { + * scoped_variable<std::string> restore_bar(bar, "Goodbye"); + * // do work with the changed bar; it gets restored upon exit + * } + * @endcode + * + * Finally, you can stop a scopevar or scoped_variable from + * invoking its completion code by calling the `clear' method on the + * object instance. Once `clear' is called, the scoped execution + * becomes inert and will do nothing when the enclosing scope is + * exited. + */ + +#ifndef _SCOPEVAR_H +#define _SCOPEVAR_H + +template <typename T> +class push_variable : public boost::noncopyable +{ + T& var; + T prev; + bool enabled; + +public: + explicit push_variable(T& _var) + : var(_var), prev(var), enabled(true) {} + explicit push_variable(T& _var, const T& value) + : var(_var), prev(var), enabled(true) { + var = value; + } + ~push_variable() { + if (enabled) + var = prev; + } + + void clear() { + enabled = false; + } +}; + +#endif // _SCOPEVAR_H |