1
0
mirror of https://github.com/TREX-CoE/qmckl.git synced 2024-07-15 23:51:05 +02:00
qmckl/src/qmckl_error.org
2021-04-09 11:26:04 +02:00

12 KiB

Error handling

The library should never make the calling programs abort, nor perform any input/output operations. This decision has to be taken by the developer of the code calling the library.

All the functions return with an exit code, defined as

typedef int32_t qmckl_exit_code;

The exit code returns the completion status of the function to the calling program. When a function call completed successfully, QMCKL_SUCCESS is returned. If one of the functions of the library fails to complete the requested task, an appropriate error code is returned to the program.

Here is the complete list of exit codes.

Macro Code Description
QMCKL_SUCCESS 0 'Success'
QMCKL_INVALID_ARG_1 1 'Invalid argument 1'
QMCKL_INVALID_ARG_2 2 'Invalid argument 2'
QMCKL_INVALID_ARG_3 3 'Invalid argument 3'
QMCKL_INVALID_ARG_4 4 'Invalid argument 4'
QMCKL_INVALID_ARG_5 5 'Invalid argument 5'
QMCKL_INVALID_ARG_6 6 'Invalid argument 6'
QMCKL_INVALID_ARG_7 7 'Invalid argument 7'
QMCKL_INVALID_ARG_8 8 'Invalid argument 8'
QMCKL_INVALID_ARG_9 9 'Invalid argument 9'
QMCKL_INVALID_ARG_10 10 'Invalid argument 10'
QMCKL_FAILURE 101 'Failure'
QMCKL_ERRNO 102 strerror(errno)
QMCKL_INVALID_CONTEXT 103 'Invalid context'
QMCKL_ALLOCATION_FAILED 104 'Allocation failed'
QMCKL_DEALLOCATION_FAILED 105 'De-allocation failed'
QMCKL_INVALID_EXIT_CODE 106 'Invalid exit code'

The qmckl_string_of_error converts an exit code into a string. The string is assumed to be large enough to contain the error message (typically 128 characters).

Decoding errors

To decode the error messages, qmckl_string_of_error converts an error code into a string.

128

The text strings are extracted from the previous table.

#+NAME:cases

const char* qmckl_string_of_error(const qmckl_exit_code error) {
switch (error) {
<<cases()>>
}
return "Unknown error";
}

void qmckl_string_of_error_f(const qmckl_exit_code error, char result[<<MAX_STRING_LENGTH()>>]) {
strncpy(result, qmckl_string_of_error(error), <<MAX_STRING_LENGTH()>>);
}
interface
   subroutine qmckl_string_of_error (error, string) bind(C, name='qmckl_string_of_error_f')
     use, intrinsic :: iso_c_binding
     import
     integer (qmckl_exit_code), intent(in), value :: error
     character, intent(out) :: string(<<MAX_STRING_LENGTH()>>)
   end subroutine qmckl_string_of_error
end interface

Data structure in context

The strings are declared with a maximum fixed size to avoid dynamic memory allocation.

#define  QMCKL_MAX_FUN_LEN   256
#define  QMCKL_MAX_MSG_LEN  1024

typedef struct qmckl_error_struct {

qmckl_exit_code exit_code;
char function[QMCKL_MAX_FUN_LEN];
char message [QMCKL_MAX_MSG_LEN];

} qmckl_error_struct;

Updating errors in the context

The error is updated in the context using qmckl_set_error. When the error is set in the context, it is mandatory to specify from which function the error is triggered, and a message explaining the error. The exit code can't be QMCKL_SUCCESS.

qmckl_exit_code
qmckl_set_error(qmckl_context context,
              const qmckl_exit_code exit_code,
              const char* function_name,
              const char* message)
{
/* Passing a function name and a message is mandatory. */
assert (function_name != NULL);
assert (message != NULL);

/* Exit codes are assumed valid. */
assert (exit_code >= 0);
assert (exit_code != QMCKL_SUCCESS);
assert (exit_code < QMCKL_INVALID_EXIT_CODE);

/* The context is assumed to exist. */
assert (qmckl_context_check(context) != QMCKL_NULL_CONTEXT);

qmckl_lock(context);
{
  qmckl_context_struct* const ctx = (qmckl_context_struct* const) context;
  assert (ctx != NULL); /* Impossible because the context is valid. */
  
  ctx->error.exit_code = exit_code;
  strncpy(ctx->error.function, function_name, QMCKL_MAX_FUN_LEN);
  strncpy(ctx->error.message, message, QMCKL_MAX_MSG_LEN);
}
qmckl_unlock(context);

return QMCKL_SUCCESS;
}

Failing

To make a function fail, the qmckl_failwith function should be called, such that information about the failure is stored in the context. The desired exit code is given as an argument, as well as the name of the function and an error message. If the message is NULL, then the default message obtained by qmckl_string_of_error is used. The return code of the function is the desired return code. Upon failure, a QMCKL_NULL_CONTEXT is returned.

qmckl_exit_code qmckl_failwith(qmckl_context context,
                            const qmckl_exit_code exit_code,
                            const char* function,
                            const char* message) {

assert (exit_code > 0);
assert (exit_code < QMCKL_INVALID_EXIT_CODE);
assert (function != NULL);
assert (strlen(function) < QMCKL_MAX_FUN_LEN);
if (message != NULL) {
 assert (strlen(message)  < QMCKL_MAX_MSG_LEN);
}

if (qmckl_context_check(context) == QMCKL_NULL_CONTEXT)
 return QMCKL_INVALID_CONTEXT;

if (message == NULL) {
 qmckl_exit_code rc = 
   qmckl_set_error(context, exit_code, function, qmckl_string_of_error(exit_code));
 assert (rc == QMCKL_SUCCESS);
} else {
 qmckl_exit_code rc = 
   qmckl_set_error(context, exit_code, function, message);
 assert (rc == QMCKL_SUCCESS);
}

return exit_code;
}

For example, this function can be used as

if (x < 0) {
return qmckl_failwith(context,
                     QMCKL_INVALID_ARG_2,
                     "qmckl_function", 
                     "Expected x >= 0");
}