gpt4 book ai didi

c - 如何将 GoLang 结构方法作为 C 回调传递

转载 作者:IT王子 更新时间:2023-10-29 00:45:59 29 4
gpt4 key购买 nike

在 Go 源码中我有

type T struct {
// some data
}

func (t *T)M(arg0 SomeType1) {
// some computations
}

var Obj *T

在我的 C 源代码中

// SomeType1C is equivalent to SomeType1.
typedef void (*CallbackFunc)(SomeType1C);

// callback will be called !after! register_callback function returns.
void register_callback(CallbackFunc callback);

我想使用 Obj.M 作为 C 中 register_callbackcallback

在用于 winapi 的 MS Windows 上,我将类似 C.CallbackFunc(unsafe.Pointer(syscall.NewCallback(Obj.M))) 的 smth 传递给 register_callback 为此(不是当然它是完全正确的,但至少这是有效的)。但是非Windows系统哪里没有NewCallback

附言:

  1. 我确定回调在 T 初始化之后注册,并在 T 被移除之前被移除。

  2. 我可能有多个 T 实例,其中一些可能同时用于回调的“源”(所以 T 不是某种单音)。

  3. Function pointer callbacks 在 GoLang 的 wiki 中使用 gateway function,但我看不出如何将它与 struct 的方法充分结合使用。

最佳答案

基本思路:

使用导出的回调作为 C 和 Go 之间的代理:

//export callback
func callback(data0 SomeType1C, data1 Data){ // data1 - data passed to register_callback_with_data
obj := convertDataToObj(data1)
obj.M(data0)
}

然后这样注册:

register_callback_with_data(callback, convertObjToData(obj));

哪里有 3 种方法:错误的(容易的)、有限的(中等的)和正确的(困难的)。

错误(但容易)的方式:

将指向 Go 结构的指针传递给 C(与原始答案一样)。这是完全错误的,因为 Go 运行时可以在内存中移动结构。通常这个操作是透明的(所有 Go 指针都会自动更新)。但是 C 内存中指向此结构的指针将不会更新,并且程序可能会在尝试使用它时崩溃/UB/...。 不要使用这种方式

有限(中等)方式:

与之前类似,但在 C 内存中分配了 Go 结构:

Obj = (*T)(C.calloc(C.size_t(unsafe.Sizeof(T{}))))

在这种情况下,Obj 不能被 Go 运行时移动,因为它在 C 内存中。但是现在如果 Obj 有指向 Go 内存的指针(带 * 变量的字段、映射、 slice 、 channel 、函数指针,...)那么这也可能导致崩溃/UB/...这是因为:

  1. 如果没有(其他)Go 指针指向同一个变量(内存),那么 Go 运行时认为这 block 内存是空闲的并且可以重用,
  2. 或者,如果有其他 Go 指针指向同一个变量(内存),那么 Go 可以在内存中移动这个变量。

因此,仅当结构没有指向 Go 内存的指针时才使用这种方式。通常这意味着结构仅包含原始字段(整数、 float 、 bool 值)。

正确(且困难)的方式:

为每个 T 类型的对象分配 id(例如整数类型)并将此 id 传递给 C。在导出的回调中,您应该将 id 转换回对象。这是正确的方式,没有限制,所以可以一直使用这种方式。但是这种方式需要维护一些数组/slice/映射以在对象和 id 之间进行转换。此外,此转换可能需要一些线程安全的同步(因此请参阅 sync.Mutexsync.RWMutex)。

原答案:

不是最佳答案并且有限制,但没有其他建议。在我的例子中,我可以将额外的数据传递给 register_callback。此数据将在每次调用时传回 callback。所以我将 unsafe.Pointer(Obj) 作为数据传递并使用 gateway function:

//export callback
func callback(data SomeType1C, additionalData unsafe.Pointer){
obj := (*T)(additionalData) // Get original Obj (pointer to instance of T)
dataGo := *(*SomeType1)(unsafe.Pointer(&data)) // Cast data from C to Go type
obj.M(dataGo)
}

然后这样注册:

register_callback_with_data(callback, unsafe.Pointer(Obj));

PS:但仍然想知道在一般情况下如何更好地做到这一点(没有附加数据)。

关于c - 如何将 GoLang 结构方法作为 C 回调传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48587118/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com