      error
      Copyright (c)  2005,2008,2010,2015 John Abbott
      GNU Free Documentation License, Version 1.2
%!includeconf: ../aux-txt2tags/config.t2t
      TeXTitle{error}{John Abbott}



== Examples ==
%======================================================================
- [ex-error1.C ../../examples/index.html#ex-error1.C]
-

== User documentation ==
%======================================================================

The standard way of reporting an error in CoCoALib is to call
``CoCoA_ERROR`` which will throw an object of type ``CoCoA::ErrorInfo``.
This type is derived from ``CoCoA::exception`` (see [[exception]]).


=== Debugging ===
%----------------------------------------------------------------------

If you get a ``CoCoA ERROR`` when you execute your program, you can
easily intercept it with your preferred debugger tool.

For example, when debugging with **gdb**, type
``` break CoCoA::ThrowError
and then ``run``.
When it stops in the call of ``ThrowError``, type ``up`` to reach the line
which originally caused the error.


=== Recommended way of reporting errors ===
%----------------------------------------------------------------------

Usually if you have detected an error in your program, you want to
report it immediately.  We recommend that you use the macro ``CoCoA_ERROR``
to do this.  Here's an example:
```
    value_t operator/(const value_t& num, const value_t& den)
    {
      if (IsZero(den))
        CoCoA_ERROR(ERR::DivByZero, "operator/ for value_t");
      ....
    }
```
The first argument should be an error ID (//i.e.// ERR::something); you can
find a list of the IDs in the file (CoCoA_ROOT)/include/CoCoA/error.H.  If
no ID is suitable, you can just put a string instead.  The second argument
should be an indication of the function in which the error occurred.


=== Adding a New Error ID and its Default Message ===
%----------------------------------------------------------------------

If you are a CoCoALib contributor and want to add a new error ID and
message (or even a new language for error messages), please read the
maintainer documentation for what to do.


=== Information about errors -- for the more advanced ===
%----------------------------------------------------------------------

The macro ``CoCoA_ERROR`` does two things:

- (1)  it creates a ``CoCoA::ErrorInfo`` object with the parameters given to the macro, and also with the filename and line number;
- (2)  it calls the function ``CoCoA::ThrowError`` on the ``ErrorInfo`` object just created.


Below we explain these two stages in more detail.

The class ``CoCoA::ErrorInfo`` is intended to be used for creating exception
objects -- indeed, it derives from ``std::exception``.  There are two things
you are likely to want to do with exception objects:

- (A)  create and throw the exception object
- (B)  catch the exception object


- Case (A) Rather than using using the C++ ``throw`` command directly, we
  recommend that you pass the error object to the function
  ``CoCoA::ThrowError`` as it makes debugging easier (see above).

  We also recommend that you use the constructor which takes a
  ``CoCoA::ERR::ID`` and a string; the string should indicate where
  the error was detected, //e.g.// the name of the C++ function which
  found the error.  Look through the list of ``CoCoA::ERR::ID``s (in
  the file ``error.H``) to find the one best suited to the type of
  error you wish to signal.

  If no error ``CoCoA::ERR::ID`` is suitable then you can use the
  constructor which accepts two string arguments: the first should be a
  description of the error (//e.g.// "Incompatible hypermatrix
  dimensions"), and the second should indicate where the error was
  detected.  If you are a CoCoALib contributor, see the notes below about
  how to add a new error ID and message.


  NOTE: if you set the C++ preprocessor symbol ``CoCoA_DEBUG`` to a value
    greater than 1 then a log message is produced each time ``CoCoA::ThrowError``
    is called; the log message is printed on ``std::clog``.


- Case (B) After catching a ``CoCoA::ErrorInfo`` object in the variable ``err``
  you can make the following queries:
```
  err == ERR::ErrorID  -- returns true iff err is of type ERR::ErrorID
                   (replace ErrorID by the ID of the error you want!)
```
  EXAMPLE (of handling a CoCoA Error):
```
  try { ... }
  catch (const CoCoA::ErrorInfo& err)
  {
    if (err != ERR::DivByZero) throw; // rethrow unexpected error
    // code to handle the "expected" division by zero error
  }
```
  If you have caught a ``CoCoA::ErrorInfo`` object and want to announce it
  as an error then call the procedure ``CoCoA::ANNOUNCE`` with the ostream
  on which to make the announcement and the ErrorInfo object as second
  argument.  This will print an eye-catching error announcement, and then
  return to the caller.  Note that ``CoCoA::ANNOUNCE`` does not cause the
  program to exit/abort, it merely prints out an eye-catching announcement.

  To facilitate debugging, an ``ErrorInfo`` object may be printed in the usual
  way; this produces a modest message, clearly different from an error
  announcement.


Recall that, as for any other "exception object", simply creating a
``CoCoA::ErrorInfo`` object does not cause the error condition to be
signalled.  To signal the error it must be thrown -- we recommend passing
the error object to the function ``CoCoA::ThrowError`` (see above).


=== Choosing the language for error messages ===
%----------------------------------------------------------------------

You may choose the language for CoCoALib error messages: the default is
English.  If an error message has not yet been translated into the
chosen language then it is replaced by the default english message.
Currently there are only two choices:
```  ErrorLanguage::english();
```  ErrorLanguage::italian();

EXAMPLE:
```
  int main()
  {
    CoCoA::ErrorLanguage::italian(); // vogliamo messaggi d'errore in italiano
    ....
  }
```
The language for error messages may be changed any number of times: the last
chosen language is the one used when creating an ``ErrorInfo`` object.




== Maintainer documentation for files error.H and error.C ==
%======================================================================

``CoCoA::ErrorInfo`` is derived from ``std::exception`` for compatibility with the
rest of C++.  How this might be useful I cannot yet say, but it does not
measurably complicate the code (though it does force the implementation of a
member function called ``what``).

The preferred constructors for ``ErrorInfo`` are those accepting an
``ERR::ID`` and a C string indicating context (with or without filename
and line number information); the other constructors should be used
only when no suitable ``ERR::ID`` exists.  The ``ERR::ID`` object indicates
the general nature of the error, and is used for selecting the error
message to be printed.

Note that the conversion from an ``ERR::ID`` to a string is slightly
convoluted: this is to allow the possibility of selecting at run-time a
language other than English for the error messages.

I chose not to offer an ``ErrorInfo`` constructor which accepts natively
``const char*`` args because the potential extra copying of strings (to
construct a ``std::string``) is hardly likely to be important, and
``std::string``s feel much cleaner.

The nature and context of the error are kept separate in an ``ErrorInfo``
object since it is possible that we may wish to propagate the nature of the
error to top level in an interactive system where it would be unhelpful
or confusing to refer to some C++ function irrelevant to the user.

The definition of the function ``CoCoA::ThrowError`` is quite straightforward.
The function is deliberately not inline: efficiency is wholly
unimportant whereas the ability to set a breakpoint in the function is
(some debuggers may be unable to set a breakpoint in an inline function).

Each CoCoA error ID object is in reality a constant global variable
containing two pointers to constant C strings called ``myName`` and
``myDefaultMesg``: the latter contains the associated default error message
(which must be in English), and the former contains the name of the error
ID object.  The identity of the error ID actually resides in the address of
the specific string constant in the data member ``myName`` -- this implies
that copying the ID object does not change its identity.  Since the
different objects necessarily have different names, the string literals
containing those names are surely distinct, and so we are guaranteed that
these addresses are distinct.  There are comparison operators (equal,
not-equal, and less-than) for ``ERR::ID``; less-than is needed for using C++
maps when implementing error messages in languages other than english.
These comparison operators merely conduct the required comparison on the
addresses of the strings in ``myName``; this is quick and simple, and
sufficient for our purposes -- the actual values of strings pointed to are
not taken into consideration!


=== To Add a New Error Code and Message ===
%----------------------------------------------------------------------

Invent a new name for the error code, and insert it in the list of names
of "error variables" (in the file error.H).  Make sure you insert it
in the right place respecting alphabetical order -- this way it is easy
to check whether a name is already present in the list.  Add a short
comment indicating what sort of error that code is to be used for.

Next you must add a message corresponding to that code.  In the file
error.C you will find a long list of "error variable" initializations.  Add
an initialization for your new "error variable" -- the syntax is quite
obvious from the other initializations there (which use the macro
``DEFINE_ERROR``).  You may wish to add translations of your new error message
into the other languages present in error.C.


=== To Add a New Language for Error Messages ===
%----------------------------------------------------------------------

You must write a function analogous to the function italian() which
resides inside the namespace ``CoCoA::ErrorLanguage``.  The new function
must have a name different from the other functions there: I suggest the
english name of the language.  Inside the function you will have to fill
a ``MsgTable_t`` object with the translated messages associated to each
possible error code.  At the end you should check to make sure that you
have a message for each possible code: it should suffice simply to count
them.  The code will still compile and run even if some translated
messages are missing: if an error occurs for which the current error
language has no translation then the default (english) message is printed.

EXAMPLE:
  Suppose we want to add german error messages.  We choose to use
  the name "german" for the function which activates german error
  messages.  Here is what we do:
:  (1) edit error.H;
      immediately after the line containing "void italian();" insert
      "void german();"

:  (2) edit error.C;
      make a copy of the function italian(){...} and change its name
      to "german" -- make sure you stay inside namespace ``ErrorLanguage``;
      translate all the error messages in the strings.



== Bugs, Shortcomings, and other ideas ==
%======================================================================

The throw specifications on the destructor and ``what`` member function
are needed for compatibility with ``std::exception`` -- I regard this as
a nuisance.  I wonder if ``std::string::c_str`` can throw?

What about parameter values?  In some cases it would be handy to give
the bad value which caused the error: //e.g.// "Bad characteristic: 33".
A problem is that a parameter value could be very large.  We could
simply allow up to 100 (say) characters of parameter information in
a suitable string.

Only very few error messages have been translated into italian so far.

Perhaps allow the user to specify which ostream to print the logging
message in ``ThrowError``?

=== new improved list of errors ===

work in progress

- ShouldNeverGetHere (previously SERIOUS)
- NYI
- nonstandard (specified by a string) -- equality test makes sense for these?
- system/LowLevel  (about GlobalMgr, MemPool, I/O, AssertFail)
 - InsuffPrecision???
 -
- BadArg (UnsuitableValue)
 - NotNonZero RENAME ExpectedNonZero
 - IndexOutOfRange   (incl iterators?)
 - ValueOutOfRange
  - NotNonNegative (RENAME ExpectedNonNegative?)
  - NotPositive (RENAME ExpectedNonNegative?)
  - TooBig  (*not* system independent!!!)
  -
 -
- BadArgType
- IncompatibleArgs  (op not def'd for the given combination of args)
 - IncompatibleSizes
 - IncompatibleTypes
 -
-

== Main changes ==
%======================================================================

**2013**
- July (v0.995??):
 - major re-write in progress
-


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% For "txt2tags safety" leave 2 empty lines after the last line of text.

