App2015
App2015

Reputation: 993

returning result or fault to the caller of the function

In all of the examples for Error handling in C and several answers on SO, all programs just exit with a status code. I'm not looking for any of that.

Error is part of input and output and a routine can either return result or fault. I want the function to return with proper details of error instead of just abnormal exit or result in case of success.

Following is not a working but I'm looking for something similar, to indicate both result or error in return of the function.

struct Error {
    int code;
    char *message;
};

int div(int x, int y) {
    if( y == 0){
        struct Error *error = {0, "Division by zero!"};
        return error;
    } else {
        return x / y;
    }
}

int main() {
    printf("%d", div(4, 2));
    printf("%d", div(4, 0));
}

Upvotes: 3

Views: 305

Answers (3)

laker93
laker93

Reputation: 822

For all its flaws, Zed Shaw's 'learn C the hard way' has a nice section on "The C Error Handling Problem" that you may find interesting.

I think one way to tackle this problem is to use debug macros to wrap the global 'errno' value that C provides.

The below 'dbg.h' code is taken from Zed Shaw's book:

dbg.h:

#ifndef __dbg_h__
#define __dbg_h__

#include <stdio.h>
#include <errno.h>
#include <string.h>

#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
        __FILE__, __LINE__, ##__VA_ARGS__)
#endif

#define clean_errno() (errno == 0 ? "None" : strerror(errno))

#define log_err(M, ...) fprintf(stderr,\
        "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
        clean_errno(), ##__VA_ARGS__)

#define log_warn(M, ...) fprintf(stderr,\
        "[WARN] (%s:%d: errno: %s) " M "\n",\
        __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)

#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
        __FILE__, __LINE__, ##__VA_ARGS__)

#define check(A, M, ...) if(!(A)) {\
    log_err(M, ##__VA_ARGS__); errno=0; goto error; }

#define sentinel(M, ...)  { log_err(M, ##__VA_ARGS__);\
    errno=0; goto error; }

#define check_mem(A) check((A), "Out of memory.")

#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
    errno=0; goto error; }

#endif

In your code that you want to check you could then do something like:

#include <stdio.h>
#include "dbg.h"

int div(int x, int y) {
    check ( y != 0, "Division by zero!");
    return x / y;
error:
    log_err("Div by zero error.");
    return 0;
}

int main() {
    printf("%d", div(4, 2));
    printf("%d", div(4, 0));
}

This way your error is caught and handled by the code in 'dbg.h'. Now this has one obvious problem in that when div(x, 0) is called: div returns 0. The problem is div() has to return an int and all ints can be produced by some valid combination of div(x, y) for ints x and y.

One solution is to wrap div in another function:

#include <stdio.h>
#include <stdlib.h>
#include "dbg.h"

char *div_interface(int x, int y) {
    check ( y != 0, "Division by zero!");
    char *buffer = malloc(100);
    sprintf(buffer, "%d", (x / y));
    return buffer;
error:
    log_err("Div by zero error.");
    char *err_ret= strdup("Div by zero error.");
    return err_ret;
}

int main() {
    char *div_1 = div_interface(4, 2);
    printf("%s\n", div_1);
    free(div_1);
    char *div_2 = div_interface(4, 0);
    printf("%s\n", div_2);
    free(div_2);
}

This isn't particularly elegant either though! The caller would then have to do the conversion from char* to an int...

Upvotes: 1

Sandro Kalatozishvili
Sandro Kalatozishvili

Reputation: 602

You can't return structure while function is returning int. But you can return different integers and then handle it something like that.

typedef struct Error {
    int code;
    char message[512];
};

int div(int x, int y, Error *err) {
    if( y == 0) {
        err->code = 0;
        strcpy(err->message, "Division by zero!");
        return -1;
    } else {
        return x / y;
    }
}

int main() {

    Error err;
    int retValue = div(4, 2, &err);

    if (retValue < 0)
    {
        printf("Code: %d, Message: %s\n", err->code, err->message);
    }
    else
    {
        printf("%d\n", retValue);
    }
}

I know that sometimes result may be less than zero but its but it's just a rough example.

Upvotes: 0

Lundin
Lundin

Reputation: 213799

struct Error *error = -> struct Error error =, then make your return type struct Error.

But since you have a struct, returning by value isn't usually a good idea. Furthermore, the return value of a mathematical function is likely expected to be the mathematical result. Therefore a better approach than returning an error, would be to use an optional parameter:

#include <stdio.h>
#include <stdlib.h>

typedef enum
{
  ERR_NONE,
  ERR_DIV_BY_ZERO
} err_code_t;

typedef struct 
{
    err_code_t code;
    const char *message;
} err_t;

int division (int x, int y, err_t* err) 
{
  int result;

  if( y == 0)
  {
    if(err != NULL)
    {
      err->code = ERR_DIV_BY_ZERO;
      err->message = "Division by zero";
    }
    result = 0;
  } 
  else 
  {
    result = x / y;
  }

  return result;
}

void error_handler (const err_t* err)
{
  fprintf(stderr, "Error %d: %s", err->code, err->message);
  exit(EXIT_FAILURE);
}

int main() 
{
  err_t err;
  int result;

  result = division(4, 2, NULL); // NULL meaning don't give any error information
  printf("%d\n", result);

  result = division(4, 0, &err);
  if(err.code != ERR_NONE)
  {
    error_handler(&err);
  }
  printf("%d\n", result);
}

Upvotes: 4

Related Questions