управление, если заявления - PullRequest
10 голосов
/ 14 сентября 2010

gcc 4.4.3 c89

У меня есть некоторые функции, которые инициализируют некоторое оборудование и возвращают либо true, либо false.Если false, то я должен инициализировать в обратном порядке.

Однако мой код выглядит очень неопрятным со всеми операторами 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 */
}

Ответы [ 10 ]

24 голосов
/ 14 сентября 2010
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;
8 голосов
/ 14 сентября 2010

Это довольно распространенная проблема, когда шаги "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;
}
7 голосов
/ 14 сентября 2010

Я бы перебрал массив указателей на функции, вызвал функции в цикле, затем, если эта функция вернула 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;
}
6 голосов
/ 14 сентября 2010
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();
}

функции удаления, вызываемые в прямом порядке, как в вашем коде.Если требуется обратный порядок, просто измените это.

4 голосов
/ 14 сентября 2010

Это "обратный порядок"?Для меня обратный порядок выглядит так:

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

И процесс инициализации будет выглядеть так:

    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);
4 голосов
/ 14 сентября 2010

Если ваши uninit_* функции могут определить, нужно ли им делать что-либо, вы можете просто:

if (!init_A() || !init_B() || !init_C() || !init_D() )
{
  uninit_C();
  uninit_B();
  uninit_A();
  return FALSE;
}
2 голосов
/ 14 сентября 2010

Возможно, вы ищете "управление ресурсами с привязкой к области" . C ++ традиционно делает это с помощью конструкторов / деструкторов. Но есть способ сделать это по-другому (как в C99, так и в C ++), немного злоупотребив утверждением for. Я написал кое-что на эту строку здесь: управление ресурсами, связанными с областью действия, для областей действия .

2 голосов
/ 14 сентября 2010

Ограниченное понимание C на работе здесь, если вы решите понизить голос, скажите, пожалуйста, почему.код в исходном сообщении будет выполнен в, но вы можете захотеть изменить его (внутренний цикл).

1 голос
/ 14 сентября 2010
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.

1 голос
/ 14 сентября 2010

У меня нет компилятора, чтобы попробовать это. Но что-то подобное может сработать?

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]();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...