golang qml (go-qml) cgo 参数有指向 Go 指针的 Go 指针

golang qml (go-qml) cgo argument has Go pointer to Go pointer

您好,我正在使用 qml 库来创建 UI。我正在尝试学习如何将信息从 UI (qml) 传递到 "do something" 。 如果 QML 只是一个 UI,它就可以工作。当我这样做时,我可以 运行 很好:

func main() {
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, "usage: %s <qml file>\n", os.Args[0])
        os.Exit(1)
    }
    if err := qml.Run(run); err != nil {
        fmt.Fprintf(os.Stderr, "error: %v\n", err)
        os.Exit(1)
    }
}

func run() error {
    engine := qml.NewEngine()

    engine.On("quit", func() { os.Exit(0) })

    component, err := engine.LoadFile(os.Args[1])
    if err != nil {
        return err
    }
    window := component.CreateWindow(nil)
    window.Show()
    window.Wait()
    return nil
}

然而,当我添加一些代码,尝试 "learn" 来自 UI 的东西时,我得到 运行 时间错误:

panic: runtime error: cgo argument has Go pointer to Go pointer

我添加的代码是:

window.On("visibleChanged", func(visible bool) {
    if (visible) {
            fmt.Println("Width:", window.Int("width"))
    }
})

我 运行正在 "go version go1.6 darwin/amd64" OSX El Capitan

知道为什么吗? google 表明这是 Go 1.6 Beta 中的一个错误,但我正在 运行 安装最新的稳定版本(几天前安装)。

如果这不是一个简单的修复,有人可以解释一下 "why" 发生这种情况吗?

如果你只是玩玩,我建议尝试使用 go 1.5.3。 Go 1.6 在使用 cgo 时引入了一组不同的内存指针约束,这是一个更严格的集合,并且有可能一些为旧版本的 go 设计的 go 包现在违反了一两个 go 规则。

如果是这种情况,让旧包与 go 1.6 一起工作,其中允许 C 调用 go 闭包,可能更难修复。但我还没有这方面的第一手经验。

问题是,当 C 代码存储一个 Go 指针(在本例中,一个指向回调函数的指针)时,垃圾收集器无法在 C 代码中跟踪该指针,并且可能会垃圾收集该指针所在的内存指向是否没有 Go 代码引用它。这将导致 C 代码在尝试访问该内存时崩溃。运行时只知道 C 代码保留了指针(这就是它可能会崩溃的原因),但它不知道 C 代码稍后会用它做什么以及它会保留多长时间。

为了避免这种情况,大多数库使用的技巧是在 Go 中也保留一个指针(例如在全局映射中),以确保内存不受垃圾收集器的影响。 go-qml 使用 this trick as well。这个技巧有效,但编译器和垃圾收集器不知道它有效,它们无法验证你没有犯错误(例如删除 Go 指针,而 C 代码仍然有它的指针)。

在 Go 1.6 中,Go 开发人员决定对此非常严格,他们根本不再允许 C 代码保留 Go 指针。然而,如果你禁用这个检查,在这种情况下一切仍然有效,因为 go-qml 正确地实现了这个技巧(但是它可能在未来崩溃,例如,如果 go 实现了一个移动的垃圾收集器)。

这是关于它的问题:https://github.com/go-qml/qml/issues/170

旁注:在这种特定情况下,传递给 C 的是指向 interface{} 的指针,它本身包含指向函数的指针。这就是您收到错误 "cgo argument has Go pointer to Go pointer" 的原因。不允许这样做的原因是在 C 调用期间保护这些指针免受 GC 的影响更加困难,而且不值得这样做,因此它被禁止 (https://github.com/golang/go/issues/12416#issuecomment-136473697)。 然而,即使允许这样做,代码仍然会违反关于 C 代码保留 Go 指针副本的规则。 这在 Go 1.6 中实际上不是问题,因为它没有实现移动垃圾收集器,但是制定了规则以便以后可以实现它。

感谢您在这里提供的所有帮助。我已经写了一个关于在 Go 中使用 QML 的初学者教程。可以在这里查看。如果我 运行 遇到更多错误并找到修复程序,我会不断更新它。谢谢大家的帮助。 QML/GO 是一个很棒的组合。

https://golog.co/blog/article/Using_Go_with_QML_part_1