JiscMail Logo
Email discussion lists for the UK Education and Research communities

Help for COMP-FORTRAN-90 Archives


COMP-FORTRAN-90 Archives

COMP-FORTRAN-90 Archives


COMP-FORTRAN-90@JISCMAIL.AC.UK


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

COMP-FORTRAN-90 Home

COMP-FORTRAN-90 Home

COMP-FORTRAN-90  2002

COMP-FORTRAN-90 2002

Options

Subscribe or Unsubscribe

Subscribe or Unsubscribe

Log In

Log In

Get Password

Get Password

Subject:

Re: Error handling mechanisms

From:

Peter Shenkin <[log in to unmask]>

Reply-To:

Fortran 90 List <[log in to unmask]>

Date:

Tue, 19 Mar 2002 17:40:57 -0500

Content-Type:

TEXT/PLAIN

Parts/Attachments:

Parts/Attachments

TEXT/PLAIN (438 lines)

Hi,

In response to Alexander Donev's request for error-handling strategies:

It's important to distinguish errors (caught by the program) from
exceptions (signals).  I'm only addressing errors here.

We developed an error handling library with C and Fortran interfaces.
It's object-based.  It's described in the README included below,
though the Fortran interface hasn't been extended to all the
features the C interface has.

In simplest practice the way it's used is:

mmerrf_initialize (to initialize the mmerr library)
mmerrf_new (to get a new handler, or else use the default)
mmerrf_file (to specify IO unit for reporting of thrown errors)
mmerrf_throw with an error level (to throw an error)

later,
mmerrf_delete (to delete the handler, unless using the default)
mmerrf_terminate (to return the library to its pristine state)
progrem exit

Though the library has no means of enforcing this, the preferred usage
pattern is (pseudocode):

program prog
  ...
  call foo1( istatus )
  if( istatus .NE. OK )then
    mmerrf_throw( ihandler, MMERR_FATAL, 'main: foo1 failed' )
    exit
  end if
end program prog
subroutine foo1( istatus )
  integer istatus
  ...
  istatus = OK
  call foo2( istatus )
  if( istatus .NE. OK )then
    mmerrf_throw( ihandler, MMERR_FATAL, 'foo1: foo2 failed' )
    istatus = NOT_OK
    exit
  end if
end subroutine foo1
subroutine foo2( istatus )
  integer istatus
  ...
  istatus = OK
  if( some_condition_fails )then
    mmerrf_throw( ihandler, MMERR_FATAL, 'foo2: problem; can''t go on', istat )
    istatus = NOT_OK
    exit
  end if
end subroutine foo2

Thus, at the end, we effectively have a call stack of err messages
printed out, which is very helpful in debugging, and as often as
possible helpful to users as well.  For the above example, the
printout would look something like this:

  FATAL foo2: problem; can't go on
  FATAL foo1: foo2 failed
  FATAL main: foo1 failed

Obviously, in practice, the messages would be more informative. :-)

Most of our interactive programs are written in C or C++;  therefore
the main use of the Fortran interface has been for simple batch-level
reporting to an output file (like a log file).  Functions useful
for interactive programs (such as delayed reporting or reporting
errors to, say, an interactive window) have not been extended to
Fortran, but they would be, should we develop a need for them.

Improvements could certainly be made to the Fortran interfaces,
particularly in the area of string handling.

-P.

mmerr README:

    mmerr - MacroModel error-reporting library;  includes functions to
    throw and catch errors at various severities, and to specify actions
    to be taken when an error of a particular severity is thrown.  The
    most typical action is the placment of a message in a file, output
    stream, or GUI window, and explicit functions are defined which
    control this behavior.  The mmerr library is object oriented: different
    handlers can be allocated, each with its own behavior.

    The test drivers tryc (for the C API) and tryf (for the F API) are
    supplied.  The expected outputs are in tryc.out and tryf.out.

