In languages and/or libraries which do not support exceptions, many/almost all functions return a value indicating success or failure of their operation - the best-known example being perhaps UN*X system calls such as open()
or chdir()
, or some libc functions.
Anyway, when I write C code, it very often ends up looking like this:
int retval;
...
retval = my_function(arg1, arg2);
if (retval != SUCCESS_VALUE) { do_something(); }
retval = my_other_function(arg1, arg2);
if (retval != SUCCESS_VALUE) { do_something_else(); }
Now, what I would like is to just not save the retval anywhere and have errors thrown in exceptions, but I can't have that. What's the next best thing? I know that there's no real solution to this problem, but I would still like to do something.
Some ideas:
assert()
's (but that's not for production code which can't afford to just die).ensure_success(my_function(args)
or ensure_success(my_other_function(args),my_error_handler,error_handler_args)
.Is there any other practice in this matter I might prefer?
Edit:
If no return statement appears in a function definition, control automatically returns to the calling function after the last statement of the called function is executed. In this case, the return value of the called function is undefined.
If the return value of a function is only going to be used as an input parameter of another function, you can pass the return value directly to the function parameter. This is done by putting the function call in the parameters list of the other function call, just like you would a variable.
Try to approach this problem with a sort of scientific curiosity. There are many that claim that C's approach to error handling leads to programmers being more aware of error conditions, paying more attention to errors and where/how they should be treated. Just consider this an exercise (if a bit tedious) of awareness, like meditation :)
Don't fight it. Solve this as much as possible in the spirit of C and your view of things will expand slightly.
Check out this article on C error handling mantra: http://tratt.net/laurie/tech_articles/articles/how_can_c_programs_be_so_reliable
One general answer to your general question is: try to make functions as small as possible so that you can directly return from them on errors. This approach is good in all languages. The rest is an exercise in structuring code.
There are many ways to achieve better error handling. It all depends on what you are trying to do. To implement extensive error handling routines just to call a few functions isn't going to be worth the fuss. In larger code bases, you can consider it.
Better error handling is most often achieved by adding more abstraction layers. For example, if you have these functions
int func1 (int arg1, int arg2)
{
return arg1 == arg2;
}
int func12 (int arg1, int arg2)
{
return arg1 - arg2;
}
and some error handler function:
void err_handler_func1 (int err_code)
{
if(err_code != 0)
{
halt_and_catch_fire();
}
}
then you could group functions and error handlers together. Either by making a struct based data type containing one function and one error handler, and then make an array of such structs. Or by making individual, related arrays accessed with an index:
typedef void(*func_t)(int, int);
typedef void(*err_handler_t)(int);
typedef enum
{
FUNC1,
FUNC2,
...
FUNC_N // not a function name, but the number of items in the enum
} func_name_t;
const func_t function [FUNC_N] =
{
func1,
func2,
...
};
const err_handler_t err_handler [FUNC_N] =
{
err_handler_func,
err_handler_func,
...
}
once you have this, you can make wrap the function calls in a suitable abstraction layer:
void execute_function (int func_n, int arg1, int arg2)
{
err_handler[func_n]( function[func_n](arg1, arg2 );
}
execute_function (FUNC1, 1, 2);
execute_function (FUNC2, 2, 2);
and so on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With