]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Extend the discussion in our exception module.
authorWolfgang Bangerth <bangerth@colostate.edu>
Tue, 6 Jun 2017 20:40:20 +0000 (14:40 -0600)
committerWolfgang Bangerth <bangerth@colostate.edu>
Wed, 7 Jun 2017 16:06:58 +0000 (10:06 -0600)
doc/doxygen/headers/exceptions.h

index 5cc02395f7c7b2eff3d677c4fd342cb63467c716..d8d9cb98729f528f733b5a2873ca2b1613de0bdb 100644 (file)
  * <ul>
  * 
  *   <li> Static assertions: These are checks that are only enabled in debug
- *   mode, not in optimized (or production) mode. They are meant to check that
- *   parameters to functions satisfy certain properties and similar
+ *   mode, not in release (or optimized, production) mode. In deal.II, static
+ *   assertions are typically used to check that parameters to functions satisfy
+ *   certain properties, that internal data structures are consistent, and similar
  *   assertions. For example, static assertions are used to make sure that two
  *   vectors that are added together have the same number of components --
  *   everything else would not make any sense anyway.
  *
- *   Such checks are performed by the Assert macro in several thousand places
+ *   Such checks are performed by the @p Assert macro in several thousand places
  *   within the library. Also, several tutorial programs starting with step-5
  *   show how to do this.
  *
  *   If a static assertion is violated, the exception mechanism generates an
  *   exception of a type that indicates what exactly goes wrong, displays
- *   appropriate information, and then aborts the program -- if you try to add
+ *   appropriate information including the exact location where the problem
+ *   was detected, and then aborts the program -- if you try to add
  *   two vectors of different length, there is nothing that can be done within
  *   the program to cope with the situation, you have to go fix the program
- *   code instead. The exceptions of this module are used to indicate the
- *   reason for the failure.
- *
- *
- *   <li> Dynamic assertions: These are used to check dynamic features, such
- *   as whether an output file can be written to. These are things that can't
- *   be checked statically, i.e. they may change from program run to program
- *   run. It is therefore insufficient to only check these situations in debug
- *   mode.
+ *   code instead. There is generally not even a reason to @p throw an exception
+ *   object using the usual C++ exception mechanism because there is nothing
+ *   a function higher up could do in such cases to rectify the situation
+ *   and deal with it in a useful way -- it's not that the program received
+ *   bad data; the program is just buggy, and one can not intelligently
+ *   work around that.
+ *
+ *   (It is sometimes useful to change the behavior of the @p Assert macro
+ *   from aborting a program to throwing exceptions. On the other hand,
+ *   exceptions are not allowed to propagate out of destructors of classes.
+ *   For this purpose, there is a variant of the macro, called @p AssertNothrow,
+ *   that can be used in destructors. These use cases are discussed further
+ *   down below on this page.)
+ *
+ *
+ *   <li> Dynamic assertions: These are used to check conditions that depend on
+ *   external things that may be different from one program run to the next, such
+ *   as whether an output file can be written to.
+ *
+ *   These are things that shouldn't
+ *   be checked statically, because it is not guaranteed that a program for which
+ *   the condition is satisfied in a debug mode run, will also have the condition
+ *   satisfied in a subsequent release mode run -- in other words, it is not
+ *   sufficient to only check these situations in debug mode.
  *
  *   Rather, one has to check them every time during execution of a
- *   program. Within deal.II, this is done using the AssertThrow macro
+ *   program. Within deal.II, this is done using the @p AssertThrow macro
  *   introduced in step-9, step-13, and
  *   following tutorial programs. The macro checks a condition, and if
  *   violated throws an exception of one of the types declared in this
  *  @code
  *    Assert (n<dim, ExcDomain(n,dim));
  *  @endcode
- *  which by macro expansion does essentially the following:
+ *  which by macro expansion does essentially the following (though the actual
+ *  code is slightly more complicated):
  *  @code
  *    #ifdef DEBUG
  *        if (!(cond))
  *              issue error of class ExcDomain(n,dim)
  *    #else
- *        do nothing
+ *        // do nothing
  *    #endif
  *  @endcode
- *  i.e. it issues an error only if the preprocessor variable
+ *  That is, it issues an error only if the preprocessor variable
  *  <tt>DEBUG</tt> is set and if the given condition (in this case
  *  <tt>n < dim</tt> is violated).
  *
  *  If the exception was declared using the <tt>DeclException0 (...)</tt>
- *  macro, i.e. without any additional parameters, its name has
+ *  macro, i.e., without any additional parameters, its name has
  *  nonetheless to be given with parentheses:
  *  <tt>Assert (i>m, ExcSomewhat());</tt>
  *
  *  message about suppressed follow-up messages is shown.
  *
  *
- *  <h3>Use of run-time exceptions</h3>
+ *  <h3>Use of run-time exceptions (dynamic checks)</h3>
+ *
+ *  C++ has a mechanism to indicate that something exceptional has
+ *  happened: exceptions that can be triggered by <tt>throw</tt> statements
+ *  and captured by <tt>catch</tt> clauses, see for example
+ *  https://en.wikipedia.org/wiki/C%2B%2B#Exception_handling and
+ *  http://www.cplusplus.com/doc/tutorial/exceptions/ .
  *
