This section outlines the error management used in the library. We are only talking here about errors at the runtime and not compiling and linking errors. Different error types can occur when using the library, among these errors, one can distinguish input output operations (attempt to load a missing file, analysis of a graph file containing types which are not defined in the current support, etc.), from errors caused by conceptual graph operations (when arguments passed to the operation do not satisfy certain constraints, etc.) or from other errors such as the failure to meet preconditions of certain methods (adding an element to a set that already contains this element, etc.).
The error management is mainly achieved through the use of exceptions, and the reader who is not familiar with this concept is invited to consult the C++ documentation for explanations on how to handle such objects.
The first section below describes how to configure the library in order to enable the error control (in order to ease debugging) or to disable it (in order to optimize performance). Such a configuration is not essential though, you can directly go to section The Exception class if you wish to use the default configuration.
During the development of an application, a large number of errors are coming from invalid parameters passed to methods. The verification of the validity of these parameters is unnecessary once the application is fully developed. In order not to slow down the execution of such an application, verifications done in the library can be detached from the processus, and you can even choose the behavior of the library where an error is detected.
The preprocessor symbol value
ASSERT_MODE (see common.h) defines the process done when an error is encountered. If this symbol is set to
0, no verification is done (excepted the syntax checking when loading a file), if this symbol is set to
1 preconditions are verified by a call to
assert() (see a documentation C/C++ for more explanations on this standard function) which causes an immediate exit of the application when a precondition is not satisfied. Finally, if this symbol is set to
2, a detected error causes the raise of an exception. The latter mode is the default one.
It is possible, when
ASSERT_MODE is set to
2 to choose which conditions must be satisfied and that they can be considered as always valid. In this way, it is possible to speed up the execution of an application being developed by choosing to enable the error management of Cogitant only for operations that are used by the part "being developped" in the application. Thus, the "finalized" part of the application can run at optimal speed. Choosing conditions to be checked is done by defining preprocessor symbols
CGA_xxx (see common.h).
If no error management is enabled (
ASSERT_MODE=0) unexpected runtime errors (
Bus error and other funny things...) can occur at runtime in case of bad parameters are passed to platform methods (note that such runtime errors can occur even if an error management is enabled because all cases of incongruous values are not tested). If the error management by
assert is enabled (
ASSERT_MODE=1), detection of an error causes an instant exit of the application and displays the message
Abort. This can be used to quickly find the responsible instructions thanks to a debugger. In the rest of this section, only error management by exceptions (ie
ASSERT_MODE = 2 ) is dealt with.
Changing the error management mode is done in different ways according to the compiler used. Under Unix, you must use the
–enable-assert= parameter followed by
0 (equivalent to
2 (equivalent to
yes), for example:
Under Windows, you have to change the value of CMake variables such as
AssertMode and specify the chosen value. Anyway, this is only the first step, and you have to completely recompile the library as well as the application built on the library.
To choose conditions to be satisfied (symbols
CGA_xxx), you have to modify the file include/cogitant/common.h and, again, completely recompile the library. Remember that the definition of such symbols have no meaning if
ASSERT_MODE is equal to
The cogitant::Exception class is a super-class of all classes representing exceptions. While this class is never used to create exceptions (only its subclasses are used), it provides a number of attributes and methods being common to all exception classes. For instance, this class has a cogitant::Exception::toString() method returning a string providing an error label, and an operator of output on a stream to display the exception. Moreover, all exceptions raised by methods of the library are instances of (subclasses) cogitant::Exception, you can handle all exceptions while only recovering cogitant::Exception ones, as in the example below.
Example. Creating an empty conceptual graph and adding a concept vertex to this graph. The vertex is then regarded as a relation vertex with a call to the cogitant::GraphObject::asRelation() method, causing a raise of an exception of type cogitant::ExceptionIncorrectType, managed in the block
catch as this class is a subclass of cogitant::Exception. The process of the exception consists in the display of this exception (which, for an instance of cogitant::ExceptionIncorrectType, causes the display of this class name). After the process (execution of the block
catch), the application continues to run, and ends "without error" in the case of this example.
Among cogitant::Exception subclasses, some are used in different circumstances (and especially input output exceptions). To precisely know which mistake has been detected, you can use the cogitant::Exception::code() method which returns an error code, whose semantics depends on the exception class (see the following paragraph in the case of input output errors as well as pages documenting cogitant::Exception subclasses).
A large number of runtime errors are caused by inputs outputs. These errors are represented by the cogitant::ExceptionIO class and its subclasses. Such errors may come from an attempt to read or write a file wrongly named or a non-compliance with the format of a file being analysed. The cogitant::ExceptionIO class identifies in particular input output errors at a file level: opening problems, reading or writing problems, filename extension unrecognized. These errors can be differentiated by the value returned by cogitant::Exception::code(). For example, cogitant::ExceptionIO::OPEN identifies a file reading problem (probably non-existent) and cogitant::ExceptionIO::CREATE identifies a file creation problem (the file directory is probably unreachable).
Example. To illustrate the use of this class, the example below try to load multiple files and displays a specific message when one of these files cannot be found. Other exceptions are displayed as they are. Thus, the first and the third file are loaded while the second one raise a cogitant::ExceptionIO, with the error code cogitant::ExceptionIO::OPEN, displaying the message
>>> File not found <<<.
If the cogitant::ExceptionIO class identifies access file errors, its subclasses represent more particularly the non-compliance with the file format. Thus, a cogitant::ExceptionInputLexeme is raised when, in the file being read, an unexpected lexeme is found. Such an error, for example, is caused when a BCGCT file does not contain a character
: after the keyword
Concepts (see Grammar of the BCGCT format for CoGITaNT 5). Under these conditions, the call to cogitant::Exception::code() make it possible to know the code of the expected lexeme (for a description of BCGCT lexeme codes, refer to the documentation of the cogitant::OperationBCGCTInput::IStreamBCGCT class).
Sometimes, a file is syntactically correct in the sense that it was written in accordance with the format syntax but it contains no valid object which can be manipulated in the library. This is the case, for example, when a concept type undefined in the current support is used in a file containing a conceptual graph. Such errors are handled by subclasses of cogitant::ExceptionInput which are exceptions classes specific to each format (cogitant::ExceptionInputBCGCT, cogitant::ExceptionInputCoGXML). Thus, the cogitant::ExceptionInputBCGCT class identifies errors when analysing of a BCGCT file and defines several error codes that can be used in conjunction with the cogitant::Exception::code() method to find the error cause (actually, some of these error codes are declared in cogitant::ExceptionInput, and inherited in all subclasses, thus in cogitant::ExceptionInputBCGCT). In any case, if these error codes are useful to test the error type by a program, it is easier to use the cogitant::ExceptionIO::toString() method (or the output operator) to display the error label on the screen. Note that the cogitant::ExceptionIO class and its subclasses include a cogitant::ExceptionIO::line() method which returns the file line number to which the error was detected.