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 mmocr的训练错误,结果全为0
  • ¥15 python的qt5界面
  • ¥15 无线电能传输系统MATLAB仿真问题
  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