代码之家  ›  专栏  ›  技术社区  ›  David X

C嵌套开关:外部开关盒内部开关

  •  1
  • David X  · 技术社区  · 16 年前

    我正在为我正在编写的解释器添加协程支持,我想执行以下操作:

    typedef enum {
        bar_stuff,
        bar_other
        } Bar;
    
    typedef enum {
        foo_error=-1,
        foo_none=0,
        foo_again
        } Foo_state;
    
    Foo_state do_foo(Bar bar,Foo_state foo)
        {
        switch(foo)
            {
            case foo_none: //start
            switch(bar)
                {
                case bar_stuff:
                    //do stuff
                    return foo_none;
                case bar_other:
                    //do other stuff
                    return foo_again;
                    case foo_again: //!! this doesn't work
                        /* edit: this is supposed to be a case of
                         *     switch(foo), not switch(bar)
                         */
                    //do more other stuff
                    return foo_none;
                default:
                    //stuff
                    return foo_none;
                }
            default:
                //fail
                return foo_error;
            }
        }
    

    显然这不起作用(我得到了重复的case值,可选的可能是未定义的behavior/segfault)。我可以把switch(bar)写成if/else if/else链,但我希望有更好的方法。

    如果有区别的话,我会用gcc。

    编辑:

    以下是可行的,但需要维护的一个PITA:

    Foo_state do_foo2(Bar bar,Foo_state foo)
        {
        switch(foo)
            {
            case foo_none:  goto case_foo_none;
            case foo_again: goto case_foo_again;
            default:
                //fail
                return foo_error;
            }
        case_foo_none: //start
        switch(bar)
            {
            case bar_stuff:
                //do stuff
                return foo_none;
            case bar_other:
                //do other stuff
                return foo_again;
                case_foo_again:
                //do more other stuff
                return foo_none;
            default:
                //stuff
                return foo_none;
            }
        }
    

    编辑2:

    好吧,这似乎并没有产生上述“更好的方式”,所以我想知道是否有人预见到这样写会有问题:

    Foo_state do_foo3(Bar bar,Foo_state foo)
        {
        switch(foo)
            {
            case foo_none: //start
            if(bar == bar_stuff)
                {
                printf("do stuff\n");
                return foo_none;
                }
            else if(bar == bar_other)
                {
                printf("do other stuff\n");
                return foo_again;
                case foo_again: //continue
                printf("do more other stuff\n");
                return foo_none;
                }
            else
                {
                printf("stuff\n");
                return foo_none;
                }
            default:
                //fail
                return foo_error;
            }
        }
    

    我看到的问题是缺少一个bar*value(因为有几个这样的函数,有些枚举有几十个值),但是我认为这个函数的测试脚本应该可以工作。。。

    4 回复  |  直到 16 年前
        1
  •  6
  •   Martin Beckett    16 年前

    您也可以将{}放在每个case:statement中
    如果没有它们,整个案例堆栈将作为一个单元进行计算,因此不能在一个案例中定义任何变量:

    但是通过

     case blah:
     {
      // do stuff
     }
     break;
    

    你可以把任何你想要的东西放进案件陈述里。

        2
  •  4
  •   Hamish Grubijan    16 年前

    嗯。。。使用卡诺图的等价物来简化逻辑

    if (cond1 && cond2) {
      doX();
      return;
    }
    if (cond3 && cond4) {
      doY();
      return;
    }
    // Sometimes you can take shortcuts
    if (cond5) {
       doZ();
    } else {
       doW();
    }
    return;
    

    这段代码是可读的。如果可能的话,最好避免嵌套的东西。

    首先检查最简单的条件将使函数更简单。

    在你的案例中,从以下几点开始:

    Foo_state do_foo2(Bar bar,Foo_state foo) {
      if (foo != foo_none && foo != foo_again) {
        return foo_error;
      }
      ...
    
      if (foo == foo_none) {
        ...
      }
      // Implicit Else
      ...
    
        3
  •  1
  •   sizzzzlerz    16 年前

    一个简单的修复方法是更改bar_uuenum中的值,使它们相对于foo_uuenum是唯一的。但是,这并不能解决代码混乱的问题。为什么要在bar-switch语句中查找foo值?从语法上讲,它是有效的(只要值是唯一的),但它的编码很差。

        4
  •  0
  •   Eric    13 年前

    好吧,看来你的代码做到了:

    bar \ foo   foo_none                          foo_again                             other
    bar_stuff   doStuff, return foo_none          do more other stuff, return foo_none  return foo_error
    bar_other   do other stuff, return foo_again  do more other stuff, return foo_none  return foo_error
    other       stuff, return foo_none            do more other stuff, return foo_none  return foo_error
    

    这就是我所说的卡诺图。下面是最简单的实现:

    Foo_state do_foo2(Bar bar,Foo_state foo) {
      if (foo == foo_again) {
        // do more stuff
        return foo_none;
      }
      if (foo != foo_none) { // other
        return foo_error;
      }
      // foo_none
      if (bar == bar_stuff) {
        // do stuff
        return foo_none;
      }
      if (bar == bar_other) {
        // do other stuff
        return foo_again;
      }
      // At this point bar = other
    
      // stuff
      return foo_none;
    }
    

    我相信这和你的代码一样,但不使用开关和goto。您可以用结果填写一个表,还可以将两个实现通过单元测试,以确保它们对所有输入执行相同的操作。