如何避免编写同一循环的多个版本

Fra*_*ert 1 c c++

在一个大循环中,我目前有一个类似的声明

if (ptr == NULL || ptr->calculate() > 5) 
  {do something} 
Run Code Online (Sandbox Code Playgroud)

其中ptr是在循环之前设置的对象指针,从不更改.

我想避免在循环的每次迭代中将ptr与NULL进行比较.(当前的最终程序就是这样做的,对吗?)一个简单的解决方案是为(ptr == NULL)编写一次循环代码,为(ptr!= NULL)编写一次.但这会增加代码量,使维护更加困难,而且如果相同的大循环出现两次只有一行或两行更改,则看起来很傻.

我能做什么?使用动态值常量可能并希望编译器是智能的?怎么样?

非常感谢!

由Luther Blissett 编辑.OP想要知道是否有更好的方法来删除指针检查:

loop {
 A; 
 if (ptr==0 || ptr->calculate()>5) B;
 C;
}
Run Code Online (Sandbox Code Playgroud)

比复制循环,如下所示:

if (ptr==0) 
loop {
 A; 
 B;
 C;
} 
else loop {
 A;
 if (ptr->calculate()>5) B;
 C;
}
Run Code Online (Sandbox Code Playgroud)

Nor*_*ame 6

我只是想通知你,显然GCC可以在优化器中执行此请求提升.这是一个模型循环(在C中):

struct C
{
 int (*calculate)();
};

void sideeffect1();
void sideeffect2();
void sideeffect3();

void foo(struct C *ptr) 
{
    int i;
    for (i=0;i<1000;i++) 
    {
        sideeffect1();
        if (ptr == 0 || ptr->calculate()>5) sideeffect2(); 
        sideeffect3();
    }
}
Run Code Online (Sandbox Code Playgroud)

用gcc 4.5和-O3编译它给出:

.globl foo
    .type   foo, @function
foo:
.LFB0:
    pushq   %rbp
.LCFI0:
    movq    %rdi, %rbp
    pushq   %rbx
.LCFI1:
    subq    $8, %rsp
.LCFI2:
    testq   %rdi, %rdi        # ptr==0? -> .L2, see below
    je  .L2
    movl    $1000, %ebx
    .p2align 4,,10
    .p2align 3
.L4:
    xorl    %eax, %eax
    call    sideeffect1      # sideeffect1
    xorl    %eax, %eax
    call    *0(%rbp)         # call p->calculate, no check for ptr==0
    cmpl    $5, %eax
    jle .L3
    xorl    %eax, %eax
    call    sideeffect2      # ok, call sideeffect2
.L3:
    xorl    %eax, %eax
    call    sideeffect3
    subl    $1, %ebx
    jne .L4
    addq    $8, %rsp
.LCFI3:
    xorl    %eax, %eax
    popq    %rbx
.LCFI4:
    popq    %rbp
.LCFI5:
    ret
.L2:                       # here's the loop with ptr==0
.LCFI6:
    movl    $1000, %ebx
    .p2align 4,,10
    .p2align 3
.L6:
    xorl    %eax, %eax
    call    sideeffect1    # does not try to call ptr->calculate() anymore
    xorl    %eax, %eax
    call    sideeffect2    
    xorl    %eax, %eax
    call    sideeffect3
    subl    $1, %ebx
    jne .L6
    addq    $8, %rsp
.LCFI7:
    xorl    %eax, %eax
    popq    %rbx
.LCFI8:
    popq    %rbp
.LCFI9:
    ret
Run Code Online (Sandbox Code Playgroud)

clang 2.7(-O3)也是如此:

foo:
.Leh_func_begin1:
    pushq   %rbp
.Llabel1:
    movq    %rsp, %rbp
.Llabel2:
    pushq   %r14
    pushq   %rbx
.Llabel3:
    testq   %rdi, %rdi        # ptr==NULL -> .LBB1_5
    je  .LBB1_5
    movq    %rdi, %rbx   
    movl    $1000, %r14d
    .align  16, 0x90
.LBB1_2:
    xorb    %al, %al          # here's the loop with the ptr->calculate check()
    callq   sideeffect1
    xorb    %al, %al
    callq   *(%rbx)     
    cmpl    $6, %eax
    jl  .LBB1_4
    xorb    %al, %al
    callq   sideeffect2
.LBB1_4:
    xorb    %al, %al
    callq   sideeffect3
    decl    %r14d
    jne .LBB1_2
    jmp .LBB1_7
.LBB1_5:
    movl    $1000, %r14d      
    .align  16, 0x90
.LBB1_6:
    xorb    %al, %al        # and here's the loop for the ptr==NULL case
    callq   sideeffect1
    xorb    %al, %al
    callq   sideeffect2
    xorb    %al, %al
    callq   sideeffect3
    decl    %r14d
    jne .LBB1_6
.LBB1_7:
    popq    %rbx
    popq    %r14
    popq    %rbp
    ret
Run Code Online (Sandbox Code Playgroud)