How do I provide access to the library I wrap with cgo?

huangapple go评论102阅读模式
英文:

How do I provide access to the library I wrap with cgo?

问题

我正在尝试为一个C库编写绑定,具体来说是libnfc。我的当前代码可以在Github上找到。

libnfc中的一个核心结构是设备(device)。它由Go类型Device表示。

type Device struct {
    d *C.nfc_device
}

libnfc中的所有操作Device的函数都是它的方法。现在,还有其他的C库(例如libfreefare)其API操作的是nfc_device。为了模块化,我希望将每个我封装的库的代码放在自己的模块中。这就导致了一个问题,我无法从其他模块中访问私有结构成员。我考虑了以下解决方案:

  • d作为Device的公共成员

    这样可以轻松地从其他模块中访问底层的nfc_device,但也容易绕过类型安全。此外,我不知道cgo是否能够识别来自不同模块的外部类型的指针。最后,如果我更改Device类型的结构,我会失去灵活性。

  • 添加一个访问器func (Device) GetCPtr() unsafe.Pointer

    这解决了上述问题,但引入了一个新问题,即在可能甚至不导入unsafe的模块中,你突然可以访问一个unsafe.Pointer

  • 添加一个访问器func (Device) GetCPtr() uintptr

    这解决了上述问题,因为你必须手动转换结果以获得正确的指针。

还有其他我忽略的方法吗?有没有更好、更符合惯例的方法来访问底层的nfc_device

英文:

I am trying to write bindings for a C library, specifically the libnfc. My current code is available on Github.

One of the central structures in the libnfc is the device. It is represented by the Go type Device.

type Device struct {
	d *C.nfc_device
} 

All functions inside the libnfc that operate on a Device are methods of it. Now, there are other C libraries (e.g. the libfreefare) whose APIs operates on nfc_devicees. For the sake of modularity, I want to place the code for each library I wrap into its own module. This leads to the problem, that I can't access private structure members from within other modules. I thought about the following solutions:

  • Make d a public member of Device

    This would make it easy to access the underlying nfc_device from within other modules, but it makes it also easy to sidestep type safety. Additionally, I don't know whether cgo recognizes pointers to foreign types if they come from different modules. Finally, I lose flexibility if I change the structure of the Device type.

  • Add an accessor func (Device) GetCPtr() unsafe.Pointer

    This solves the issues above but introduces the new issue that you suddently have access to an unsafe.Pointer in a module that might not even import unsafe.

  • Add an accessor func (Device) GetCPtr() uintptr

    This solves the aforementioned issue, as you have to manually cast the result to get a proper pointer.

Are there any ways I missed? Is there a better, more idiomatic way to provide access to the underlying nfc_device?

答案1

得分: 1

我一般支持你的第三个提议,因为这是reflect包处理这个问题的方式

你还可以在你的libnfc包装器中只公开一个接口,例如:

type NFCDevice interface {
    Read() ([]byte, error)
    Write() ([]byte, error)
    // ...
}

现在你有一个安全的公共API。

此外,你的device类型实现了一个函数:

func (d *device) NfcDevice() *C.nfc_device {
    return d.nfc_device
}

你可以在其他包装器中使用这个函数,通过断言你的NFCDevice实现接口:

interface {
    NfcDevice() *C.nfc_device
}

你可以在其他包装器中即时创建这个接口。这样一来,程序员必须有意识地做一些事情才能访问你的device的内部工作方式。

英文:

I'm generally in favour with the third proposal of yours as this is the way the reflect package
handles this issue
.

What you could also do is to expose only an interface in your libnfc wrapper, e.g.

type NFCDevice interface {
    Read() ([]byte, error)
    Write() ([]byte, error)
    // ...
}

Now you have a public API that is safe.

Additionally, your device type implements a function

func (d *device) NfcDevice() *C.nfc_device {
    return d.nfc_device
}

which you can use in your other wrappers by asserting your NFCDevice to implement the
interface

interface {
    NfcDevice() *C.nfc_device
}

which you can create on the fly in the other wrappers. This way a programmer has to deliberately
do something to access the inner workings of your device.

huangapple
  • 本文由 发表于 2014年2月9日 08:21:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/21653547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定