1
0
mirror of https://github.com/TREX-CoE/qmckl.git synced 2024-11-19 12:32:40 +01:00
qmckl/src/qmckl_error.org

357 lines
12 KiB
Org Mode
Raw Normal View History

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
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-19 18:17:01 +01:00
The ~qmckl_strerror~ converts an exit code into a string. The
string is assumed to be large enough to contain the error message
(typically 128 characters).
#+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);
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
# 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
# Fortran interface
2021-03-30 14:51:23 +02:00
#+begin_src f90 :tangle (eval fh_func) :noexport :noweb yes
interface
2021-03-29 01:39:12 +02:00
subroutine qmckl_string_of_error (error, string) bind(C, name='qmckl_string_of_error_f')
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
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
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~.
# 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
well as the name of the function and an error message. The return
code of the function is the desired return code.
#+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 (message != NULL);
assert (strlen(function) < QMCKL_MAX_FUN_LEN);
assert (strlen(message) < QMCKL_MAX_MSG_LEN);
if (qmckl_context_check(context) == QMCKL_NULL_CONTEXT)
return QMCKL_NULL_CONTEXT;
const qmckl_exit_code rc =
qmckl_set_error(context, exit_code, function, message);
assert (rc == QMCKL_SUCCESS);
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
* TODO Decoding errors
To decode the error messages, ~qmckl_strerror~ converts an
error code into a string.
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