代码之家  ›  专栏  ›  技术社区  ›  pankajt

开关柜编程实践

  •  2
  • pankajt  · 技术社区  · 17 年前
    enum SQLErrorCode{
          OK = 0,
          PARTIAL_OK = 1,
          SOMEWHAT_OK = 2,
          NOT_OK = 3,
    };
    

    代码1:

    int error = getErrorCode();
    if((error == SQLErrorCode.PARTIAL_OK) ||
      (error == SQLErrorCode.SOMEWHAT_OK) ||
      (error == SQLErrorCode.NOT_OK) ||
      (error < 0))
       callFunction1();
    else
        callFunction2();
    

    代码2:

    switch(error){
           case SQLErrorCode.PARTIAL_OK: 
                                        callFunction1();
                                        break;
            case SQLErrorCode.SOMEWHAT_OK:
                                        callFunction1();
                                        break;
            case SQLErrorCode.NOT_OK: 
                                        callFunction1();
                                        break;
            default:
                                        callFunction2();
                                        break;
    }
    

    我更喜欢哪种方法。就性能而言,不应该有太大的差别。如何处理切换情况下的错误<0条件。

    编辑: 乔尔的解决方案:

    switch(error) {
         case SQLErrorCode.PARTIAL_OK: 
         case SQLErrorCode.SOMEWHAT_OK:
         case SQLErrorCode.NOT_OK: 
             callFunction1();
             break;
         case SQLErrorCode.OK:
             callFunction2();
             break;
         default:     // error < 0 is handled
             callFunction1();
             break;
    }
    

    Q. 处理错误<0。如果我必须处理其他不属于这里任何情况(包括默认情况)的错误号码。

    7 回复  |  直到 15 年前
        1
  •  6
  •   quark    17 年前

    对这么少的案件来说不是很重要,但是 switch 是 事实上,对于整数来说更快:它可以而且通常是作为一个跳转表而不是一系列 条件检查。

    作为比较,将不同案例的数量增加到10个:

    enum SQLErrorCode{
        CODE0 = 0,
        CODE1 = 1,
        CODE2 = 2,
        CODE3 = 3,
        CODE4 = 4,
        CODE5 = 5,
        CODE6 = 6,
        CODE7 = 7,
        CODE8 = 8,
        CODE9 = 9
    };
    
    enum SQLErrorCode getErrorCode();
    
    void run()
    {
        int error = getErrorCode();
    #ifdef CASE1
        if((error == CODE0) ||       
           (error == CODE1) ||
           (error == CODE2) ||
           (error == CODE3) ||
           (error == CODE4) ||
           (error == CODE5) ||
           (error == CODE6) ||
           (error == CODE7) ||
           (error == CODE8) ||
           (error == CODE9) ||
           (error < 0))
            callFunction1();
        else
            callFunction2();
    #endif
    #ifdef CASE2
        switch(error)
        {
            case CODE0:
                callFunction1();
                break;
        case CODE1:
            callFunction1();
            break;
        case CODE2:
            callFunction1();
            break;
        case CODE3:
            callFunction1();
            break;
        case CODE4:
            callFunction1();
            break;
        case CODE5:
            callFunction1();
            break;
        case CODE6:
            callFunction1();
            break;
        case CODE7:
            callFunction1();
            break;
        case CODE8:
            callFunction1();
            break;
        case CODE9:
            callFunction1();
            break;
        default:
            callFunction2();
            break;
    }
    #endif
    

    }

    现在看看在使用gcc的Linux上构建的第一种情况与第二种情况所生成的程序集。

    如果您查看程序集,您将看到一个显著的区别(对于更大的语句): || S(或) if / else 如果你这样做的话)是一系列的分支,一次一个。这个 转换 变成一个大表:它需要更多的代码,但这意味着它可以在一个跳转中处理。

    (顺便说一下,我们在这里谈论的是C,对吗?不是C?您将无法编译的代码:在C枚举器中,不使用枚举名称作为前缀。所以它是 PARTIAL_OK 没有 SQLErrorCode. )

    代码1 : cc -DCASE1 -s switch.s switch.c

            .file   "1241256.c"
            .text
    .globl run
            .type   run, @function
    run:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $24, %esp
            call    getErrorCode
            movl    %eax, -4(%ebp)
            cmpl    $0, -4(%ebp)
            je      .L2
            cmpl    $1, -4(%ebp)
            je      .L2
            cmpl    $2, -4(%ebp)
            je      .L2
            cmpl    $3, -4(%ebp)
            je      .L2
            cmpl    $4, -4(%ebp)
            je      .L2
            cmpl    $5, -4(%ebp)
            je      .L2
            cmpl    $6, -4(%ebp)
            je      .L2
            cmpl    $7, -4(%ebp)
            je      .L2
            cmpl    $8, -4(%ebp)
            je      .L2
            cmpl    $9, -4(%ebp)
            je      .L2
            cmpl    $0, -4(%ebp)
            jns     .L13
    .L2:
            call    callFunction1
            jmp     .L15
    .L13:
            call    callFunction2
    .L15:
            leave
            ret
            .size   run, .-run
            .ident  "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"
            .section        .note.GNU-stack,"",@progbits
    

    代码2 : cc -DCASE2 -s switch.s switch.c

            .text
    .globl run
            .type   run, @function
    run:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $24, %esp
            call    getErrorCode
            movl    %eax, -4(%ebp)
            cmpl    $9, -4(%ebp)
            ja      .L2
            movl    -4(%ebp), %eax
            sall    $2, %eax
            movl    .L13(%eax), %eax
            jmp     *%eax
            .section        .rodata
            .align 4
            .align 4
    .L13:
            .long   .L3
            .long   .L4
            .long   .L5
            .long   .L6
            .long   .L7
            .long   .L8
            .long   .L9
            .long   .L10
            .long   .L11
            .long   .L12
            .text
    .L3:
            call    callFunction1
            jmp     .L15
    .L4:
            call    callFunction1
            jmp     .L15
    .L5:
            call    callFunction1
            jmp     .L15
    .L6:
            call    callFunction1
            jmp     .L15
    .L7:
            call    callFunction1
            jmp     .L15
    .L8:
            call    callFunction1
            jmp     .L15
    .L9:
            call    callFunction1
            jmp     .L15
    .L10:
            call    callFunction1
            jmp     .L15
    .L11:
            call    callFunction1
            jmp     .L15
    .L12:
            call    callFunction1
            jmp     .L15
    .L2:
            call    callFunction2
    .L15:
            leave
            ret
            .size   run, .-run
            .ident  "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"
            .section        .note.GNU-stack,"",@progbits
    
        2
  •  12
  •   Adam Batkin    17 年前

    在不表达对哪一个最好的偏好的情况下,还有另一种可能性:

    switch(error){
        case SQLErrorCode.PARTIAL_OK: 
        case SQLErrorCode.SOMEWHAT_OK:
        case SQLErrorCode.NOT_OK: 
                                    callFunction1();
                                    break;
        default:
                                    callFunction2();
                                    break;
    }
    
        3
  •  4
  •   Joel    17 年前

    为什么不。。。

    switch(error) {
        case SQLErrorCode.PARTIAL_OK: 
        case SQLErrorCode.SOMEWHAT_OK:
        case SQLErrorCode.NOT_OK: 
             callFunction1();
             break;
        case SQLErrorCode.OK:
             callFunction2();
             break;
        default:
             if (error < 0)
                  callFunction1();
             else
                  callFunction2();
             break;
    }
    

    写起来比开关容易,读起来比中频容易。但它仍然处理错误<0。

    编辑:

    理查德提出了一个很好的观点。我已经编辑过处理已知范围之外的正负错误。

        4
  •  2
  •   Phil Miller    17 年前

    假设 getErrorCode() 返回一个枚举值或小于0的值,如何

    int error = getErrorCode();
    if (error == SQLErrorCode.OK)
      callFunction2(); // Good path
    else
      callFunction1(); // Error / not good enough path
    

    显然,如果您的代码需要 callFunction2() 在 error > 3 ,那么这就行不通了。

        5
  •  2
  •   JRL    17 年前

    您还可以编写一个函数来确定什么是OK错误或NotOK错误代码:

    bool isOK(int code)
    {
      return code == SQLErrorCode.OK;
    }
    

    你的代码可能会变成:

    if (isOk(getErrorCode()))
    {
      callFunction2;
    }
    else
    {
      callFunction1;
    }
    
        6
  •  1
  •   ahawker    17 年前

    我有一阵子没碰过C了,但它是不是掉下来了?所以你可以这样写第二个查克…

    switch(error){
       case SQLErrorCode.PARTIAL_OK: 
        case SQLErrorCode.SOMEWHAT_OK:
        case SQLErrorCode.NOT_OK: 
                                    callFunction1();
                                    break;
        default:
                                    callFunction2();
                                    break;
    

    }

        7
  •  1
  •   David Thornley    17 年前

    每当你有多种方法获得相同的效果,你就会引起混乱。因为,在交换机版本中,您有不同的情况 SqlErrorCode.PARTIAL_OK 和 SqlErrorCode.SOMEWHAT_OK 这意味着它们有不同的处理方式。需要做一些研究来看看发生了什么(并且它与if语句处理不完全兼容,这可能意味着它会让您困惑)。

    在本例中,我将使用一个if语句,因为这个想法是使用一个或另一个函数。