C SYNOPSIS
    #include <mmerr.h>

    INITIALIZE AND TERMINATE MMERR PACKAGE
    Mmerr_level mmerr_initialize( void );
    void mmerr_terminate( void );

    ALLOCATE AND FREE A HANDLER
    Mmerr_level mmerr_new( int *const ihandler );
    void mmerr_delete( const int ihandler );

    DEFINE A STRING WHICH WILL ALWAYS PRECEDE A HANDLER'S MESSAGES
    Mmerr_level mmerr_prefix( const int ihandler, const char *const prefix );

    SET AND GET MINIMAL LEVEL FOR REPORTING
    Mmerr_level mmerr_level( const int ihandler, const unsigned int level );
    Mmerr_level mmerr_get_level( const int ihandler,
     unsigned int *const level );

    THROW AN ERROR
    Mmerr_level mmerr_throw( const int ihandler, const unsigned int level,
     const char *const fmt, ... );

    DEFINE LOCATION WHERE ERROR MESSAGE WILL BE PLACED
    Mmerr_level mmerr_file( const int ihandler, const FILE *const fp );
    Mmerr_level mmerr_window( const int ihandler,
     Mmerr_window_print const wputs );

    SET USER_DEFINED CATCH FUNCTION FOR AN ERROR LEVEL
    Mmerr_level mmerr_catch( const int ihandler, const unsigned int level,
     Mmerr_func const catch_func, void *const catch_data );

    DELAYED ERROR HANDLING
    Mmerr_level mmerr_maxlevel( const int ihandler,
     unsigned int *const maxlevel );
    Mmerr_level mmerr_queue_on( const int ihandler, const int is_active );
    Mmerr_level mmerr_queue_get( const int ihandler, char **const str );
    Mmerr_level mmerr_errclear( const int ihandler );

    RETURN POINTER TO CHAR STRING DESCRIBING AN ERROR LEVEL
    char *mmerr_return_code( const unsigned int level );

FORTRAN SYNOPSIS
    N.B.  Except where otherwise noted, function arguments follow
    Fortran naming conventions, except that, for clarity, arguments
    shown in capitals have values supplied by the caller, whereas
    arguments shown in lower case have values supplied to the caller
    by the function.  In every case, "istat" is a status variable
    which will return either MMERR_OK or MMERR_BUMMER, as described
    below.

    INCLUDE 'mmerr.inc'

    Initialize and terminate mmerr package:
    SUBROUTINE MMERRF_INITIALIZE( istat )
    SUBROUTINE MMERRF_TERMINATE( )

    Allocate and free a handler:
    SUBROUTINE MMERRF_NEW( ihandler, istat )
    SUBROUTINE MMERRF_DELETE( IHANDLER )

    Define a string which will always precede a handler's messages:
    SUBROUTINE MMERRF_PREFIX( IHANDLER, PREFIX, istat )
    CHARACTER*(MMERR_S_STRLEN) PREFIX

    Set and get minimal level for reporting:
    SUBROUTINE MMERRF_LEVEL( IHANDLER, ILEVEL )
    SUBROUTINE MMERRF_GET_LEVEL( IHANDLER, ilevel, istat )

    Throw an error:
    SUBROUTINE MMERRF_THROW( IHANDLER, ILEVEL, MESSAGE, istat )
    CHARACTER*(MMERR_L_STRLEN)

    Define location where error message will be placed:
    SUBROUTINE MMERRF_FILE( IHANDLER, IUNIT, istat )
    SUBROUTINE MMERRF_WINDOW( !!! not implemented )

    Set user_defined catch function for an error level:
    SUBROUTINE MMERRF_CATCH( !!! not implemented )

    Delayed error handling:
    SUBROUTINE MMERRF_MAXLEVEL( IHANDLER, MAXLEVEL, istat )
    SUBROUTINE MMERRF_QUEUE_ON( !!! not implemented )
    SUBROUTINE MMERRF_QUEUE_GET( !!! not implemented )
    SUBROUTINE MMERRF_ERRCLEAR( IHANDLER )

    Return pointer to char string describing an error level:
    SUBROUTINE MMERRF_RETURN_CODE( IHANDLER, STRING, istat )
    CHARACTER*(MMERR_S_STRLEN) STRING


