UP | HOME

Error handling

Table of Contents

1 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.

const char* qmckl_string_of_error(const qmckl_exit_code error) {
  switch (error) {
  case QMCKL_SUCCESS:
              return "Success";
            break;
  case QMCKL_INVALID_ARG_1:
              return "Invalid argument 1";
            break;
  case QMCKL_INVALID_ARG_2:
              return "Invalid argument 2";
            break;
  case QMCKL_INVALID_ARG_3:
              return "Invalid argument 3";
            break;
  case QMCKL_INVALID_ARG_4:
              return "Invalid argument 4";
            break;
  case QMCKL_INVALID_ARG_5:
              return "Invalid argument 5";
            break;
  case QMCKL_INVALID_ARG_6:
              return "Invalid argument 6";
            break;
  case QMCKL_INVALID_ARG_7:
              return "Invalid argument 7";
            break;
  case QMCKL_INVALID_ARG_8:
              return "Invalid argument 8";
            break;
  case QMCKL_INVALID_ARG_9:
              return "Invalid argument 9";
            break;
  case QMCKL_INVALID_ARG_10:
              return "Invalid argument 10";
            break;
  case QMCKL_INVALID_ARG_11:
              return "Invalid argument 11";
            break;
  case QMCKL_INVALID_ARG_12:
              return "Invalid argument 12";
            break;
  case QMCKL_INVALID_ARG_13:
              return "Invalid argument 13";
            break;
  case QMCKL_INVALID_ARG_14:
              return "Invalid argument 14";
            break;
  case QMCKL_INVALID_ARG_15:
              return "Invalid argument 15";
            break;
  case QMCKL_INVALID_ARG_16:
              return "Invalid argument 16";
            break;
  case QMCKL_INVALID_ARG_17:
              return "Invalid argument 17";
            break;
  case QMCKL_INVALID_ARG_18:
              return "Invalid argument 18";
            break;
  case QMCKL_INVALID_ARG_19:
              return "Invalid argument 19";
            break;
  case QMCKL_INVALID_ARG_20:
              return "Invalid argument 20";
            break;
  case QMCKL_FAILURE:
              return "Failure";
            break;
  case QMCKL_ERRNO:
              return strerror(errno);
            break;
  case QMCKL_INVALID_CONTEXT:
              return "Invalid context";
            break;
  case QMCKL_ALLOCATION_FAILED:
              return "Allocation failed";
            break;
  case QMCKL_DEALLOCATION_FAILED:
              return "De-allocation failed";
            break;
  case QMCKL_NOT_PROVIDED:
              return "Not provided";
            break;
  case QMCKL_OUT_OF_BOUNDS:
              return "Index out of bounds";
            break;
  case QMCKL_INVALID_EXIT_CODE:
              return "Invalid exit code";
            break;
  }
  return "Unknown error";
}

void qmckl_string_of_error_f(const qmckl_exit_code error, char result[128]) {
  strncpy(result, qmckl_string_of_error(error), 128-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(128)
   end subroutine qmckl_string_of_error
end interface

2 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;

3 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;
}

4 Get the error

Upon error, the error type and message can be obtained from the context using qmckl_get_error. The message and function name is returned in the variables provided. Therefore, passing a function name and message is mandatory.

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

  /* 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. */

/* Turn off annoying GCC warning */
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
#endif

    strncpy(function_name, ctx->error.function, QMCKL_MAX_FUN_LEN-1);
    strncpy(message      , ctx->error.message , QMCKL_MAX_MSG_LEN-1);

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

    (*exit_code) = ctx->error.exit_code;
  }
  qmckl_unlock(context);

  return QMCKL_SUCCESS;
}

5 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");
 }

Author: TREX CoE

Created: 2022-02-09 Wed 13:39

Validate