用 cgo 生成用于 cgo 的 C 兼容的结构体
假设(并非完全假设,这里有 demo)你正在编写一个程序包,用于连接 Go 和其它一些提供大量 C 结构体内存的程序。这些结构可能是系统调用的结果,也可能是一个库给你提供的纯粹信息性内容。无论哪种情况,你都希望将这些结构传递给你的程序包的用户,以便他们可以使用这些结构执行操作。在你的包中,你可以直接使用 cgo 提供的 C. 类型。但这有点恼人(这些整型它们没有对应的原生 Go 类型,使得与常规 Go 代码交互需要乱七八糟的强制转换),并且对于其它导入你的包的代码没有帮助。因此,你需要以某种方式使用原生的 Go 结构体。
一种方式是手动为这些 C 结构体的定义你自己的 Go 版本。这有两个缺点。这太枯燥了(还很容易出错),并且不能保证你能获得与 C 完全相同的内存布局(后者通常但并非总是很重要)。幸运的是有一种更好的方法,那就是使用 cgo 的 -godefs 功能或多或少地为你自动生成结构体声明。生成结果并不总是完美的,但可能会为你带来最大的收益。
使用 -godefs 的起点是特殊的 cgo Go 源文件,该文件需要将某些 Go 类型声明为某些 C 类型。例如:
// +build ignore
package kstat
// #include <kstat.h>
import "C"
type IO C.kstat_io_t
type Sysinfo C.sysinfo_t
const Sizeof_IO = C.sizeof_kstat_io_t
const Sizeof_SI = C.sizeof_sysinfo_t
这些常量对于喜欢较真的人很有用,可以用来在后面对比检查 Go 类型的 unsafe.Sizeof() 和 C 类型的大小是否一致。
运行 go tool cgo -godefs .go ,它将打印一系列带有导出字段和所有内容的标准 Go 类型到标准输出。然后,你可以将其保存到文件中并使用。如果你认为 C 类型可能会更改,则应将生成的文件保留下来,这样就避免重新生成文件遇到的很多麻烦。如果 C 类型基本上是固定的,则可以使用 godoc 对生成的输出进行注释。 cgo 会考虑类型匹配问题,它会把原始的 C 结构中存在的 padding 也插入到输出中。
我不知道如果原始的 C 结构体不可能在 Go 中重建出来,cgo 会怎么办。 比如 Go 需要 padding,但是 C 不需要。希望它会指出错误。这是你以后可能要检查这些 sizeof 的原因之一。
-godefs 最大的限制是与 cgo 通常具有的限制相同:它没有对 C 联合类型(union)的真正支持,因为 Go 确实没有这个。如果你的 C 结构体中有联合,你得自己弄清楚如何处理它们;我相信 cgo 把这些转换为大小合适的 uint8 数组,但这对于实际访问内容不是很有用。
这里有两个问题。假设你有一个嵌入了另一个结构体类型的结构体:
struct cpu_stat {
struct cpu_sysinfo cpu_sysinfo;
struct cpu_syswait cpu_syswait;
struct vminfo cpu_vminfo;
}
在这里,你必须给 cgo 一些帮助,方式是在主结构体类型之前创建嵌入结构类型的 Go 版本:
type Sysinfo C.struct_cpu_sysinfo
type Syswait C.struct_cpu_syswait
type Vminfo C.struct_cpu_vminfo
type CpuStat C.struct_cpu_stat
然后 cgo 才能生成正确的内嵌的 Go 结构的 CpuStat 结构。如果不这样做,你将获得一个 CpuStat 结构类型,该结构类型具有不完整的类型信息,其中的 Sysinfo 等字段将引用名为 _Ctype_… 的未在任何地方定义的类型。
顺便说一句,我在这确实是指 Sysinfo ,而不是 Cpu_sysinfo 。cgo 足够聪明,可以从结构字段名称中删除这种常见的前缀。我不知道它的算法是怎样的,但至少是有用的。
第二个问题是嵌入了匿名结构:
struct mntinfo_kstat {
....
struct {
uint32_t srtt;
uint32_t deviate;
} m_timers[4];
....
}
不幸的是,cgo 根本无法处理这种问题。具体可以去看 issue 5253 ,你有两个选择,第一种是使用建议的 CL 修复,这个目前仍然适用于 src/cmd/cgo/gcc.go 并且能够工作(对我来说)。如果你不想构建自己的 Go 工具链(或者如果 CL 不再适用或无法工作),另一种解决方案是创建一个新的 C 头文件,该文件具有整个结构体的变体,通过创建具名结构体去除结构体的匿名化。
struct m_timer {
uint32_t srtt;
uint32_t deviate;
}
struct mntinfo_kstat_cgo {
....
struct m_timer m_timers [4];
....
}
然后,在你的 Go 文件中,
...
// #include "myhacked.h"
...
type MTimer C.struct_m_timer
type Mntinfo C.struct_mntinfo_kstat_cgo
除非你搞错了,否则两个 C 结构体应具有完全相同的大小和布局,并且彼此完全兼容。现在你可以在你的版本上使用 -godefs 了,记住按照前面问题 1 的处理,需要为 m_timer 创建明确的 Go 类型。 如果你飘了(你认为你不在需要重新生成这些内容了),你可以在生成的 Go 文件中逆转这个过程,重新将 MTimer 类型匿名化到结构体中(因为 Go 对匿名结构体有很好的支持)。因为你没有更改实际内容,只是改了类型声明,所以结果应该与原始的布局相同。
PS:-godefs 的输入文件被设置为不被正常 go build 过程构建,因为它只用于 godefs 生成。如果这个文件也被包含在 go build 构建的源码中,你会得到关于 Go 类型多处定义的构建错误。必然的结果是,你不需要将此文件和任何相关 .h 文件与软件包的常规 .go 文件放在同一目录。你可以把他们放在子目录,或者放在完全独立的位置。
(我认为该 package 行在 godefs .go 文件中唯一要做的就是设置 cgo 将在输出中打印的软件包名称。)
via: https://utcc.utoronto.ca/~cks/space/blog/programming/GoCGoCompatibleStructs
作者:ChrisSiebenmann 译者:befovy 校对:polaris1119
用 cgo 生成用于 cgo 的 C 兼容的结构体的更多相关文章
- 生成用于ROM初始化的coe文件---使用matlab
生成用于ROM初始化的coe文件---使用matlab t=0:2*pi/2^12:2*pi; y=0.5*sin(t)+0.5; r=ceil(y*(2^8-1)); fid = fopen('si ...
- 莫烦python教程学习笔记——使用波士顿数据集、生成用于回归的数据集
# View more python learning tutorial on my Youtube and Youku channel!!! # Youtube video tutorial: ht ...
- [GO]通过结构体生成json
package main import ( "encoding/json" "fmt" ) type IT struct { //一定要注意这里的成员变量的名字 ...
- 内核中用于数据接收的结构体struct msghdr(转)
内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...
- go语言之进阶篇通过结构体生成json
1.通过结构体生成json 示例: package main import ( "encoding/json" "fmt" ) //成员变量名首字母必须大写 t ...
- golang 兼容不同json结构体解析实践
线上服务器,同一个web接口有时需要兼容不同版本的结构体.这种情况思路是使用interface{}接收任意类型数据,结合reflect包处理. 如下,http接口调用者会传入不同的json结构数据(单 ...
- go 从表结构生成结构体
package main import ( "fmt" "github.com/gohouse/converter" ) func main() { // 初始 ...
- python脚本将json文件生成C语言结构体
1.引言 以前用过python脚本根据excel生成相关C语言代码,其实本质就是文件的读写,主要是逻辑问题,这次尝试将json文件生成C语言的结构体. 2.代码 这是一个json文件,生成这个结构体的 ...
- CGO 类型(CGO Types) 一
CGO Types C作为一种混合编程语言已经很久了,无论那些广泛使用的包是用何种语言实现的,都导出了和C兼容的API.Go程序调用C程序,可以借助两种工具实现,一种是cgo,另一种是SWIG工具.C ...
随机推荐
- Producter and Consumer
package pinx.thread; import java.util.LinkedList; import java.util.Queue; public class ProducerConsu ...
- CSS背景处理
CSS背景处理 背景样式 背景颜色 如果想让一个区域具有纯色的背景,可以使用background-color进行设置,背景颜色可以是rgb,rgba,#16网页色. <!DOCTYPE html ...
- Bootstrap 3 -> 4 : 居中布局的变化
我们知道,Bootstrap布局的核心是栅格系统,一行有12个栅格. 比如,我想让两个宽度400px左右的div居中显示. 这个时候,我们可以利用栅格的列偏移功能. <div class=&qu ...
- 【揭秘】C语言类型转换时发生了什么?
ID:技术让梦想更伟大 作者:李肖遥 链接:https://mp.weixin.qq.com/s/ZFf3imVaJgeesuhl1Kn9sQ 在C语言中,数据类型指的是用于声明不同类型的变量或函数的 ...
- elementUI form表单验证不通过的三个原因
<el-form :model="form" :rules="rules"> <el-form-item prop="input&q ...
- 题解 洛谷 P4694 【[PA2013]Raper】
首先考虑题目的性质,不难发现光盘的花费是一个凸函数.当生产 \(0\) 张光盘时,其花费为 \(0\),随着光盘生产数的增加,最优情况肯定是先选择工厂便宜的时刻,所以花费会增长越来越快,因此其为一个下 ...
- DJANGO-天天生鲜项目从0到1-011-订单-订单提交和创建
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
- leetcode题库练习_两数之和
题目:两数之和 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元素不能 ...
- ThreadLocal源码分析以及why导致内存泄露
1 ThreadLocal? This class provides thread-local variables. These variables differ from their normal ...
- JSONObject遍历
导入JSONObject和JSONArray所需要的jar包 JSONObject所必需的6个jar包: commons-beanutils-1.7.0.jar commons-collections ...