2021-03-09 01:16:23 +01:00
|
|
|
#+TITLE: Error handling
|
|
|
|
#+SETUPFILE: ../docs/theme.setup
|
|
|
|
|
2021-03-10 12:58:38 +01:00
|
|
|
* Headers :noexport:
|
2021-03-09 01:16:23 +01:00
|
|
|
|
|
|
|
#+NAME: filename
|
|
|
|
#+begin_src elisp tangle: no
|
|
|
|
(file-name-nondirectory (substring buffer-file-name 0 -4))
|
|
|
|
#+end_src
|
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
#+begin_src c :tangle (eval h_private_type)
|
|
|
|
#ifndef QMCKL_ERROR_HPT
|
|
|
|
#define QMCKL_ERROR_HPT
|
|
|
|
#+end_src
|
|
|
|
|
2021-03-09 01:16:23 +01:00
|
|
|
#+begin_src c :tangle (eval c)
|
|
|
|
#include <stdint.h>
|
2021-03-30 14:51:23 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "qmckl_error_type.h"
|
|
|
|
#include "qmckl_context_private_type.h"
|
|
|
|
#include "qmckl_context_type.h"
|
|
|
|
|
|
|
|
#include "qmckl_context_func.h"
|
|
|
|
#include "qmckl_error_func.h"
|
2021-03-09 01:16:23 +01:00
|
|
|
#+end_src
|
2021-03-05 03:45:30 +01:00
|
|
|
|
2021-03-09 01:16:23 +01:00
|
|
|
#+begin_src c :tangle (eval c_test) :noweb yes
|
2021-03-05 03:45:30 +01:00
|
|
|
#include "qmckl.h"
|
|
|
|
#include "munit.h"
|
2021-03-09 01:16:23 +01:00
|
|
|
MunitResult test_<<filename()>>() {
|
|
|
|
#+end_src
|
2021-03-05 03:45:30 +01:00
|
|
|
|
2021-03-18 18:02:06 +01:00
|
|
|
#+end_src
|
2021-03-19 19:02:43 +01:00
|
|
|
|
2021-03-10 12:58:38 +01:00
|
|
|
*
|
|
|
|
:PROPERTIES:
|
|
|
|
:UNNUMBERED: t
|
|
|
|
:END:
|
2021-03-05 03:45:30 +01:00
|
|
|
|
2021-03-09 01:16:23 +01:00
|
|
|
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.
|
2021-03-05 03:45:30 +01:00
|
|
|
|
2021-03-19 18:26:29 +01:00
|
|
|
All the functions return with an exit code, defined as
|
2021-03-09 01:16:23 +01:00
|
|
|
#+NAME: type-exit-code
|
2021-03-30 14:51:23 +02:00
|
|
|
#+begin_src c :comments org :tangle (eval h_type)
|
2021-03-05 03:45:30 +01:00
|
|
|
typedef int32_t qmckl_exit_code;
|
2021-03-09 01:16:23 +01:00
|
|
|
#+end_src
|
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
|
|
|
|
#+begin_src f90 :comments org :tangle (eval fh_type) :exports none
|
|
|
|
integer , parameter :: qmckl_exit_code = c_int32_t
|
|
|
|
#+end_src
|
|
|
|
|
2021-03-09 01:16:23 +01:00
|
|
|
The exit code returns the completion status of the function to the
|
2021-03-10 12:58:38 +01:00
|
|
|
calling program. When a function call completed successfully,
|
|
|
|
~QMCKL_SUCCESS~ is returned. If one of the functions of
|
2021-03-09 01:16:23 +01:00
|
|
|
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.
|
|
|
|
|
|
|
|
#+NAME: table-exit-codes
|
2021-03-19 18:17:01 +01:00
|
|
|
| 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' |
|
2021-03-09 01:16:23 +01:00
|
|
|
|
|
|
|
# We need to force Emacs not to indent the Python code:
|
|
|
|
# -*- org-src-preserve-indentation: t
|
2021-03-19 18:17:01 +01:00
|
|
|
|
2021-03-10 12:58:38 +01:00
|
|
|
#+begin_src python :var table=table-exit-codes :results drawer :exports none
|
2021-03-05 03:45:30 +01:00
|
|
|
""" This script generates the C and Fortran constants for the error
|
|
|
|
codes from the org-mode table.
|
|
|
|
"""
|
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
result = [ "#+begin_src c :comments org :tangle (eval h_type) :exports none" ]
|
2021-03-19 18:17:01 +01:00
|
|
|
for (text, code,_) in table:
|
2021-03-05 03:45:30 +01:00
|
|
|
text=text.replace("~","")
|
2021-03-30 14:51:23 +02:00
|
|
|
result += [ f"#define {text:30s} ((qmckl_exit_code) {code:d})" ]
|
2021-03-09 01:16:23 +01:00
|
|
|
result += [ "#+end_src" ]
|
2021-03-05 03:45:30 +01:00
|
|
|
|
|
|
|
result += [ "" ]
|
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
result += [ "#+begin_src f90 :comments org :tangle (eval fh_type) :exports none" ]
|
2021-03-19 18:17:01 +01:00
|
|
|
for (text, code,_) in table:
|
2021-03-05 03:45:30 +01:00
|
|
|
text=text.replace("~","")
|
2021-03-30 14:51:23 +02:00
|
|
|
result += [ f" integer(qmckl_exit_code), parameter :: {text:30s} = {code:d}" ]
|
2021-03-09 01:16:23 +01:00
|
|
|
result += [ "#+end_src" ]
|
2021-03-05 03:45:30 +01:00
|
|
|
|
|
|
|
return '\n'.join(result)
|
|
|
|
|
2021-03-09 01:16:23 +01:00
|
|
|
#+end_src
|
|
|
|
|
|
|
|
#+RESULTS:
|
|
|
|
:results:
|
2021-03-30 14:51:23 +02:00
|
|
|
#+begin_src c :comments org :tangle (eval h_type) :exports none
|
|
|
|
#define QMCKL_SUCCESS ((qmckl_exit_code) 0)
|
|
|
|
#define QMCKL_INVALID_ARG_1 ((qmckl_exit_code) 1)
|
|
|
|
#define QMCKL_INVALID_ARG_2 ((qmckl_exit_code) 2)
|
|
|
|
#define QMCKL_INVALID_ARG_3 ((qmckl_exit_code) 3)
|
|
|
|
#define QMCKL_INVALID_ARG_4 ((qmckl_exit_code) 4)
|
|
|
|
#define QMCKL_INVALID_ARG_5 ((qmckl_exit_code) 5)
|
|
|
|
#define QMCKL_INVALID_ARG_6 ((qmckl_exit_code) 6)
|
|
|
|
#define QMCKL_INVALID_ARG_7 ((qmckl_exit_code) 7)
|
|
|
|
#define QMCKL_INVALID_ARG_8 ((qmckl_exit_code) 8)
|
|
|
|
#define QMCKL_INVALID_ARG_9 ((qmckl_exit_code) 9)
|
|
|
|
#define QMCKL_INVALID_ARG_10 ((qmckl_exit_code) 10)
|
|
|
|
#define QMCKL_FAILURE ((qmckl_exit_code) 101)
|
|
|
|
#define QMCKL_ERRNO ((qmckl_exit_code) 102)
|
|
|
|
#define QMCKL_INVALID_CONTEXT ((qmckl_exit_code) 103)
|
|
|
|
#define QMCKL_ALLOCATION_FAILED ((qmckl_exit_code) 104)
|
|
|
|
#define QMCKL_DEALLOCATION_FAILED ((qmckl_exit_code) 105)
|
|
|
|
#define QMCKL_INVALID_EXIT_CODE ((qmckl_exit_code) 106)
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
#+begin_src f90 :comments org :tangle (eval fh_type) :exports none
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_SUCCESS = 0
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_1 = 1
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_2 = 2
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_3 = 3
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_4 = 4
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_5 = 5
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_6 = 6
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_7 = 7
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_8 = 8
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_9 = 9
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_ARG_10 = 10
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_FAILURE = 101
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_ERRNO = 102
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_CONTEXT = 103
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_ALLOCATION_FAILED = 104
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_DEALLOCATION_FAILED = 105
|
|
|
|
integer(qmckl_exit_code), parameter :: QMCKL_INVALID_EXIT_CODE = 106
|
2021-03-09 01:16:23 +01:00
|
|
|
#+end_src
|
|
|
|
:end:
|
2021-03-05 03:45:30 +01:00
|
|
|
|
2021-03-30 22:40:56 +02:00
|
|
|
The ~qmckl_string_of_error~ converts an exit code into a string. The
|
2021-03-19 18:17:01 +01:00
|
|
|
string is assumed to be large enough to contain the error message
|
|
|
|
(typically 128 characters).
|
|
|
|
|
2021-03-30 22:40:56 +02:00
|
|
|
* Decoding errors
|
|
|
|
|
|
|
|
To decode the error messages, ~qmckl_string_of_error~ converts an
|
|
|
|
error code into a string.
|
|
|
|
|
2021-03-19 18:26:29 +01:00
|
|
|
#+NAME: MAX_STRING_LENGTH
|
|
|
|
: 128
|
2021-03-19 18:17:01 +01:00
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
#+begin_src c :comments org :tangle (eval h_func) :exports none :noweb yes
|
2021-03-29 01:39:12 +02:00
|
|
|
const char* qmckl_string_of_error(const qmckl_exit_code error);
|
2021-03-30 22:40:56 +02:00
|
|
|
void qmckl_string_of_error_f(const qmckl_exit_code error,
|
|
|
|
char result[<<MAX_STRING_LENGTH()>>]);
|
2021-03-19 18:17:01 +01:00
|
|
|
#+end_src
|
|
|
|
|
|
|
|
The text strings are extracted from the previous table.
|
|
|
|
|
|
|
|
#+NAME:cases
|
2021-03-29 01:17:33 +02:00
|
|
|
#+begin_src python :var table=table-exit-codes :exports none :noweb yes
|
2021-03-19 18:17:01 +01:00
|
|
|
""" This script extracts the text associated with the error codes
|
|
|
|
from the table.
|
|
|
|
"""
|
|
|
|
|
|
|
|
result = []
|
|
|
|
for (text, code, message) in table:
|
|
|
|
text = text.replace("~","")
|
|
|
|
message = message.replace("'",'"')
|
|
|
|
result += [ f"""case {text}:
|
2021-03-29 01:39:12 +02:00
|
|
|
return {message};
|
2021-03-19 18:17:01 +01:00
|
|
|
break;""" ]
|
|
|
|
return '\n'.join(result)
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
2021-03-19 18:26:29 +01:00
|
|
|
# Source
|
2021-03-19 18:17:01 +01:00
|
|
|
#+begin_src c :comments org :tangle (eval c) :noweb yes
|
2021-03-29 01:39:12 +02:00
|
|
|
const char* qmckl_string_of_error(const qmckl_exit_code error) {
|
2021-03-19 18:17:01 +01:00
|
|
|
switch (error) {
|
|
|
|
<<cases()>>
|
|
|
|
}
|
2021-03-29 01:39:12 +02:00
|
|
|
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()>>);
|
2021-03-19 18:17:01 +01:00
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
2021-03-19 18:26:29 +01:00
|
|
|
# Fortran interface
|
2021-03-30 14:51:23 +02:00
|
|
|
#+begin_src f90 :tangle (eval fh_func) :noexport :noweb yes
|
2021-03-19 18:26:29 +01:00
|
|
|
interface
|
2021-03-29 01:39:12 +02:00
|
|
|
subroutine qmckl_string_of_error (error, string) bind(C, name='qmckl_string_of_error_f')
|
2021-03-19 18:26:29 +01:00
|
|
|
use, intrinsic :: iso_c_binding
|
2021-03-30 14:51:23 +02:00
|
|
|
import
|
|
|
|
integer (qmckl_exit_code), intent(in), value :: error
|
2021-03-19 19:11:06 +01:00
|
|
|
character, intent(out) :: string(<<MAX_STRING_LENGTH()>>)
|
|
|
|
end subroutine qmckl_string_of_error
|
2021-03-19 18:26:29 +01:00
|
|
|
end interface
|
|
|
|
#+end_src
|
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
* Data structure in context
|
|
|
|
|
|
|
|
The strings are declared with a maximum fixed size to avoid
|
|
|
|
dynamic memory allocation.
|
|
|
|
|
|
|
|
#+begin_src c :comments org :tangle (eval h_private_type)
|
|
|
|
#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;
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
* Updating errors in the context
|
|
|
|
|
2021-03-30 22:40:56 +02:00
|
|
|
The error is updated in the context using ~qmckl_set_error~.
|
2021-03-30 14:51:23 +02:00
|
|
|
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~.
|
|
|
|
|
|
|
|
# Header
|
|
|
|
#+begin_src c :comments org :tangle (eval h_func) :exports none
|
|
|
|
qmckl_exit_code
|
|
|
|
qmckl_set_error(qmckl_context context,
|
|
|
|
const qmckl_exit_code exit_code,
|
|
|
|
const char* function_name,
|
|
|
|
const char* message);
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
# Source
|
|
|
|
#+begin_src c :tangle (eval c)
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
* 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
|
2021-03-30 22:40:56 +02:00
|
|
|
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.
|
2021-03-30 14:51:23 +02:00
|
|
|
|
|
|
|
#+begin_src c :comments org :tangle (eval h_func) :exports none
|
|
|
|
qmckl_exit_code qmckl_failwith(qmckl_context context,
|
|
|
|
const qmckl_exit_code exit_code,
|
|
|
|
const char* function,
|
|
|
|
const char* message) ;
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
#+begin_src c :comments org :tangle (eval c)
|
|
|
|
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);
|
2021-03-30 22:40:56 +02:00
|
|
|
if (message != NULL) {
|
|
|
|
assert (strlen(message) < QMCKL_MAX_MSG_LEN);
|
|
|
|
}
|
2021-03-30 14:51:23 +02:00
|
|
|
|
|
|
|
if (qmckl_context_check(context) == QMCKL_NULL_CONTEXT)
|
2021-03-30 22:40:56 +02:00
|
|
|
return QMCKL_INVALID_CONTEXT;
|
2021-03-30 14:51:23 +02:00
|
|
|
|
2021-03-30 22:40:56 +02:00
|
|
|
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);
|
|
|
|
}
|
2021-03-30 14:51:23 +02:00
|
|
|
|
|
|
|
return exit_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
For example, this function can be used as
|
|
|
|
#+begin_src c :tangle no
|
|
|
|
if (x < 0) {
|
|
|
|
return qmckl_failwith(context,
|
|
|
|
QMCKL_INVALID_ARG_2,
|
|
|
|
"qmckl_function",
|
|
|
|
"Expected x >= 0");
|
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
2021-03-10 12:58:38 +01:00
|
|
|
* End of files :noexport:
|
2021-03-05 03:45:30 +01:00
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
#+begin_src c :comments link :tangle (eval h_private_type)
|
|
|
|
#endif
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
2021-03-10 12:58:38 +01:00
|
|
|
** Test
|
2021-03-30 14:51:23 +02:00
|
|
|
#+begin_src c :comments link :tangle (eval c_test)
|
2021-03-05 03:45:30 +01:00
|
|
|
return MUNIT_OK;
|
|
|
|
}
|
2021-03-30 14:51:23 +02:00
|
|
|
#+end_src
|
2021-03-05 03:45:30 +01:00
|
|
|
|
2021-03-30 14:51:23 +02:00
|
|
|
# -*- mode: org -*-
|
|
|
|
# vim: syntax=c
|
2021-03-05 03:45:30 +01:00
|
|
|
|
|
|
|
|