Управляющие операторы if
gcc 4.4.3 c89
У меня есть некоторые функции, которые инициализируют некоторое оборудование и возвращают true или false. Если false, я должен uninitialize в обратном порядке.
Однако мой код выглядит очень неопрятным со всеми операторами if.
Например, каждая функция может возвращать либо true, либо false. Это образец. Как вы видите, код выглядит очень неопрятным. Я просто ищу любой совет о том, как я могу его очистить, чтобы сделать его более управляемым и, если возможно, масштабируемым?
Большое спасибо за любой совет,
if(init_A() == TRUE) {
if(init_B() == TRUE) {
if(init_C() == TRUE) {
if(init_D() == TRUE) {
if(init_E() == TRUE) {
/* ALL STARTED OK */
}
else {
uninit_A();
uninit_B();
uninit_C();
uninit_D();
}
}
else {
uninit_A();
uninit_B();
uninit_C();
}
}
else {
uninit_A();
uninit_B();
}
}
else {
/* Failed to initialize B */
uninit_B();
}
}
else {
/* Failed to start */
}
Ответы
Ответ 1
Это довольно распространенная проблема, когда шаги "init" соответствуют вещам типа malloc()
или lock()
, а шаги "uninit" соответствуют вещам типа free()
и unlock()
. Это особенно проблема, когда ресурсы должны быть освобождены в строго обратном порядке, в котором они были выделены.
Это один случай, когда использование goto
оправдано:
int somefunc()
{
int retval = ERROR;
if (init_A() != TRUE)
goto out_a;
if (init_B() != TRUE)
goto out_b;
if (init_C() != TRUE)
goto out_c;
if (init_D() != TRUE)
goto out_d;
if (init_E() != TRUE)
goto out_e;
/* ALL STARTED OK */
/* ... normal processing here ... */
retval = OK;
uninit_E();
out_e:
uninit_D();
out_d:
uninit_C();
out_c:
uninit_B();
out_b:
uninit_A();
out_a:
return retval;
}
Ответ 2
if(init_A() != TRUE) {
goto EndA;
}
if(init_B() != TRUE) {
goto EndB;
}
if(init_C() != TRUE) {
goto EndC;
}
if(init_D() != TRUE) {
goto EndD;
}
if(init_E() != TRUE) {
goto EndE;
}
...
return;
EndE: uninitD();
EndD: uninitC();
EndC: uninitB();
EndB: uninitA();
EndA: return;
Ответ 3
Я бы запрограммировал массив указателей на функции, вызвал функции в цикле, а затем, если эта функция вернула false, выполните соответствующую функцию uninit_*
.
Вот пример:
void (*inits[5]) (void);
void (*uninits[4]) (void);
int main(void) {
inits[0] = init_A;
inits[1] = init_B;
inits[2] = init_C;
inits[3] = init_D;
inits[4] = init_E;
uninits[0] = uninit_A;
uninits[1] = uninit_B;
uninits[2] = uninit_C;
uninits[3] = uninit_D;
for(int i = 0; i < 5; i++) {
if((*inits[i])() != TRUE) {
int j = (i < 4) ? i : 4;
while(j--) {
(*uninits[j])();
}
break;
}
}
return 1;
}
Ответ 4
BOOL a = FALSE, b = FALSE, c = FALSE, d = FALSE, e = FALSE;
if ( (a = init_A()) && (b = init_B()) && (c = init_C()) && (d = init_D()) && (e = init_E()) )
{
}
else
{
if ( e ) uninit_E();
if ( d ) uninit_D();
if ( c ) uninit_C();
if ( b ) uninit_B();
if ( a ) uninit_A();
}
uninit функции вызываются в прямом порядке, как в вашем коде. Если требуется обратный порядок, просто измените это.
Ответ 5
Если ваши функции uninit_*
могут определить, нужно ли им что-то делать, просто:
if (!init_A() || !init_B() || !init_C() || !init_D() )
{
uninit_C();
uninit_B();
uninit_A();
return FALSE;
}
Ответ 6
Это "обратный порядок"? Для меня обратный порядок таков:
void uninit(int from) {
switch (from) {
/* ... */
case 3: uninit_C(); /* fall_through */
case 2: uninit_B(); /* fall_through */
case 1: uninit_A(); /* fall_through */
case 0: break;
}
}
И процесс init будет выглядеть следующим образом
int count = 0;
if (init_A()) {
count++;
if (init_B()) {
count++;
if(init_C()) {
count++;
if(init_D()) {
count++;
if(init_E()) {
count++;
}
}
}
}
}
if (count == 5) /* ALL OK */;
uninit(count);
Ответ 7
Ограниченное понимание работы C здесь, если вы решите понизить, пожалуйста, скажите мне, почему.
#include <stdio.h>
int init_a() { return 1; }; // succeed
int init_b() { return 1; }; // succeed
int init_c() { return 0; }; // fail
void uninit_a() { printf("uninit_a()\n"); }
void uninit_b() { printf("uninit_b()\n"); }
void uninit_c() { printf("uninit_c()\n"); }
typedef struct _fp {
int (*init)();
void (*uninit)();
} fp;
int init() {
fp fps[] = {
(fp){&init_a, &uninit_a},
(fp){&init_b, &uninit_b},
(fp){&init_c, &uninit_c}
};
unsigned int i = 0, j;
for(; i < sizeof(fps) / sizeof(fp); ++i) {
if(!(*fps[i].init)()) {
for(j = 0; j < i; ++j) {
(*fps[j].uninit)();
}
return -1;
}
}
return 0;
}
int main() {
init();
return 0;
}
Вывод:
uninit_a()
uninit_b()
Это тот же порядок, в котором будет выполнен код в исходном сообщении, но вы можете его отменить (внутренний цикл).
Ответ 8
То, что вы, возможно, ищете, - это управление ресурсами, связанное с областью. С++ традиционно делает это с конструкторами/деструкторами. Но есть способ сделать это по-другому (как на C99, так и на С++), немного нарушив for
-statement. Я написал что-то на этой строке здесь:
управление ресурсами, связанное с областью видимости, для областей видимости.
Ответ 9
У меня нет компилятора, чтобы попробовать это. Но что-то вроде этого может работать?
int (*init[])() = {init_A, init_B, init_C, init_D, init_E};
int (*uninit[])() = {uninit_A, uninit_B, uninit_C, uninit_D, uninit_E};
int main()
{
initfunction(init, 0)
return 0;
}
void initfunction((*init[])(), pos)
{
if(init[pos]() == TRUE)
initfunction(init, pos++)
else
return;
uninit[pos]();
}
Ответ 10
int X = 0;
if(init_A() == TRUE) {
X++;
if(init_B() == TRUE) {
X++;
if(init_C() == TRUE) {
X++;
if(init_D() == TRUE) {
X++;
if(init_E() == TRUE) {
X++;
/* ALL STARTED OK */
}
}
}
}
}
/* You said reverse order which I took to mean this,
* though your did not do it this way. */
switch (X) {
case 5:
return SUCCESS;
case 4:
uninit_D();
case 3:
uninit_C();
case 2:
uninit_B();
case 1:
uninit_A();
return FAILURE;
}
Что-то, что я делаю, чтобы не допускать ошибок в коде, например:
static int do_A(void);
static int do_B(void);
static int do_C(void);
static int do_D(void);
static int do_A(void) {
if (init_A() == FALSE) {
return FALSE;
}
if (do_B() == FALSE) {
uninit_A();
return FALSE;
}
return TRUE;
}
...
static int do_D(void) {
return init_D();
}
Все остальные функции do_ должны выглядеть так же, как do_A.