Exceptions are raised if an error is detected. Seldon is able to check for several mistakes:
To enable SELDON_CHECK_MEMORY
, for example, put
(before #include <Seldon.hxx>
):
#define SELDON_CHECK_MEMORY
Alternatively, there are debug levels:
In practice, it is advocated to choose SELDON_DEBUG_LEVEL_4 in the development stage and SELDON_DEBUG_LEVEL_2 for the stable version. Indeed SELDON_DEBUG_LEVEL_4 slows down the program but checks many things and SELDON_DEBUG_LEVEL_2 should not slow down the program and ensures that it is reasonably safe.
Development stage:
#define SELDON_DEBUG_LEVEL_4
Stable version:
#define SELDON_DEBUG_LEVEL_2
The objects that may be launched by Seldon are of type:
WrongArgument
, NoMemory
, WrongDim
,
WrongIndex
, WrongRow
, WrongCol
,
IOError
and LapackError
. They all derive from
Error
. They provide the method What
that returns a
string explaining the error, and the method CoutWhat
that
displays on screen this explanation. Therefore, to catch an exception raised
by Seldon, put:
catch(Seldon::Error& Err) { Err.CoutWhat(); }
Two macros are defined to help:
TRY
:
#define TRY try {
END
:
#define END \ } \ catch(Seldon::Error& Err) \ { \ Err.CoutWhat(); \ return 1; \ } \ catch (std::exception& Err) \ { \ cout << "C++ exception: " << Err.what() << endl; \ return 1; \ } \ catch (std::string& str) \ { \ cout << str << endl; \ return 1; \ } \ catch (const char* str) \ { \ cout << str << endl; \ return 1; \ } \ catch(...) \ { \ cout << "Unknown exception..." << endl; \ return 1; \ }
It is advocated that you enclose your code (in the main
function) with TRY;
and END;
:
int main(int argc, char** argv) { TRY; // Here goes your code. END; return 0; }
Suppose your code contains an error and raises an exception. You probably want to identify the function that raised the exception. The error message should contain the name of the function. But you probably want to know the exact line where the error occurred and the sequence of calls. Then, you have two options, using a debugger.
One option is to place a breakpoint in Error::Error(string function =
"", string comment = "")
(see file share/Errors.cxx) because this
constructor should be called before the exception is actually raised.
Another option, more convenient because no breakpoint is to be placed, is
to define SELDON_WITH_ABORT
. With that flag activated, if a
Seldon exception is raised, the program will simply abort. The call stack is
then at hand. See the example below, using gdb under Linux. The program error.cpp demonstrates the technique:
#define SELDON_WITH_ABORT #define SELDON_DEBUG_LEVEL_4 // which checks bounds. #include "Seldon.hxx" using namespace Seldon; int main(int argc, char** argv) { TRY; IVect V(3); cout << "Bad access: " << V(3) << endl; END; return 0; }
The error and the calls sequence are easy to obtain:
$ g++ -o error -g -I ~/src/seldon error.cpp $ ./error ERROR! Index out of range in Vector<VectFull>::operator(). Index should be in [0, 2], but is equal to 3. Aborted $ gdb ./error GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu"... (gdb) run Starting program: /tmp/error ERROR! Index out of range in Vector<VectFull>::operator(). Index should be in [0, 2], but is equal to 3. Program received signal SIGABRT, Aborted. 0x00007f4b7e136fb5 in raise () from /lib/libc.so.6 (gdb) up #1 0x00007f4b7e138bc3 in abort () from /lib/libc.so.6 (gdb) up #2 0x000000000040308e in WrongIndex (this=0x1a83370, function= {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fff86e3e0f0 "H?\001"}}, comment= {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fff86e3e100 "??\001"}}) at /home/mallet/src/seldon/share/Errors.cxx:206 206 abort(); (gdb) up #3 0x0000000000404888 in Seldon::Vector<int, Seldon::VectFull, Seldon::MallocAlloc<int> >::operator() (this=0x7fff86e3e1c0, i=3) at /home/mallet/src/seldon/vector/Vector.cxx:440 440 throw WrongIndex("Vector<VectFull>::operator()", (gdb) up #4 0x000000000040364b in main (argc=1, argv=0x7fff86e3e2d8) at error.cpp:11 11 cout << "Bad access: " << V(3) << endl;
Apart from exceptions, two useful macros are defined to ease the debugging activities:
ERR
:
#define ERR(x) cout << "Hermes - " #x << endl
DISP
:
DISP(x) cout << #x ": " << x << endl
In a code:
ERR(5); ERR(5a); int i = 7; DISP(i + 1); ERR(Last line);
returns on screen:
Hermes - 5 Hermes - 5a i + 1: 8 Hermes - Last line