Go语言高级编程

324课时
1.4K学过
8分

2. 第2章 CGO编程

引言

2.1 快速入门

2.1.1 最简CGO程序

2.1.2 基于C标准库函数输出字符串

2.1.3 使用自己的C函数

2.1.4 C代码的模块化

2.1.5 用Go重新实现C函数

2.1.6 面向C接口的Go编程

2.2 CGO基础

2.2.1 import "C"语句

2.2.2 #cgo语句

2.2.3 build tag 条件编译

2.3 类型转换

2.3.1 数值类型

2.3.2 Go 字符串和切片

2.3.3 结构体、联合、枚举类型

2.3.4 数组、字符串和切片

2.3.5 指针间的转换

2.3.6 数值和指针的转换

2.3.7 切片间的转换

2.4 函数调用

2.4.1 Go调用C函数

2.4.2 C函数的返回值

2.4.3 void函数的返回值

2.4.4 C调用Go导出函数

2.5 内部机制

2.5.1 CGO生成的中间文件

2.5.2 Go调用C函数

2.5.3 C调用Go函数

2.6 实战: 封装qsort

2.6.1 认识qsort函数

2.6.2 将qsort函数从Go包导出

2.6.3 改进:闭包函数作为比较函数

2.6.4 改进:消除用户对unsafe包的依赖

2.7 CGO内存模型

2.7.1 Go访问C内存

2.7.2 C临时访问传入的Go内存

2.7.3 C长期持有Go指针对象

2.7.4 导出C函数不能返回Go内存

2.8 C++ 类包装

2.8.1 C++ 类到 Go 语言对象

2.8.1.1 准备一个 C++ 类

2.8.1.2 用纯C函数接口封装 C++ 类

2.8.1.3 将纯C接口函数转为Go函数

2.8.1.4 包装为Go对象

2.8.2 Go 语言对象到 C++ 类

2.8.2.1 构造一个Go对象

2.8.2.2 导出C接口

2.8.2.3 封装C++对象

2.8.2.4 封装C++对象改进

2.8.3 彻底解放C++的this指针

2.9 静态库和动态库

2.9.1 使用C静态库

2.9.2 使用C动态库

2.9.3 导出C静态库

2.9.4 导出C动态库

2.9.5 导出非main包的函数

2.10 编译和链接参数

2.10.1 编译参数:CFLAGS/CPPFLAGS/CXXFLAGS

2.10.2 链接参数:LDFLAGS

2.10.3 pkg-config

2.10.4 go get 链

2.10.5 多个非main包中导出C函数

课程评价 (0)

请对课程作出评价:
0/300

学员评价

暂无精选评价
20分钟

2.9.5 导出非main包的函数

通过go help buildmode命令可以查看C静态库和C动态库的构建说明:

-buildmode=c-archive
	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
	Build the listed main package, plus all packages it imports,
	into a C shared library. The only callable symbols will
	be those functions exported using a cgo //export comment.
	Requires exactly one main package to be listed.

文档说明导出的C函数必须是在main包导出,然后才能在生成的头文件包含声明的语句。但是很多时候我们可能更希望将不同类型的导出函数组织到不同的Go包中,然后统一导出为一个静态库或动态库。

要实现从是从非main包导出C函数,或者是多个包导出C函数(因为只能有一个main包),我们需要自己提供导出C函数对应的头文件(因为CGO无法为非main包的导出函数生成头文件)。

假设我们先创建一个number子包,用于提供模加法函数:

package number

import "C"

//export number_add_mod
func number_add_mod(a, b, mod C.int) C.int {
	return (a + b) % mod
}

然后是当前的main包:

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测试文件如下:

#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函数了。