关于C#:管理if语句


managing if statements

GCC 4.4.3C89

我有一些函数可以初始化一些硬件并返回true或false。如果为false,则必须按相反的顺序取消初始化。

但是,我的代码看起来非常不整洁,包含了所有的if语句。

例如,每个函数都可以返回"真"或"假"。这是一个样本。正如您所看到的,代码看起来非常不整洁。我只是在寻找任何建议,如何清理它,使它更容易管理,如果可能结痂?

非常感谢您的建议,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;


这是一个很常见的问题,其中"init"步骤对应于malloc()lock()之类的内容,"uninit"步骤对应于free()unlock()之类的内容。当资源必须严格按照分配顺序的相反顺序进行分配时,这尤其是一个问题。

在这种情况下,使用goto是合理的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;
}


我将遍历一个函数指针数组,调用循环中的函数,然后如果该函数返回false,则执行相应的uninit_*函数。

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
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函数按直接顺序调用,如在代码中。如果需要反转顺序,只需更改此顺序。


那是"逆序"?对我来说,逆序是这样的:

1
2
3
4
5
6
7
8
9
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;
    }
}

初始化过程是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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);


如果您的uninit_*函数可以检测它们是否需要执行任何操作,那么您只需:

1
2
3
4
5
6
7
if (!init_A() || !init_B() || !init_C() || !init_D() )
{
  uninit_C();
  uninit_B();
  uninit_A();
  return FALSE;
}


您可能正在寻找的是"范围绑定资源管理"。C++传统上使用构造函数/析构函数来实现这一点。但是,通过滥用EDCOX1的7Ω-语句有一种方式(在C99和C++中)有不同的方式。我在这行写了一些东西:用于范围的范围绑定资源管理。


对C的理解有限,如果你决定投反对票,请告诉我原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#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()
"
); }
void uninit_b() { printf("uninit_b()
"
); }
void uninit_c() { printf("uninit_c()
"
); }

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

输出:

1
2
uninit_a()
uninit_b()

这与执行原始日志中的代码的顺序相同,但您可能希望将其反转(内部循环)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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;
}

我发现自己正在做一些事情来防止自己在代码中出错,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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_u函数都应该与do_u a相似。


我没有编译器来测试这个。但像这样的事情可能奏效?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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]();
}