drdl18946 2018-02-02 16:37
浏览 416
已采纳

如何将GoLang的struct方法作为C回调传递

In Go source I have

type T struct {
    // some data
}

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

var Obj *T

In C sources I have

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

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

I would like to use Obj.M as callback for register_callback in C.

On MS Windows for winapi I pass smth like C.CallbackFunc(unsafe.Pointer(syscall.NewCallback(Obj.M))) to register_callback for this (not sure is it fully correct, but at least this works). But where is no NewCallback for non-Windows systems.

PS:

  1. I'm sure that callback is registered after T is initialised and removed before T is removed.

  2. I may have multiple instances of T and some of them may be used to callback's 'source' at same time (so T is not some kind of singltone).

  3. Function pointer callbacks in GoLang's wiki uses gateway function, but I don't see how to adequate use it with struct's method.

  • 写回答

1条回答 默认 最新

  • doumi7861 2018-02-13 11:58
    关注

    Base idea:

    Use exported callback as a proxy between C and Go:

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

    and register it like this:

    register_callback_with_data(callback, convertObjToData(obj));
    

    Where are 3 ways: wrong (and easy), limited (medium) and right (hard).

    Wrong (and easy) way:

    Pass pointer to Go struct into C (as in original answer). This is totally wrong because Go runtime can move struct in memory. Usually this operation is transparent (all Go pointers will be updated automatically). But pointers in C memory to this struct will not be updated and program may crash/UB/... when tries to use it. Do not use this way.

    Limited (medium) way:

    Similar to previous, but with Go struct allocated in C memory:

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

    In this case Obj can not be moved by Go runtime because it is in C memory. But now if Obj has pointers to Go memory (fields with *-variables, maps, slices, channels, function-pointers, ...) then this also may cause crash/UB/... This is because:

    1. if there are no (other) Go pointers to the same variable (memory), then Go runtime thinks that this memory is free and can be reused,
    2. or, if there is other Go pointer to same variable (memory), then Go can move this variable in memory.

    So, use this way only if struct has no pointers to Go memory. Usually this means that struct contains only primitive fields (ints, floats, bool).

    Right (and hard) way:

    Assign id (of integer type for example) for each object of type T and pass this id into C. In exported callback you should convert id back to object. This is right way with no limitation, so this way may be used always. But this way requires to maintain some array/slice/map to convert between objects and ids. Moreover, this convertation may require some synchronization for thread-safe (so see sync.Mutex and sync.RWMutex).

    Original answer:

    Not best answer and has restrictions, but no other suggested. In my case I can pass additional data to register_callback. This data will be passed back to callback on each call. So I pass unsafe.Pointer(Obj) as data and use 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)
    }
    

    and register it like this:

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

    PS: but still want to know how to do this better in general case (without additional data).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器