1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 04:22:22 +00:00

ch2-06: 初稿完成

This commit is contained in:
chai2010 2018-01-11 03:20:46 +08:00
parent 8786e6fe07
commit 4f611e892a
2 changed files with 168 additions and 13 deletions

View File

@ -15,7 +15,7 @@
* [2.3. CGO编程基础](ch2-cgo/ch2-03-basic.md) * [2.3. CGO编程基础](ch2-cgo/ch2-03-basic.md)
* [2.4. CGO内存模型](ch2-cgo/ch2-04-memory.md) * [2.4. CGO内存模型](ch2-cgo/ch2-04-memory.md)
* [2.5. C++类包装](ch2-cgo/ch2-05-class.md) * [2.5. C++类包装](ch2-cgo/ch2-05-class.md)
* [2.6. 静态库和动态库(Doing)](ch2-cgo/ch2-06-static-shared-lib.md) * [2.6. 静态库和动态库](ch2-cgo/ch2-06-static-shared-lib.md)
* [2.7. Go实现Python模块(TODO)](ch2-cgo/ch2-07-py-module.md) * [2.7. Go实现Python模块(TODO)](ch2-cgo/ch2-07-py-module.md)
* [2.8. SWIG(TODO)](ch2-cgo/ch2-08-swig.md) * [2.8. SWIG(TODO)](ch2-cgo/ch2-08-swig.md)
* [2.9. 补充说明(TODO)](ch2-cgo/ch2-09-faq.md) * [2.9. 补充说明(TODO)](ch2-cgo/ch2-09-faq.md)

View File

@ -139,23 +139,178 @@ $ dlltool -dllname number.dll --def number.def --output-lib libnumber.a
## 导出C静态库 ## 导出C静态库
CGO不仅可以使用C静态库也可以将Go实现的函数导出为C静态库。我们现在用Go实现前面的number库的莫加法函数。
创建number.go内容如下
```go
package main
import "C"
func main() {}
//export number_add_mod
func number_add_mod(a, b, mod C.int) C.int {
return (a + b) % mod
}
```
根据CGO文档的要求我们需要在main包中导出C函数。对于C静态库构建方式来说会忽略main包中的main函数只是简单导出C函数。采用以下命令构建
```
$ go build -buildmode=c-archive -o number.a
```
在生成number.a静态库的同时cgo还会生成一个number.h文件。
number.h文件的内容如下为了便于显示内容做了精简
```c
#ifdef __cplusplus
extern "C" {
#endif
extern int number_add_mod(int p0, int p1, int p2);
#ifdef __cplusplus
}
#endif
```
其中`extern "C"`部分的语法是为了同时适配C和C++两种语言。核心内容是声明了要导出的number_add_mod函数。
然后我们创建一个`_test_main.c`的C文件用于测试生成的C静态库用下划线作为前著名是让go build构建C静态库时忽略这个文件
```c
#include "number.h"
#include <stdio.h>
int main() {
int a = 10;
int b = 5;
int c = 12;
int x = number_add_mod(a, b, c);
printf("(%d+%d)%%%d = %d\n", a, b, c, x);
return 0;
}
```
通过以下命令编译并运行:
```
$ gcc -o a.out _test_main.c number.a
$ ./a.out
```
使用CGO创建静态库的过程非常简单。
## 导出C动态库 ## 导出C动态库
CGO导出动态库的过程和静态库类似只是将构建模式改为`c-shared`,输出文件名改为`number.so`而已:
```
$ go build -buildmode=c-shared -o number.so
```
`_test_main.c`文件内容不变,然后用以下命令编译并运行:
```
$ gcc -o a.out _test_main.c number.so
$ ./a.out
```
## 导出非main包的函数 ## 导出非main包的函数
## Plugin 通过`go help buildmode`命令可以查看C静态库和C动态库的构建说明
静态注入 ```
C动态库注入 -buildmode=c-archive
Goplugin特性 Build the listed main package, plus all packages it imports,
into a C archive file. The only callable symbols will be those
functions exported using a cgo //export comment. Requires
exactly one main package to be listed.
<!-- -buildmode=c-shared
使用c静态库 Build the listed main package, plus all packages it imports,
使用c动态库 into a C shared library. The only callable symbols will
创建c静态库 be those functions exported using a cgo //export comment.
创建c动态库 Requires exactly one main package to be listed.
```
跨越多个go包导出函数 文档说明导出的C函数必须是在main包导出然后才能在生成的头文件包含声明的语句。但是很多时候我们可能更希望将不太类型的导出函数组织到不同的Go包中然后统一导出为一个静态库或动态库。
要实现从是从非main包导出C函数或者是多个包导出C函数因为只能有一个main包我们需要自己提供导出C函数对应的头文件因为CGO无法为非main包的导出函数生成头文件
假设我们先创建一个number子包用于提供莫加法函数
```go
package number
import "C"
//export number_add_mod
func number_add_mod(a, b, mod C.int) C.int {
return (a + b) % mod
}
```
然后是当前的main包
```go
package main
import "C"
import (
"fmt"
_ "./number"
)
func main() {
println("Done")
}
//export goPrintln
func goPrintln(s *C.char) {
fmt.Println("goPrintln:", C.GoString(s))
}
```
其中我们导入了number子包在number子包中有导出的C函数number_add_mod同时我们在main包也导出了goPrintln函数。
通过以下命令创建C静态库
```
$ go build -buildmode=c-archive -o main.a
```
这时候在生成main.a静态库的同时也会生成一个main.h头文件。但是main.h头文件中只有main包中导出的goPrintln函数的声明并没有number子包导出函数的声明。其实number_add_mod函数在生成的C静态库中是存在的我们可以直接使用。
创建`_test_main.c`测试文件如下:
```c
#include <stdio.h>
void goPrintln(char*);
int number_add_mod(int a, int b, int mod);
int main() {
int a = 10;
int b = 5;
int c = 12;
int x = number_add_mod(a, b, c);
printf("(%d+%d)%%%d = %d\n", a, b, c, x);
goPrintln("done");
return 0;
}
```
我们并没有包含CGO自动生成的main.h头文件而是通过手工方式声明了goPrintln和number_add_mod两个导出函数。这样我们就实现了从多个Go包导出C函数了。
动态库的注意点
-->