- *  For this mode, the standard <tt>C++</tt> <tt>throw</tt> and <tt>catch</tt>
- *  concept exists. We
- *  want to keep to this, but want to extend it a bit. In general, the
- *  structure is the same, i.e. you normally raise and exception by
+ *  At some fundamental level, a typical C++ exception is an object that
+ *  is placed in some special place, and then the function exits the current
+ *  scope (e.g., the current function) through an exceptional return path.
+ *  This is often enough to tell what problem triggered the exception,
+ *  but more frequently it would be nice if one had more information: for
+ *  example, in which line of the code the problem happened, or what
+ *  non-existent entry of a sparse matrix the code wanted to write into.
+ *
+ *  Dynamic assertions in deal.II therefore extend this mechanism a bit.
+ *  Typically, one would raise an exception by code such as
  *  @code
  *    if (!(cond))
  *      throw ExcSomething();
  *    try {
  *      do_something ();
  *    }
- *    catch (exception &e) {
+ *    catch (std::exception &e) {
  *      std::cerr << "Exception occurred:" << std::endl
  *           << e.what ()
  *           << std::endl;
  *      do_something_to_reciver ();
  *    };
  *  @endcode
- *  <tt>exception</tt> is a standard <tt>C++</tt> class providing basic functionality for
- *  exceptions, such as the virtual function <tt>what()</tt> which returns some
+ *  <tt>std::exception</tt> is a standard <tt>C++</tt> class providing basic functionality for
+ *  exceptions, such as the virtual function <tt>what()</tt> that returns some
  *  information on the exception itself. This information is useful if an
  *  exception can't be handled properly, in which case as precise a description
  *  as possible should be printed.
  *
  *  The problem here is that to get significant and useful information out
- *  of <tt>what()</tt>, it is necessary to overload this function in out exception
+ *  of <tt>what()</tt>, it is necessary to overload this function in our exception
  *  class and call the <tt>throw</tt> operator with additional arguments to the
  *  exception class. The first thing, overloading the <tt>what</tt> function is
  *  done using the <tt>DeclExceptionN</tt> macros, but putting the right information,
  *
  *  For this purpose, the macro <tt>AssertThrow</tt> was invented. It does
  *  mainly the same job as does the <tt>Assert</tt> macro, but it does not
- *  kill the program, it rather throws an exception as shown above. The mode
+ *  abort the program; rather, it throws an exception as shown above. The mode
  *  of usage is
  *  @code
  *    AssertThrow (cond, ExcSomething(additional information));
  *  Obviously for the <tt>DeclException0(name)</tt> macro, no types and
  *  also no output sequence is allowed.
  *
- * @author Wolfgang Bangerth, 1998-2006
+ *
+ *  <h3>A corner case of @p Assert: The @p AssertNothrow macro</h3>
+ *
+ *  The default implementation of the @p Assert macro, as discussed above,
+ *  prints detailed information about what exactly went wrong to the
+ *  screen and then aborts the program. Aborting the program is useful
+ *  because it allows easily finding the place where something went
+ *  wrong -- including all of the information how we got to that
+ *  place -- by running the program in a debugger.
+ *
+ *  On the other hand, there are cases where aborting a program may be
+ *  undesirable and one needs to exit in a somewhat more graceful
+ *  way -- even if there is really not very much one can do in these
+ *  cases to still produce a meaningful result. An example is if a
+ *  deal.II program is run a one module in a bigger framework of
+ *  software. Think, for example, of a case where a deal.II program
+ *  computed the flow field that corresponds to a set of input
+ *  variables provided by some optimization routine: if the optimizer
+ *  on the outside provided a negative density as input (a condition
+ *  one might want to check via @p Assert), then this
+ *  clearly makes no sense, and the flow solver cannot produce a
+ *  meaningful answer; but it should tell that to the optimizer nicely,
+ *  rather than just aborting the entire process (optimizer and flow
+ *  solver).
+ *
+ *  For this purpose, one can call
+ *  deal_II_exceptions::disable_abort_on_exception() that switches
+ *  what @p Assert does from aborting the program to essentially the
+ *  same as @p AssertThrow does, namely using the C++ @p throw mechanism
+ *  to raise an exception. This exception can then be caught at a higher
+ *  level -- e.g., in the optimization routine that sits atop the flow
+ *  solver, and that can then decide what it wants to do with the
+ *  situation.
+ *
+ *  This is all nice and good, but C++ does not allow throwing exceptions
+ *  inside the destructors of classes, or in a function that is currently
+ *  being called from a destructor higher up in the call stack. To this
+ *  end, there is a separate macro, @p AssertNothrow, that can be used in
+ *  destructors: It acts just like @p Assert usually does -- in particular,
+ *  it only checks the condition in debug mode -- but it is immune to the
+ *  effect of deal_II_exceptions::disable_abort_on_exception(): It will
+ *  only ever abort the program, and never throw an exception.
+ *
+ *
+ * @author Wolfgang Bangerth, 1998-2017
  */

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.