deal.II version GIT relicensing-2206-gaa53ff9447 2024-12-02 09:10:00+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
Public Member Functions | Private Attributes | List of all members
ScopeExit Class Reference

#include <deal.II/base/scope_exit.h>

Public Member Functions

 ScopeExit (const std::function< void()> &exit_function)
 
 ScopeExit (const ScopeExit &)=delete
 
 ~ScopeExit ()
 
ScopeExitoperator= (const ScopeExit &)=delete
 

Private Attributes

const std::function< void()> exit_function
 

Detailed Description

A class that stores a function object (typically a lambda function) that is executed in the destructor of the current object. Such a class is useful to execute an action whenever the function in which the object is declared exits, whether that is by falling off the end of the function, an explicit return statement, or because an exception is thrown. In all of these cases, the destructors of all local objects are run, and the destructor of the ScopeExit object then executes the stored action.

The typical use case is that we want to perform a clean-up action. For example, let's say that we want to print to the console that we're leaving a long-running and potentially complicated function. This could be achieved as follows:

void long_running_and_complex_function ()
{
ScopeExit scope_exit_action ([]() { std::cout << "Leaving the long
running function!\n"; });
for (...)
{
// some 800 lines of complex code
}

The point here is that in order to have the message printed, we do not need to go through the 800 lines of complex code and find all places where either there is an explicit return statement, or where a function that is called may throw an exception that we do not explicitly catch: Any way the current function is exited will lead to the message being printed.

A perhaps more instructive example is the following (from some past iteration of the implementation of SUNDIALS::KINSOL::solve()):

status = KINSol(kinsol_mem, solution, data.strategy, u_scale, f_scale);
if (pending_exception)
{
try
{
std::rethrow_exception(pending_exception);
}
catch (const RecoverableUserCallbackError &exc)
{
pending_exception = nullptr;
if (status == 0)
; // just eat the exception
else
throw;
}
catch (...)
{
pending_exception = nullptr;
throw;
}
}
AssertKINSOL(status);
internal::copy(initial_guess_and_solution, solution);
// Free the vectors which are no longer used.
#ifdef DEAL_II_WITH_MPI
{
N_VDestroy_Parallel(solution);
N_VDestroy_Parallel(u_scale);
N_VDestroy_Parallel(f_scale);
}
else
#endif
{
N_VDestroy_Serial(solution);
N_VDestroy_Serial(u_scale);
N_VDestroy_Serial(f_scale);
}
long nniters;
status = KINGetNumNonlinSolvIters(kinsol_mem, &nniters);
AssertKINSOL(status);
if (J != nullptr)
SUNMatDestroy(J);
if (LS != nullptr)
SUNLinSolFree(LS);
KINFree(&kinsol_mem);
return static_cast<unsigned int>(nniters);
static ::ExceptionBase & RecoverableUserCallbackError()
#define AssertKINSOL(code)
Definition kinsol.h:47
std::vector< index_type > data
Definition mpi.cc:735

This code has a bug: At the bottom, it contains clean-up code that destroys a variety of objects created throughout the life of the function we are looking at. Because SUNDIALS is a library written in C, objects such as solution, u_scale, J, LS, etc., are really just regular pointers that need to be explicitly freed. But this does not happen in the places higher up where we throw an exception and don't catch it again immediately. One might be tempted to just copy the clean-up code to right before the std::rethrow_exception statement, but there is a path through the catch clauses marked with "just eat the exception" that ends up returning control flow back to the regular path, and if we hit this path, we would call the clean-up functions twice – also not what we want. The upshot of this kind of example is that there are cases where it is quite difficult to say where exactly a function will exit and where the clean-up code needs to be placed; it is far easier to write it only once and let the compiler determine where it needs to be called, via a class such as the current one.

The function is conceptually very similar to the std::experimental::scope_exit class template that may find its way into a future C++ standard.

Definition at line 133 of file scope_exit.h.

Constructor & Destructor Documentation

◆ ScopeExit() [1/2]

ScopeExit::ScopeExit ( const std::function< void()> &  exit_function)
inlineexplicit

Constructor. Takes a function object that is to be executed at the place where the current object goes out of scope as argument.

Definition at line 169 of file scope_exit.h.

◆ ScopeExit() [2/2]

ScopeExit::ScopeExit ( const ScopeExit )
delete

Copy constructor. These kinds of objects cannot be copied, so the constructor is deleted.

◆ ~ScopeExit()

ScopeExit::~ScopeExit ( )
inline

Destructor. Execute the stored action.

Definition at line 175 of file scope_exit.h.

Member Function Documentation

◆ operator=()

ScopeExit & ScopeExit::operator= ( const ScopeExit )
delete

Copy operator. These kinds of objects cannot be copied, so the operator is deleted.

Member Data Documentation

◆ exit_function

const std::function<void()> ScopeExit::exit_function
private

A copy of the function to be executed.

Definition at line 164 of file scope_exit.h.


The documentation for this class was generated from the following file: