2

我有以下示例代码:

@interface S1 : NSObject
{
    void(*fn_)();
}
@end
@implementation S1
- (void) set:(BOOL)f
{
    if (f)
    {
        struct A { static void f() { std::cout << "1" << std::endl; } };
        fn_ = A::f;
    }
    else
    {
        struct A { static void f() { std::cout << "2" << std::endl; } };
        fn_ = A::f;
    }
}
- (void) test { fn_(); }
@end

struct S2
{
    void set(BOOL f)
    {
        if (f)
        {
            struct A { static void f() { std::cout << "1" << std::endl; } };
            fn_ = A::f;
        }
        else
        {
            struct A { static void f() { std::cout << "2" << std::endl; } };
            fn_ = A::f;
        }
    }
    void test() { fn_(); }
    void(*fn_)();
};

int main(int argc, const char * argv[])
{
    auto s1 = [[S1 alloc] init];
    [s1 set:TRUE];
    [s1 test];
    [s1 set:FALSE];
    [s1 test];

    S2 s2;
    s2.set(TRUE);
    s2.test();
    s2.set(FALSE);
    s2.test();

    return 0;
}

它打印

1
1
1
2

但我期待

1
2
1
2

如果我将第二个结构的名称更改为不同的名称(例如“B”),则始终按预期工作。

不会出现任何警告,因此很难找出您的程序无法正常运行的原因。

这是我的无知还是llvm的错误?

4

2 回答 2

2

它确实似乎是一个clang错误。(需要注意的是没有指定Objective-C++语言的标准,所以“正确”的东西有点悬而未决)

如果我不是编译成可执行代码,而是使用这样的命令生成 LLVM IR

clang -g -std=c++11  -Wall -Wextra localstruct.mm  -emit-llvm -S

-set:方法编译为此,突出显示两个函数指针分配行:

define internal void @"\01-[S1 set:]"(%0* %self, i8* %_cmd, i8 signext %f) uwtable ssp {
  %1 = alloca %0*, align 8
  %2 = alloca i8*, align 8
  %3 = alloca i8, align 1
  store %0* %self, %0** %1, align 8
  call void @llvm.dbg.declare(metadata !{%0** %1}, metadata !1490), !dbg !1491
  store i8* %_cmd, i8** %2, align 8
  call void @llvm.dbg.declare(metadata !{i8** %2}, metadata !1492), !dbg !1491
  store i8 %f, i8* %3, align 1
  call void @llvm.dbg.declare(metadata !{i8* %3}, metadata !1493), !dbg !1494
  %4 = load i8* %3, align 1, !dbg !1495
  %5 = icmp ne i8 %4, 0, !dbg !1495
  br i1 %5, label %6, label %12, !dbg !1495

; <label>:6                                       ; preds = %0
  %7 = load %0** %1, align 8, !dbg !1497
  %8 = load i64* @"OBJC_IVAR_$_S1.fn_", !dbg !1497, !invariant.load !1499
  %9 = bitcast %0* %7 to i8*, !dbg !1497
  %10 = getelementptr inbounds i8* %9, i64 %8, !dbg !1497
  %11 = bitcast i8* %10 to void ()**, !dbg !1497

store void ()* @"_ZN10-[S1 set:]1A1fEv", void ()** %11, align 8, !dbg !1497

  br label %18, !dbg !1500

; <label>:12                                      ; preds = %0
  %13 = load %0** %1, align 8, !dbg !1501
  %14 = load i64* @"OBJC_IVAR_$_S1.fn_", !dbg !1501, !invariant.load !1499
  %15 = bitcast %0* %13 to i8*, !dbg !1501
  %16 = getelementptr inbounds i8* %15, i64 %14, !dbg !1501
  %17 = bitcast i8* %16 to void ()**, !dbg !1501

store void ()* @"_ZN10-[S1 set:]1A1fEv", void ()** %17, align 8, !dbg !1501

  br label %18

; <label>:18                                      ; preds = %12, %6
  ret void, !dbg !1503
}

这两种情况似乎都引用了相同的函数符号_ZN10-[S1 set:]1A1fEv。如果您查看结构中方法的相应代码,它引用了两个:_ZZN2S23setEaEN1A1fEv_ZZN2S23setEaEN1A1fE_0v

FWIW,GCC 的 Objective-C++ 编译器产生了预期的结果。请务必将错误报告给 clang 项目,不要只解决代码中的问题。

于 2012-10-29T18:11:24.503 回答
1

该代码看起来非常像您正在分配一个实例变量以指向实际位于堆栈上的结构的内容。

您看到的输出只是巧合;它可能会在打开优化和/或任何类型的函数调用复杂性时崩溃。

只是一个猜测——我的 C++ 有点生疏了。

于 2012-09-27T21:13:20.433 回答