深入CGO编程

内容大纲:CGO的价值,快速入门,类型转换,函数调用,CGO内部机制,实战: 包装 C.qsort,内存模型,Go访问C++对象, Go对象导出为C++对象,静态库和动态库,编写Python扩展,编译和链接参数
展开查看详情

1.深入CGO编程 柴树杉) chai2010 ( @ 青云QingCloud 1.1

2.感谢 GopherChina http://gopherchina.org/ 2.1

3. 感谢 韦光京 对CGO的贡献 https://github.com/golang/go/commits?author=wgj- zz 2.2

4. 幻灯片 网址 https://chai2010.cn/talks/cgo2018/ 2.3

5. 个人简介 @青云 应用平台研发工程师 QingCloud , 语言代码 贡献者 Go (ChaiShushan) 语言圣经 翻译者 Go 语言高级编程 作者 开发中 Go ( ...) 多云应用管理平台开发者 OpenPitrix https://github.com/chai2010 https://chai2010.cn 3.1

6.珠三角技术沙龙深圳(2011.02.27) Go集成C&C++代码 3.2

7.珠三角技术沙龙深圳(2011.02.27) 更多图片 3.3

8. 个人签名 当歌曲、传说都已经缄默的时候,只有代码还在 说话! Less is more! 3.4

9. 内容大纲 的价值 CGO 快速入门 类型转换 函数调用 CGO内部机制 实战: 包装 C.qsort 内存模型 4.1

10. 内容大纲(续) 访问C++对象, Go对象导出为C++对象 Go 静态库和动态库 编写Python扩展 编译和链接参数 4.2

11. CGO 的价值 小调查: 有多少人 听说过 或 简单使用过 CGO? 1. 没有银弹, Go语言也不是银弹, 无法解决全部问题 2. 通过CGO可以继承C/C++将近半个世纪的软件积累 3. 通过CGO可以用Go给其它系统写C接口的共享库 4. CGO是Go和其它语言直接通讯的桥梁 CGO 是一个保底的后备技术 CGO 是 Go 的替补技术 5.1

12. 可能的CGO的场景 通过OpenGL或OpenCL使用显卡的计算能力 通过OpenCV来进行图像分析 通过Go编写Python扩展 通过Go编写移动应用 5.2

13. Cgo is not Go https://dave.cheney.net/2016/01/18/cgo-is-not-go 5.3

14. 快速入门 examples/hello-v1/main.go: package main //#include <stdio.h> import "C" func main() { C.puts(C.CString(" 你好, GopherChina 2018!\n")) } 编译运行: $ go run examples/hello-v1/main.go 你好 , GopherChina 2018! 6.1

15. 简单说明 表示启用 CGO import "C" 前的注释表示包含C头文件: import "C" <stdio.h> 表示将 Go 字符串转为 C 字符串 C.CString C.puts调用 语言的puts函数输出 C 字符串 C 6.2

16. 调用自定义的C函数 examples/hello-v2/main.go: package main /* #include <stdio.h> static void SayHello(const char* s) { puts(s); } */ import "C" func main() { C.SayHello(C.CString("Hello, World\n")) } 6.3

17. C 代码模块化 examples/hello-v3/hello.h: extern void SayHello(const char* s); examples/hello-v3/hello.c: #include "hello.h" #include <stdio.h> void SayHello(const char* s) { puts(s); } examples/hello-v3/main.go: //#include "./hello.h" import "C" 6.4

18. C 代码模块化 - 改用Go重写C模块 examples/hello-v4/hello.h: extern void SayHello(/* const */ char* s); examples/hello-v4/hello.go: package main import "C" import "fmt" //export SayHello func SayHello(s *C.char) { fmt.Print(C.GoString(s)) } 函数参数去掉 const 修饰符 hello.c => hello.go 6.5

19. 手中无剑, 心中有剑 examples/hello-v5/hello.go: package main // extern void SayHello(char* s); import "C" import "fmt" func main() { C.SayHello(C.CString("Hello, World\n")) } //export SayHello func SayHello(s *C.char) { fmt.Print(C.GoString(s)) } C语言版本 SayHello 函数实现只存在于心中 面向纯 C 接口的 Go 语言编程 6.6

20. 忘掉心中之剑 // +build go1.10 package main // extern void SayHello(_GoString_ s); import "C" import "fmt" func main() { C.SayHello("Hello, World\n") } //export SayHello func SayHello(s string) { fmt.Print(s) 也是一种 C 字符串 GoString Go 的一切都可以从C理解 6.7

21. 类型转换 指针 - unsafe 包的灵魂 Go字符串和切片的结构 Go指针和C指针之间的转换 数值和指针之间的转换 不同类型指针转换 字符串和切片转换 ... 7.1

22. 指针 - unsafe 包的灵魂 Go 版无类型指针和数值化的指针: var p unsafe.Pointer = nil // unsafe var q uintptr = uintptr(p) // builtin C 版无类型指针和数值化的指针: void *p = NULL; uintptr_t q = (uintptr_t)(p); // <stdint.h> unsafe.Pointer 是 Go指针 和 C指针 转换的中 介 uintptr 是 Go 中 数值 和 指针 转换的中介 7.2

23. unsafe 包 type ArbitraryType int type Pointer *ArbitraryType func Sizeof(x ArbitraryType) uintptr func Alignof(x ArbitraryType) uintptr func Offsetof(x ArbitraryType) uintptr Pointer: 面向编译器无法保证安全的指针类型转换 Sizeof: 值所对应变量在内存中的大小 Alignof:值所对应变量在内存中地址几个字节对齐 Offsetof: 结构体中成员的偏移量 7.3

24. unsafe 包 - C语言版本 typedef void* Pointer; sizeof(type or expression); // C offsetof(type, member); // <stddef.h> alignof(type-id); // C++ 11 C 指针的安全性永远需要自己负责 是关键字, 语义和 Go 基本一致 sizeof 是宏, 展开为表达式, 语义和 Go 基本一致 offsetof 是新特性, 可忽略 alignof 7.4

25. Go 字符串和切片的结构 type reflect.StringHeader struct { Data uintptr Len int } type reflect.SliceHeader struct { Data uintptr Len int Cap int } typedef struct { const char *p; GoInt n; } GoString; typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; reflect 包定义的结构和CGO生成的C结构是一致的 GoString 和 GoSlice 和头部结构是兼容的 7.5

26.实战: int32 和 *C.char 相互转换(A)

27.7.6

28.实战: int32 和 *C.char 相互转换(B) // int32 => *C.char var x = int32(9527) var p *C.char = (*C.char)(unsafe.Pointer(uintptr(x))) // *C.char => int32 var y *C.char var q int32 = int32(uintptr(unsafe.Pointer(y))) 1. 第一步: int32 => uintprt 2. 第二步: uintptr => unsafe.Pointer 3. 第三步: unsafe.Pointer => *C.char 4. 反之亦然 7.7

29.实战: *X 和 *Y 相互转换(A) 7.8