12 KiB
Error handling
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()>>-1);
}
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-1);
strncpy(ctx->error.message, message, QMCKL_MAX_MSG_LEN-1);
}
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");
}