DESCRIPTION
    The mmerr library provides an API to an error reporting and handling
    facility largely intended for use by writers of code libraries,
    but also usable by application programmers.

    ihandler, returned by merr_new(), is an instance of an error handler.
    User code passes error conditions to such a handler as they arise,
    using mmerr_throw().  The other functions either set up actions which the
    mmerr library is to perform when an error of a particular severity
    occurs or retrieve information about errors after they have
    occurred.

    mmerr_initialize() initializes the library, mmerr_new() creates a
    new error handler, mmerr_delete() deletes an old handler, and
    mmerr_terminate(), which is provided primarily to enforce
    malloc cleanliness, deallocates all storage previously declared by
    the mmerr package;  this includes deletion of all extant handlers.
    It is legal to call mmerr_initialize() multiple times, but all
    calls beyond the first, prior to an mmerr_terminate(), are ignored.

    mmerr_throw() is the mechanism by which user or library code declares
    to the mmerr package that an error has occurred.  For example:

        foo = ( struct foo_tag * )malloc( nfoo * sizeof(struct foo_tag );
        if( foo == NULL ) {
                /* fatal error:  malloc() fails: */
                ( void )mmerr_throw( ihandler, MMERR_FATAL,
                 "Could not malloc() %d bytes for foo.\n",
                 nfoo*sizeof(struct foo_tag) );
                exit( 1 );
        }

    ihandler is an error handler either earlier obtained from
    mmerr_new() or else is equal to MMERR_DEFAULT_HANDLER (see below).
    MMERR_FATAL is one of several predefined error levels available
    in mmerr.h.  Most user code will probably throw only MMERR_FATAL and
    MMERR_WARNING, but a more complete list is:  MMERR_OK, MMERR_DEBUG,
    MMERR_INFO, MMERR_WARNING, MMERR_FATAL.  In addition, MMERR_BUMMER
    and MMERR_OFF exist and have special meanings which will be discussed
    later;  they should not be thrown by user code.

    Most of the mmerr library calls set up actions that will be taken
    when a throw, such as the one shown above, is received.  Unless actions
    are properly set up, no action or reporting will take place, unless
    MMERR_DEFAULT_HANDLER is used (see below).

    mmerr_level() specifies the minimum error level that must be thrown
    for any action to take place.  For example, if mmerr_level() is
    set to MMERR_WARNING, then an mmerr_throw() call specifying a level
    of MMERR_INFO will result in no action whatsoever.  This allows
    the mmerr library to be used as a mechanism for logging debugging
    information:  if mmerr_throw() is called many places in the code
    with a low level, such as MMERR_INFO, then these messages will be
    reported only when mmerr_level() is set to this or to the even lower
    MMERR_OK value.  The default mmerr minimum reporting level is
    MMERR_WARNING.  If mmerr_level is called with the special value
    MMERR_OFF, then error reporting and handling will be turned completely
    off for the handler in question.

    mmerr_file() allows a file to be specified to which error messages are
    to be logged.  Specifying a NULL value of the fp variable turns
    off file reporting.  merr_window() allows a user-defined callback to
    be registered.  This callback, which is expected to obey the typedef:

        typedef int ( *Window_print )( char *string );

    is supposed to print its argument to the user's graphical window(s).
    It is also expected to return EOF if it knows that it has failed
    (for example, if its widget destination has not yet been instantiated,
    and if the user wishes to deem this an error).  The message passed by
    mmerr_throw() is printed to a file or a window if mmerr_file() or
    mmerr_window() has previously been used to define such a destination
    and if the thrown error exceeds the minimum reporting level specified
    in mmerr_level() (MMERR_WARNING by default).  When an error is
    reported, the text thrown is preceded, on output, by an identifier
    reflecting the severity of the error: "WARNING" for a warning, "FATAL"
    for a fatal error, etc.

    When mmerr_library is initialized, a default error handler is
    always set up, and may be referred to by MMERR_DEFAULT_HANDLER.
    The default action is to put thrown messages on the stderr output
    unit, though, as with any other handler, this action may be altered
    by means of mmerr_file().

    Most of the other MacroModel libraries accept a single argument
    to their initialize() functions:  the mmerr handler which the
    library is to use.  These libraries themselves call mmerr_initialize(),
    and proceed to throw errors to the mmerr handler received in their
    own initialization calls.  In the simplest case, the application
    author will initialize all such libraries with MMERR_DEFAULT_HANDLER.

    mmerr_prefix() defines a prefix (character string) which will
    precede all error messages.  For example, if an application is
    called "myapp", and if it throws messages itself, in addition
    to using several libraries, then, if the application and the
    libraries all use the same mmerr handler, calling mmerr_prefix()
    with this handler and the string "myapp; " will cause a thrown
    WARNING to be preceeded by the string "myapp; WARNING:".  On
    the other hand, if the application causes each library to use
    its own handler, then each handler may be passed its own prefix.

    mmerr_catch() allows a user-specified error-catching function
    to be registered for any desired error level.  If an error of the
    specified level is later thrown, this function will be called after
    other any other reporting is done, all provided, of course, that the
    level of the throw exceeds the minimum reporting level.  The
    user-supplied callback is expected to conform to the typedef:

        typedef void ( *Mmerr_func )( void* arg_list );

    The argument to the callback is registered at the same time as the function,
    using mmerr_catch()'s catch_data argument.  It is the responsibility
    of the user code to keep this variable current in the event that the
    callback is actually called.  The user may call mmerr_catch() with
    several functions to be called at different error levels.  A
    specification of NULL in place of a valid function pointer turns off
    this mechanism for the error-level in question.

    Several additional mmerr functions support delayed-mode error handling.
    mmerr_maxlevel() returns the most severe error that has occurred
    since the start of the application or since the most recent call to
    mmerr_errclear().  If desired, mmerr_queue_on() may be specified
    with a non-zero value in its is_active variable, or later turned
    off with is_active set to zero.  When it is turned on, any messages
    which mmerr would record are appended to internally malloc()ed storage.
    A call to mmerr_queue_get() returns a strdup()ed copy of the queue in
    the variable str.  It is the user's responsibility to free() str when
    s/he is done with it; a call to mmerr_errclear() free()s mmerr's copy
    of the queue in addtion to resetting the maximum error level recorded
    to its initial value of MMERR_OK.

    mmerr_return_code() returns an ascii string describing the meaning
    of an Mmerr_level value.  For example, mmerr_return_code(5) returns
    "MMERR_FATAL", given the mmerr.h file as of the time of this
    writing.

FORTRAN NOTES
    The same error handler may be manipulated and thrown to from
    the C and Fortran sides of a mixed-language program.

    If a negative value of IUNIT is passed to MMERRF_FILE(), this
    has the effect of turning off file-direction of thrown errors;
    this is the Fortran equivalent of passing a NULL file-pointer
    to the corresponding C-API function.

    Only a string, of length MMERR_L_STRING, may be passed as as
    message to MMERRF_THROW(); this is in contrast to the C function
    mmerr_throw(), which accepts a printf()-style format and a
    variable-length list of values to be printed.

    Several facilities are not not implemented directly from the
    Fortran API.  These include specification of output to a GUI
    window, of user-specified catch funtions and of delayed-mode
    error reporting.  However, the writer of a Fortran program
    may link in his own C module which calls these mmerr functions
    using the handler obtained from the Fortran side.

EXAMPLES
    Immediate-mode reporting is recommended for high-level procedures
    which are called infrequently and which return a status.  Delayed-mode
    reporting is recommended for low-level procedures that might be invoked
    many times in a row and which do not return a status;  a sequence
    of such calls might be bracketed by a mmerr_errclear() /  mmerr_maxerr()
    pair.

    For example, let us suppose that the writer of an application using
    the the "lib1" library and the lib1 library writer are both making
    use of the mmerr facility.  The writer of lib1 might supply functions
    with prototypes:

        Mmerr_level lib1_setup( void );  /* initialize lib1 variables */
        Mmerr_level lib1_mmerr( *libhandle ); /* pass lib1's handler to usr */
        float lib1_getx( int iatm ); /* get the x coordinate of an atom */

    Within lib1, an error handler is allocated, and the library writer has
    provided the "lib1_mmerr" mechanism to pass its "ihandle" value
    back to the user of the library.  By calling mmerr_level(), mmerr_file(),
    etc., with this value, the library user may then direct the error-
    reporting behavior of lib1.  (Alternatively, the writer of lib1 might choose
    to hide the identity of its handler to the user, opting instead to
    log errors to some generic output file already known to it.)

    The user of lib1 might first set up his own error handler using
    mmerr_new(&userhandle).  Then his code might look like this:

        mmerr_level( usrhandle, MMERR_WARNING );
        mmerr_file( usrhandle, stdout );
        level = lib1_setup();
        mmerr_throw( usrhandle, level, "usr: lib1_setup returns %s\n",
         mmerr_return_code(level) );
        if( level == MMERR_FATAL ) exit( 1 );

        mmerr_errclear( libhandle );
        mmerr_queue_on( libhandle );
        for( iatm=0; iatm<natm; ++iatm ) {
                /* assume lib1_getx() throws an error to its mmerr handler
                 *  if it encounters a problem, such as iatm out of range;
                 *  we ignore these errors for now: */
                xcoord[ iatm ] = lib1_getx( iatm );
        }
        /* now see if there were any lib1_getx errors: */
        mmerr_maxerr( libhandle, &level );
        if( level >= MMERR_WARNING ) {
                char *str;
                mmerr_queue_get( libhandle, &str );
                mmerr_throw( usrhandle, level,
                 "Error %s encountered during lib1_getx; queue=%s\n",
                 mmerr_return_code(level), str );
                free( str );
                mmerr_errclear( libhandle );
                /* insert code to work around the error... *.
        }


DIAGNOSTICS

    A problem arises when an error occurs within the  mmerr library itself.
    For example, malloc() might return NULL, or a file pointer supplied
    by the user might be invalid.  Such errors might well prevent
    mmerr from logging errors in the manner to which it is accustomed.
    For this reason, the mmerr library does not throw errors to itself;
    however, most of the mmerr functions do return a status to the calling
    code.

    The Mmerr_level returned by an mmerr function will be either MMERR_OK,
    indicating that no error occurred, or MMERR_BUMMER, indicating that
    an unrecoverable error occurred within the mmerr library.  Although
    the example code shown above does not, for simplicity's sake, check
    these error status of mmerr calls, "real" code should do so.

USAGE SUGGESTIONS

    The provider of a library using mmerr error handling may, in some
    cases, wish to hide the identity of the mmerr handler from the user.
    For example, if the library is called "lib1", a simple function
    called lib1_errfile() might be provided which takes a single
    argument, a file pointer, and sets up mmerr logging to this file.
    This saves the user of the library from having to know anything
    about mmerr.

    On the other hand, the library provider might wish to allow its
    mmerr handler to be fully configurable by the end user, as we
    have done by making an mmerr handler specifiable in the library
    initialization procedue.

    Both methods can, in principle, coexist.

--
  Peter S. Shenkin                      Schrodinger, Inc.
  VP, Software Development              120 W. 45th St.
  646 366 9555 x111 Tel                 New York, NY 10036
  646 366 9550 FAX                      [log in to unmask]
                                        http://www.schrodinger.com

Top of Message | Previous Page | Permalink

JiscMail Tools


RSS Feeds and Sharing


Advanced Options


Archives

December 2023
February 2023
November 2022
September 2022
February 2022
January 2022
June 2021
November 2020
September 2020
June 2020
May 2020
April 2020
December 2019
October 2019
September 2019
March 2019
February 2019
January 2019
November 2018
October 2018
September 2018
August 2018
July 2018
May 2018
April 2018
March 2018
February 2018
January 2018
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
January 2017
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
December 2015
November 2015
October 2015
September 2015
August 2015
June 2015
April 2015
March 2015
January 2015
December 2014
November 2014
October 2014
August 2014
July 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013
July 2013
June 2013
May 2013
April 2013
February 2013
January 2013
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
April 2012
March 2012
February 2012
January 2012
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
December 2010
November 2010
October 2010
August 2010
July 2010
June 2010
March 2010
February 2010
January 2010
December 2009
October 2009
August 2009
July 2009
June 2009
March 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
January 2007
2006
2005
2004
2003
2002
2001
2000
1999
1998
1997


JiscMail is a Jisc service.

View our service policies at https://www.jiscmail.ac.uk/policyandsecurity/ and Jisc's privacy policy at https://www.jisc.ac.uk/website/privacy-notice

For help and support help@jisc.ac.uk

Secured by F-Secure Anti-Virus CataList Email List Search Powered by the LISTSERV Email List Manager