【问题标题】:C nested switches: outer switch's case inside inner switchC嵌套开关:外部开关的外壳内部开关
【发布时间】:2010-12-30 23:53:58
【问题描述】:

我正在为我正在编写的解释器添加协程支持,我想做如下的事情:

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_* 值(因为有几个这样的函数,并且一些枚举有几十个值),但我想一个测试脚本应该可以工作......

【问题讨论】:

  • 当你说你想要“类似下面的东西”然后发布无效代码时,我们应该认为你真正想要什么?如果该代码有效,它会做什么?
  • 是的,PITA 是对的……为什么不在纸上画出一个状态机,在纸上简化它,然后想出至少可以自我记录这个过程的变量的正确名称到一个程度?您正在用这些变量表示一个状态。为什么不使用单个变量来表示状态?
  • 我发布了一个参考,说明我的意思是如何工作。 (但我希望找到一种不那么丑陋的方法。)
  • 说真的,在 Excel 中画一个 3 x 3 的表格,列出要执行的东西和要返回的值。一起阻止条件。 upload.wikimedia.org/wikipedia/commons/0/02/… 你可以使用另一个快捷方式 if(foo_again && bar_other) { ... } 那应该只剩下一个 switch 语句。
  • 可能有用:blog.think-async.com/2009/08/secret-sauce-revealed.html(虽然其中一些是 C++ 特定的,但 switch hack 在 C 中是相同的,它展示了如何从单个逻辑中提取“共同调用”。 )

标签: c language-design interpreter coroutine


【解决方案1】:

您也可以将 { } 放在每个案例中:语句
没有它们,整个案例堆栈将作为一个单元进行评估,因此不能在一个案例中定义变量:

但是通过放置

 case blah:
 {
  // do stuff
 }
 break;

你可以在case语句中放任何你想要的东西。

【讨论】:

    【解决方案2】:

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

    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
      ...
    

    【讨论】:

    • 如果(foo==foo_again) 我希望它在return foo_again; 之后恢复,那就不一样了。
    • @lpthnc:我同意你的方法...听起来像是一个使用嵌套 switch 语句一起编码的有限状态机...但是您的方法更简洁!
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      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。您可以用结果填写表格,还可以通过单元测试对这两个实现进行测试,以确保它们对所有输入执行相同的操作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多