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

时间:2009-12-30 03:05:32

标签: c language-design interpreter coroutine

我正在为我正在写的翻译添加协程支持,我想做类似以下的事情:

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

显然这不起作用(我得到重复的案例值,替代方案可能是未定义的行为/段错误)。我可以将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_ *值(因为有几个这样的函数,有些枚举有几十个值),但我想这个测试脚本应该有用......

4 个答案:

答案 0 :(得分:6)

您也可以将{}放在每个案例中:声明
如果没有它们,整个案例堆栈将被评估为一个单元,因此在一个案例中不能定义任何变量:

但是提出

 case blah:
 {
  // do stuff
 }
 break;

您可以将任何想要的内容放在案例陈述中。

答案 1 :(得分:4)

嗯...使用等效的卡诺图来简化逻辑并使用

完成所有操作
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
  ...

答案 2 :(得分:1)

一个简单的修复方法是更改​​bar_ enum中的值,使它们相对于foo_枚举是唯一的。但是,这并未解决您的代码令人困惑的事实。为什么要在bar_ switch语句中查找foo_值?从语法上讲,它是有效的(只要值是唯一的),但它的编码很差。

答案 3 :(得分:0)

好吧,您的代码似乎是这样做的:

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

我相信这与你的代码完全相同,但不使用switch和gotos。您可以使用结果填写表格,也可以通过单元测试放置两个实现,以确保它们对所有输入执行相同的操作。