262 lines
8.1 KiB
C++
262 lines
8.1 KiB
C++
/*
|
|
* 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 scoped_execute.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 scoped_execute<> 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) {
|
|
* scoped_execute<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 scoped_execute 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() {
|
|
* scoped_execute<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 scoped_execute 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 _SCOPED_EXECUTE_H
|
|
#define _SCOPED_EXECUTE_H
|
|
|
|
#include <boost/noncopyable.hpp>
|
|
#include <boost/function.hpp>
|
|
|
|
template <typename T>
|
|
class scoped_variable : public boost::noncopyable
|
|
{
|
|
T& var;
|
|
T prev;
|
|
bool enabled;
|
|
|
|
public:
|
|
explicit scoped_variable(T& _var)
|
|
: var(_var), prev(var), enabled(true) {}
|
|
explicit scoped_variable(T& _var, const T& value)
|
|
: var(_var), prev(var), enabled(true) {
|
|
var = value;
|
|
}
|
|
~scoped_variable() {
|
|
if (enabled)
|
|
var = prev;
|
|
}
|
|
|
|
void clear() {
|
|
enabled = false;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
class scoped_execute : public boost::noncopyable
|
|
{
|
|
typedef boost::function<void (T)> function_t;
|
|
|
|
function_t code;
|
|
T arg;
|
|
bool enabled;
|
|
|
|
public:
|
|
explicit scoped_execute(const function_t& _code, T _arg)
|
|
: code(_code), arg(_arg), enabled(true) {}
|
|
|
|
~scoped_execute() {
|
|
if (enabled)
|
|
code(arg);
|
|
}
|
|
|
|
void clear() {
|
|
enabled = false;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
class scoped_execute<void> : public boost::noncopyable
|
|
{
|
|
typedef boost::function<void ()> function_t;
|
|
|
|
function_t code;
|
|
bool enabled;
|
|
|
|
public:
|
|
explicit scoped_execute(const function_t& _code)
|
|
: code(_code), enabled(true) {}
|
|
|
|
~scoped_execute() {
|
|
if (enabled)
|
|
code();
|
|
}
|
|
|
|
void clear() {
|
|
enabled = false;
|
|
}
|
|
};
|
|
|
|
#endif // _SCOPED_EXECUTE_H
|