- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
在 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_callback
的 callback
。
在用于 winapi 的 MS Windows 上,我将类似 C.CallbackFunc(unsafe.Pointer(syscall.NewCallback(Obj.M)))
的 smth 传递给 register_callback
为此(不是当然它是完全正确的,但至少这是有效的)。但是非Windows系统哪里没有NewCallback
。
附言:
我确定回调在 T
初始化之后注册,并在 T
被移除之前被移除。
我可能有多个 T
实例,其中一些可能同时用于回调的“源”(所以 T
不是某种单音)。
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/...这是因为:
因此,仅当结构没有指向 Go 内存的指针时才使用这种方式。通常这意味着结构仅包含原始字段(整数、 float 、 bool 值)。
正确(且困难)的方式:
为每个 T
类型的对象分配 id(例如整数类型)并将此 id 传递给 C。在导出的回调中,您应该将 id 转换回对象。这是正确的方式,没有限制,所以可以一直使用这种方式。但是这种方式需要维护一些数组/slice/映射以在对象和 id 之间进行转换。此外,此转换可能需要一些线程安全的同步(因此请参阅 sync.Mutex
和 sync.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/
我是一名优秀的程序员,十分优秀!