I have a thread in my operating system that is called in a fixed interval and then executes a list of 10-15 distinct functions sequentially. Each function has a return parameter that either says 0 (OK) or not 0 (error). Looks something like this:
while (1) {
error &= function_1();
error &= function_2(some_parameter);
error &= function_3();
handleError(error);
}
However it would be preferred that when one of the functions returns an error, the error is handled immediately and the other functions are not being executed anymore (single error failure).
For two functions I could do an if condition before each function but for 10-15 that would lead to a lot of unnecessary ifs.
For that I would use an array of function pointers that I go through sequentially:
int (*p_functions[3])() = { function_1, function_2, function_3 }
while (1) {
for (int i = 0; i < 3, i++) {
error = p_functions[i];
if (error) {
handle_error(error);
break;
}
}
}
My issue here is that as you can see in the first example my function_2() has a parameter that gets maybe generated by another function beforehand. So I can't deal with functions that have different parameters.
Are there any other ways to solve that? Or maybe with some tricks for pointer casting? I heard dirty casting is a thing?
Given a function that returns 0 on success and 1 on error, you can change the existing code to use ||, which has short circuit behavior and more closely matches what you want to do anyway, instead of &:
while (1) {
error = error || function_1();
error = error || function_2(some_parameter);
error = error || function_3();
handleError(error);
}
Now, once error is set to 1, no further functions will be called.
As far as handling a specific error, you can set the variable with a function's return value shifted a certain amount based on which function failed, then check the bitmap in the error function.
uint32_t error_map = 0;
while (1) {
error_map || (error_map |= (function_1() << 0));
error_map || (error_map |= (function_2(some_parameter) << 1));
error_map || (error_map |= (function_3() << 2));
handleError(error_map);
}
Then in handleError:
if (error_map & (1<<0)) {
// function 1 error
}
if (error_map & (1<<1)) {
// function 2 error
}
if (error_map & (1<<2)) {
// function 3 error
}
If the functions could return any non-zero value on error, you can capture that error code in a separate variable:
uint32_t error = 0, error_map = 0;
while (1) {
error_map||(error_map |= (((error = function_1()) != 0) << 0));
error_map||(error_map |= (((error = function_2(some_parameter)) != 0) << 1));
error_map||(error_map |= (((error = function_3()) != 0) << 2));
handleError(error, error_map);
}
And the above with a macro to make it more readable:
#define RUN_ON_NO_ERROR(error, error_map, index, call) \
((error_map)||((error_map) |= ((((error) = (call)) != 0) << (index))))
uint32_t error = 0, error_map = 0;
while (1) {
RUN_ON_NO_ERROR(error, error_map, 0, function_1());
RUN_ON_NO_ERROR(error, error_map, 1, function_2(some_parameter));
RUN_ON_NO_ERROR(error, error_map, 2, function_3());
handleError(error, error_map);
}
#define E(e,x) e = (e ? e : x)
while (1) {
error = 0;
E(error, function_1());
E(error, function_2(some_parameter));
E(error, function_3());
handleError(error);
}
Isn't too bad; this is the style things like the SV test suite are written in; and the actual error value is preserved.
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