QuantLib defines a set of four error handling macros in the file errors.hpp.

QL_FAIL
QL_ASSERT
QL_REQUIRE
QL_ENSURE

These macros are intended to simplify checking any conditions and throwing an error. All three macros will throw an object of class Error which is derived from std::exception.

The macro QL_FAIL will throw an error unconditionally. Its usage is

QL_FAIL(message)

The message argument is anything that can be written to an std::ostringstream. A typical use of QL_FAIL might be signalling an unwanted outcome as in the following example

int find(const char *array; int size, char val) {
  for (int i=0; i<size; ++i)
    if (array[i]==val) return i;

  QL_FAIL("Value not found in array of size " << size)
}

In this example, the function find() looks for a value in an array by sequentially comparing every element with the given value. If the value is not found the function fails and throws an error which can be handled in a catch block.
Note how the message argument is made up of a string and an integer joined by the << operator. This is possible because, internally, the macro will pipe the message argument into an ostringstream.

int main()
{
  char array[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
  try {
    char c;
    std::cin >> c;
    int pos = find(array, 10, c);
    std::cout << "Position is " << pos << std::endl;
  }
  catch (Error e) {
    std::cerr << "Error: " << e.what() << std::endl;
  }
  return 0;
}

The above code illustrates how to handle the error thrown by the QL_FAIL statement. The what() method of the Error class returns the error message.

The other three macros throw an error if a given condition is not satisfied. In fact all three marcos QL_ASSERT, QL_REQUIRE and QL_ENSURE are synonyms for the same behaviour. They’re usage is

QL_ASSERT(condition, message);

or

QL_ASSERT(condition, message) {
  // do something
}

The condition is any expression that evaluates to a boolean and message is the error message as in the QL_FAIL macro. Note that, in the first of the two samples, there should be a semicolon at the end of the line. QL_ASSERT will throw an error when the condition is not met, i.e. if it evaluates to false.

Why does QuantLib provide three macros that achieve exactly the same thing? The reason is clarity! QL_REQUIRE should be used to test pre-conditions, QL_ENSURE to test post-conditions and QL_ASSERT for everything else.

int factorial(int n) {
  // checking pre-condition
  QL_REQUIRE(n>=0, "Factorial: Argument " << n << " less than zero");

  // do calculation

  QL_ENSURE(result>=0, "Oops! Negative result in factorial"); 
  return result;
}

Like before we should encapsulate the code in a try-catch construct.

int main()
{
  try {
    int n;
    std::cin >> n;
    int N = factorial(n);
    std::cout << "Factorial of " << n <<" is " << N << std::endl;
  }
  catch (Error e) {
    std::cerr << "Error: " << e.what() << std::endl;
  }
  return 0;
}

On my computer, if I enter the number 20 in the above program,

In the standard configuration the error string returned by what() will be exactly the message that was passed to the macro. But it is possible to make the message more verbose and to include the line number, the file name and the name of the function from which the error was thrown in the message. Two macros can be defined which control the verbosity. The macro QL_ERROR_LINES controls the output of the line number and the file name. If defined the string returned by what() will contain the file name and the line number before the actual message. Similarly, defining the macro QL_ERROR_FUNCTION will switch on the function name in the output if the compiler provides this information. These macros have to be defined when building the QuantLib library. If you use the configure script provided with the distribution then you can enable the macros with

./configure --enable-error-lines --enable-error-functions

With these configuration option the above code will result in the following output when I enter 20 into the above program

Error:
post2_exceptions.cpp:24: In function `int factorial(int)':
Oops! Negative result in factorial

The four macros QL_FAIL, QL_ASSERT, QL_REQUIRE and QL_ENSURE make error handling much more convenient and encourage the programmer to make frequent use of exceptions throughout their code. In the end, this results in more stable and safer code.

Tagged with:
 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>