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

学员评价

暂无精选评价
15分钟

4.7.2 插件中读取扩展信息

在本章的第二节我们已经简单讲述过Protobuf插件的工作原理,并且展示了如何生成RPC必要的代码。插件是一个generator.Plugin接口:

type Plugin interface {
	// Name identifies the plugin.
	Name() string
	// Init is called once after data structures are built but before
	// code generation begins.
	Init(g *Generator)
	// Generate produces the code generated by the plugin for this file,
	// except for the imports, by calling the generator's methods P, In,
	// and Out.
	Generate(file *FileDescriptor)
	// GenerateImports produces the import declarations for this file.
	// It is called after Generate.
	GenerateImports(file *FileDescriptor)
}

我们需要在Generate和GenerateImports函数中分别生成相关的代码。而Protobuf文件的全部信息都在*generator.FileDescriptor类型函数参数中描述,因此我们需要从函数参数中提前扩展定义的元数据。

pbgo框架中的插件对象是pbgoPlugin,在Generate方法中首先需要遍历Protobuf文件中定义的全部服务,然后再遍历每个服务的每个方法。在得到方法结构之后再通过自定义的getServiceMethodOption方法提取rest扩展信息:

func (p *pbgoPlugin) Generate(file *generator.FileDescriptor) {
	for _, svc := range file.Service {
		for _, m := range svc.Method {
			httpRule := p.getServiceMethodOption(m)
			...
		}
	}
}

在讲述getServiceMethodOption方法之前我们先回顾下方法扩展的定义:

extend google.protobuf.MethodOptions {
	HttpRule rest_api = 20180715;
}

pbgo为服务的方法定义了一个rest_api名字的扩展,在最终生成的Go语言代码中会包含一个pbgo.E_RestApi全局变量,通过该全局变量可以获取用户定义的扩展信息。

下面是getServiceMethodOption方法的实现:

func (p *pbgoPlugin) getServiceMethodOption(
	m *descriptor.MethodDescriptorProto,
) *pbgo.HttpRule {
	if m.Options != nil && proto.HasExtension(m.Options, pbgo.E_RestApi) {
		ext, _ := proto.GetExtension(m.Options, pbgo.E_RestApi)
		if ext != nil {
			if x, _ := ext.(*pbgo.HttpRule); x != nil {
				return x
			}
		}
	}
	return nil
}

首先通过proto.HasExtension函数判断每个方法是否定义了扩展,然后通过proto.GetExtension函数获取用户定义的扩展信息。在获取到扩展信息之后,我们再将扩展转型为pbgo.HttpRule类型。

有了扩展信息之后,我们就可以参考第二节中生成RPC代码的方式生成REST相关的代码。