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

Merge branch 'master' of github.com:chai2010/advanced-go-programming-book

This commit is contained in:
Xargin 2018-01-11 13:10:38 +08:00
commit 53eeb7e5d5
67 changed files with 1579 additions and 43 deletions

2
.gitignore vendored
View File

@ -24,6 +24,8 @@ _obj
# macOS # macOS
.DS_Store .DS_Store
*.a
*.lib
*.so *.so
*.dll *.dll
*.obj *.obj

View File

@ -15,12 +15,12 @@
* [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. CGO包的组织(Doing)](ch2-cgo/ch2-06-go-get-friendly.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)
* [第三章 Go汇编语言](ch3-asm/readme.md) * [第三章 汇编语言](ch3-asm/readme.md)
* [第四章 Go和脚本语言](ch4-xlang/readme.md) * [第四章 移动平台](ch4-mobile/readme.md)
* [第六章 Go和Web](ch6-web/readme.md) * [第六章 Go和Web](ch6-web/readme.md)
* [6.1. Web开发简介](ch6-web/ch6-01-introduction.md) * [6.1. Web开发简介](ch6-web/ch6-01-introduction.md)
* [6.2. Router请求路由](ch6-web/ch6-02-router.md) * [6.2. Router请求路由](ch6-web/ch6-02-router.md)

View File

@ -185,7 +185,7 @@ fmt.Println("len(s1):", (*reflect.StringHeader)(unsafe.Pointer(&s1)).Len) // 5
fmt.Println("len(s2):", (*reflect.StringHeader)(unsafe.Pointer(&s2)).Len) // 5 fmt.Println("len(s2):", (*reflect.StringHeader)(unsafe.Pointer(&s2)).Len) // 5
``` ```
根据Go语言规范Go语言的源文件都是采用UTF8编码。因此Go源文件中出现的字符串面值常量一般也是UTF8编码的对于转义字符则没有这个限制。提到Go字符串时我们一般都会假设字符串对应的是一个合法的UTF8编码的字符序列。可以用内置的`print`调试函数或`fmt.Print`函数直接打印,也可以用`for range`循环直接遍历UTF8解码后的UNICODE码点值。 根据Go语言规范Go语言的源文件都是采用UTF8编码。因此Go源文件中出现的字符串面值常量一般也是UTF8编码的对于转义字符则没有这个限制。提到Go字符串时我们一般都会假设字符串对应的是一个合法的UTF8编码的字符序列。可以用内置的`print`调试函数或`fmt.Print`函数直接打印,也可以用`for range`循环直接遍历UTF8解码后的Unicode码点值。
下面的“Hello, 世界”字符串中包含了中文字符,可以通过打印转型为字节类型来查看字符底层对应的数据: 下面的“Hello, 世界”字符串中包含了中文字符,可以通过打印转型为字节类型来查看字符底层对应的数据:
@ -210,7 +210,7 @@ fmt.Println("\xe7\x95\x8c") // 打印: 界
![](../images/ch1-03-string-2.png) ![](../images/ch1-03-string-2.png)
Go语言的字符串中可以存放任意的二进制字节序列而且即是UTF8字符序列也可能会遇到坏的编码。如果遇到一个错误的UTF8编码输入将生成一个特别的UNICODE字符‘\uFFFD这个字符在不同的软件中的显示效果可能不太一样在印刷中这个符号通常是一个黑色六角形或钻石形状里面包含一个白色的问号<E58FB7> Go语言的字符串中可以存放任意的二进制字节序列而且即使是UTF8字符序列也可能会遇到坏的编码。如果遇到一个错误的UTF8编码输入将生成一个特别的Unicode字符‘\uFFFD这个字符在不同的软件中的显示效果可能不太一样在印刷中这个符号通常是一个黑色六角形或钻石形状里面包含一个白色的问号<E58FB7>
下面的字符串中我们故意损坏了第一字符的第二和第三字节因此第一字符将会打印为“<EFBFBD>第二和第三字节则被忽略后面的“abc”依然可以正常解码打印错误编码不会向前扩散是UTF8编码的优秀特性之一 下面的字符串中我们故意损坏了第一字符的第二和第三字节因此第一字符将会打印为“<EFBFBD>第二和第三字节则被忽略后面的“abc”依然可以正常解码打印错误编码不会向前扩散是UTF8编码的优秀特性之一
@ -257,7 +257,7 @@ fmt.Printf("%#v\n", []rune("Hello, 世界")) // []int32{19990, 30028}
fmt.Printf("%#v\n", string([]rune{'世', '界'})) // 世界 fmt.Printf("%#v\n", string([]rune{'世', '界'})) // 世界
``` ```
从上面代码的输出结果来看,我们可以发现`[]rune`其实是`[]int32`类型,这里的`rune`只是`int32`类型的别名,并不是重新定义的类型。`rune`用于表示每个UNICODE码点目前只使用了21个bit位。 从上面代码的输出结果来看,我们可以发现`[]rune`其实是`[]int32`类型,这里的`rune`只是`int32`类型的别名,并不是重新定义的类型。`rune`用于表示每个Unicode码点目前只使用了21个bit位。
字符串相关的强制类型转换主要涉及到`[]byte``[]rune`两种类型。每个转换都可能隐含重新分配内存的代价,最坏的情况下它们的运算时间复杂度都是`O(n)`。不过字符串和`[]rune`的转换要更为特殊一些,因为一般这种强制类型转换要求两个类型的底层内存结构要尽量一致,显然它们底层对应的`[]byte``[]int32`类型是完全不同的内部布局,因此这种转换可能隐含重新分配内存的操作。 字符串相关的强制类型转换主要涉及到`[]byte``[]rune`两种类型。每个转换都可能隐含重新分配内存的代价,最坏的情况下它们的运算时间复杂度都是`O(n)`。不过字符串和`[]rune`的转换要更为特殊一些,因为一般这种强制类型转换要求两个类型的底层内存结构要尽量一致,显然它们底层对应的`[]byte``[]int32`类型是完全不同的内部布局,因此这种转换可能隐含重新分配内存的操作。
@ -276,7 +276,7 @@ func forOnString(s string, forBody func(i int, r rune)) {
} }
``` ```
`for range`迭代字符串时每次解码一个UNICODE字符,然后进入`for`循环体,遇到崩坏的编码并不会导致迭代停止。 `for range`迭代字符串时每次解码一个Unicode字符,然后进入`for`循环体,遇到崩坏的编码并不会导致迭代停止。
**`[]byte(s)`转换模拟实现** **`[]byte(s)`转换模拟实现**
@ -325,7 +325,7 @@ func str2runes(s []byte) []rune {
} }
``` ```
因为底层内存结构的差异,字符串到`[]rune`的转换必然会导致重新分配`[]rune`内存空间然后依次解码并复制对应的UNICODE码点值。这种强制转换并不存在前面提到的字符串和字节切片转化时的优化情况。 因为底层内存结构的差异,字符串到`[]rune`的转换必然会导致重新分配`[]rune`内存空间然后依次解码并复制对应的Unicode码点值。这种强制转换并不存在前面提到的字符串和字节切片转化时的优化情况。
**`string(runes)`转换模拟实现** **`string(runes)`转换模拟实现**
@ -552,14 +552,14 @@ func FindPhoneNumber(filename string) []byte {
} }
``` ```
类似的问题,在删除切片元素时可能会遇到。假设切片里存放的是指针对象,那么下面删除末尾元素后,被删除的元素依然被切片底层数组引用,从而导致不能即使被自动垃圾回收器回收(这要依赖回收器的实现方式): 类似的问题,在删除切片元素时可能会遇到。假设切片里存放的是指针对象,那么下面删除末尾元素后,被删除的元素依然被切片底层数组引用,从而导致不能及时被自动垃圾回收器回收(这要依赖回收器的实现方式):
```go ```go
var a []*int{ ... } var a []*int{ ... }
a = a[:len(a)-1] // 本删除的最后一个元素依然被引用, 可能导致GC操作被阻碍 a = a[:len(a)-1] // 本删除的最后一个元素依然被引用, 可能导致GC操作被阻碍
``` ```
保险的方式是先将需要自动内存回收的元素设置为`nil`,保证自动回收器可发现需要回收的对象,然后再进行切片的删除操作: 保险的方式是先将需要自动内存回收的元素设置为`nil`,保证自动回收器可发现需要回收的对象,然后再进行切片的删除操作:
```go ```go
var a []*int{ ... } var a []*int{ ... }
@ -567,12 +567,12 @@ a[len(a)-1] = nil // GC回收最后一个元素内存
a = a[:len(a)-1] // 从切片删除最后一个元素 a = a[:len(a)-1] // 从切片删除最后一个元素
``` ```
当然如果切片存在的周期很短的话可以不用刻意处理这个问题。因为如果切片本身已经可以被GC回收的话切片对应的每个元素自然也就是可以被回收了。 当然如果切片存在的周期很短的话可以不用刻意处理这个问题。因为如果切片本身已经可以被GC回收的话切片对应的每个元素自然也就是可以被回收了。
**切片类型强制转换** **切片类型强制转换**
为了安全,当两个切片类型`[]T``[]Y`的底层原始切片类型不同时Go语言是无法直接转换类型的。不过安全都是有一定代价的有时候这种转换是有它的价值的——可简化编码或者是提升代码的性能。比如在64位系统上需要对一个`[]float64`切片进行高速排序,我们可以将它强制转为`[]int`整数切片,然后以整数的方式进行排序(因为`float64`遵循IEEE754浮点数标准特性当浮点数有序时对应的整数也必然是有序的 为了安全,当两个切片类型`[]T``[]Y`的底层原始切片类型不同时Go语言是无法直接转换类型的。不过安全都是有一定代价的有时候这种转换是有它的价值的——可简化编码或者是提升代码的性能。比如在64位系统上需要对一个`[]float64`切片进行高速排序,我们可以将它强制转为`[]int`整数切片,然后以整数的方式进行排序(因为`float64`遵循IEEE754浮点数标准特性当浮点数有序时对应的整数也必然是有序的
下面的代码通过两种方法将`[]float64`类型的切片转换为`[]int`类型的切片: 下面的代码通过两种方法将`[]float64`类型的切片转换为`[]int`类型的切片:
@ -603,7 +603,7 @@ func SortFloat64FastV2(a []float64) {
} }
``` ```
第一种强制转换是先将切片数据的开始地址转换为一个较大的数组的指针,然后对数组指针对应的数组重新做切片操作。中间需要`unsafe.Pointer`接两个不同类型的指针传递。需要注意的是Go语言实现中非0大小数组的长度不得超过2GB因此需要针对数组元素的类型大小计算数组的最大长度范围`[]uint8`最大2GB`[]uint16`最大1GB以此类推但是`[]struct{}`数组的长度可以超过2GB 第一种强制转换是先将切片数据的开始地址转换为一个较大的数组的指针,然后对数组指针对应的数组重新做切片操作。中间需要`unsafe.Pointer`接两个不同类型的指针传递。需要注意的是Go语言实现中非0大小数组的长度不得超过2GB因此需要针对数组元素的类型大小计算数组的最大长度范围`[]uint8`最大2GB`[]uint16`最大1GB以此类推但是`[]struct{}`数组的长度可以超过2GB
第二种转换操作是分别取到两个不同类型的切片头信息指针,任何类型的切片头部信息底层都是对应`reflect.SliceHeader`结构,然后通过更新结构体方式来更新切片信息,从而实现`a`对应的`[]float64`切片到`c`对应的`[]int`类型切片的转换。 第二种转换操作是分别取到两个不同类型的切片头信息指针,任何类型的切片头部信息底层都是对应`reflect.SliceHeader`结构,然后通过更新结构体方式来更新切片信息,从而实现`a`对应的`[]float64`切片到`c`对应的`[]int`类型切片的转换。

View File

@ -8,7 +8,7 @@
```go ```go
if v, ok := m["key"]; ok { if v, ok := m["key"]; ok {
return value return v
} }
``` ```
@ -66,7 +66,7 @@ func CopyFile(dstName, srcName string) (written int64, err error) {
} }
``` ```
上面的代码虽然能够工作但是隐藏一个bug。如果第`os.Open`调用失败,那么会在没有释放`src`文件资源的情况下返回。虽然我们可以通过在第二个返回语句前添加`src.Close()`调用来修复这个BUG但是当代码变得复杂时类似的问题将很难被发现和修复。我们可以通过`defer`语句来确保每个被正常打开的文件都能被正常关闭: 上面的代码虽然能够工作但是隐藏一个bug。如果第`os.Open`调用失败,那么会在没有释放`src`文件资源的情况下返回。虽然我们可以通过在第二个返回语句前添加`src.Close()`调用来修复这个BUG但是当代码变得复杂时类似的问题将很难被发现和修复。我们可以通过`defer`语句来确保每个被正常打开的文件都能被正常关闭:
```go ```go
func CopyFile(dstName, srcName string) (written int64, err error) { func CopyFile(dstName, srcName string) (written int64, err error) {

View File

@ -1,12 +1,12 @@
# 2.4. CGO内存模型 # 2.4. CGO内存模型
CGO是架接Go语言和C语言的桥梁不仅仅在二进制接口层面实现互通,同时要考虑两种语言的内存模型的差异。如果在CGO处理的跨语言函数调用时涉及指针的传递则可能会出现Go语言和C语言共享某一段内存的场景。我们知道C语言的内存在分配之后就是稳定的但是Go语言因为函数栈的动态伸缩可能导致栈中内存地址的移动。如果C语言持有的是移动之前的Go指针那么以旧指针访问Go对象时会导致程序崩溃。这是Go和C内存模型的最大差异。 CGO是架接Go语言和C语言的桥梁使二者在二进制接口层面实现了互通,但是我们要注意因两种语言的内存模型的差异而可能引起的问题。如果在CGO处理的跨语言函数调用时涉及到了指针的传递则可能会出现Go语言和C语言共享某一段内存的场景。我们知道C语言的内存在分配之后就是稳定的但是Go语言因为函数栈的动态伸缩可能导致栈中内存地址的移动(这是Go和C内存模型的最大差异)。如果C语言持有的是移动之前的Go指针那么以旧指针访问Go对象时会导致程序崩溃。
## Go访问C内存 ## Go访问C内存
在Go语言访问C语言内存是最简单的情形我们在之前的例子中已经见过多次。因此C语言空间的内存是稳定的只要不是被人为提前释放那么在Go语言空间可以放心大胆地使用。 C语言空间的内存是稳定的只要不是被人为提前释放那么在Go语言空间可以放心大胆地使用。在Go语言访问C语言内存是最简单的情形我们在之前的例子中已经见过多次。
因为Go语言实现的现在我们无法在Go语言中创建大于2GB内存的切片具体请参考makeslice实现代码。不过借助cgo技术我们可以在C语言环境创建大于2GB的内存然后转为Go语言的切片使用 因为Go语言实现的限制我们无法在Go语言中创建大于2GB内存的切片具体请参考makeslice实现代码。不过借助cgo技术我们可以在C语言环境创建大于2GB的内存然后转为Go语言的切片使用
```go ```go
package main package main
@ -38,17 +38,17 @@ func main() {
} }
``` ```
例子中我们通过makeByteSlize来创建大于4G内存大小的切片从而绕过了Go语言实现的限制需要代码验证。而freeByteSlice辅助函数用于释放从C语言函数创建的切片。 例子中我们通过makeByteSlize来创建大于4G内存大小的切片从而绕过了Go语言实现的限制需要代码验证。而freeByteSlice辅助函数用于释放从C语言函数创建的切片。
因为C语言内存空间是稳定的基于C语言内存构造的切片也是绝对稳定的不会因为Go语言栈的变化而被移动。 因为C语言内存空间是稳定的基于C语言内存构造的切片也是绝对稳定的不会因为Go语言栈的变化而被移动。
## C临时访问传入的Go内存 ## C临时访问传入的Go内存
cgo之所以存在的一大因素是要继承C/C++语言几十年的软件遗产。而C/C++很多库都是需要通过指针直接处理传入的内存数据的。因此cgo中也有很多需要将Go内存传入C语言函数的场景。 cgo之所以存在的一大因素是为了方便在Go语言中接纳吸收过去几十年来使用C/C++语言软件构建的大量的软件资源。C/C++很多库都是需要通过指针直接处理传入的内存数据的,因此cgo中也有很多需要将Go内存传入C语言函数的应用场景。
假设一个极端场景:我们将一个Go语言内存传入了C语言函数后该Go语言内存位于于另一个goroutinue的栈上。在C语言函数执行期间另一个goroutinue因为栈不足的原因发生了栈的扩展也就是导致了原来的Go语言内存被移动到了新的位置。但是此时此刻C语言函数并不知道该Go语言内存已经移动了位置仍然用之前的地址来操作该内存——也就是将导致内存越界。以上是一个推论真实情况有些差异也就是说C访问传入的Go内存可能是不安全的 假设一个极端场景:我们将一块位于某goroutinue的栈上的Go语言内存传入了C语言函数后在此C语言函数执行期间此goroutinue的栈因为空间不足的原因发生了扩展也就是导致了原来的Go语言内存被移动到了新的位置。但是此时此刻C语言函数并不知道该Go语言内存已经移动了位置仍然用之前的地址来操作该内存——这将将导致内存越界。以上是一个推论真实情况有些差异也就是说C访问传入的Go内存可能是不安全的
当然有RPC远程远程过程调用的经验的用户可能会考虑通过完全传值的方式处理借助C语言内存稳定的特性在C语言空间先开辟同样大小的内存然后将Go的内存填充到C的内存空间返回的内存也是如此处理。下面的例子是这种思路的具体实现 当然有RPC远程过程调用的经验的用户可能会考虑通过完全传值的方式处理借助C语言内存稳定的特性在C语言空间先开辟同样大小的内存然后将Go的内存填充到C的内存空间返回的内存也是如此处理。下面的例子是这种思路的具体实现
```go ```go
package main package main
@ -73,9 +73,9 @@ func main() {
} }
``` ```
需要将Go的字符串传入C语言时先通过`C.CString`将Go语言字符串对应的内存数据复制到在C语言新创建的内存空间。上面例子的处理思路虽然时安全的,但是效率极其低下(因为要多次分配内存并逐个复制元素),同时也极其繁琐。 需要将Go的字符串传入C语言时先通过`C.CString`将Go语言字符串对应的内存数据复制到新创建的C语言内存空间上。上面例子的处理思路虽然是安全的,但是效率极其低下(因为要多次分配内存并逐个复制元素),同时也极其繁琐。
为了简化并高效处理该场景向C语言传入Go语言内存cgo针对该场景定义了专门的规则在CGO调用的C语言函数返回前cgo保证传入的Go语言内存在此期间不会发生移动C语言函数可以大胆地使用Go语言的内存 为了简化并高效处理此种向C语言传入Go语言内存的问题cgo针对该场景定义了专门的规则在CGO调用的C语言函数返回前cgo保证传入的Go语言内存在此期间不会发生移动C语言函数可以大胆地使用Go语言的内存
根据新的规则我们可以直接传入Go字符串的内存 根据新的规则我们可以直接传入Go字符串的内存
@ -99,11 +99,11 @@ func main() {
} }
``` ```
现在的处理方式更加直接,避免的分配额外的内存,完美的解决方案。 现在的处理方式更加直接,且避免了分配额外的内存。完美的解决方案!
任何完美的技术都有被滥用的时候CGO的这种看似完美的规则也是存在隐患的。我们假设调用的C语言函数需要长时间运行那么将会导致被他引用的Go语言内存在C语言返回前不能被移动从而可能间接地导致这个Go内存栈对应的goroutine不能动态伸缩栈内存也就是可能导致这个goroutine被阻塞。因此在需要长时间运行的C语言函数特别是在纯CPU运算之外还可能因为需要等待其它的资源而需要不确定时间才能完成的函数需要谨慎处理传入的Go语言内存。 任何完美的技术都有被滥用的时候CGO的这种看似完美的规则也是存在隐患的。我们假设调用的C语言函数需要长时间运行那么将会导致被他引用的Go语言内存在C语言返回前不能被移动从而可能间接地导致这个Go内存栈对应的goroutine不能动态伸缩栈内存也就是可能导致这个goroutine被阻塞。因此在需要长时间运行的C语言函数特别是在纯CPU运算之外还可能因为需要等待其它的资源而需要不确定时间才能完成的函数需要谨慎处理传入的Go语言内存。
不过需要小心的是在取到Go内存时需要马上传入C语言函数期间不能保存到临时变量后再间接传入C语言函数。因为CGO只能保证在C函数调用之后被传入的Go语言内存不会发生移动它并不能保证在传入C函数之前内存发送变化。 不过需要小心的是在取得Go内存后需要马上传入C语言函数不能保存到临时变量后再间接传入C语言函数。因为CGO只能保证在C函数调用之后被传入的Go语言内存不会发生移动它并不能保证在传入C函数之前内存不发生变化。
以下代码是错误的: 以下代码是错误的:
@ -114,15 +114,15 @@ pb := (*int16)(unsafe.Pointer(tmp))
*pb = 42 *pb = 42
``` ```
因为tmp并不是指针类型在它获取到Go对象地址之后x对象可能会被移动但是因为不是指针类型不会被Go语言运行时通过更新新内存的地址。在非制作类型的tmp保持Go对象的地址和在C语言环境保持Go对象的地址的效果是一样的如果原始的Go对象内存发生了移动Go语言运行时并不会同步更新它们。 因为tmp并不是指针类型在它获取到Go对象地址之后x对象可能会被移动但是因为不是指针类型所以不会被Go语言运行时更新成新内存的地址。在非指针类型的tmp保持Go对象的地址和在C语言环境保持Go对象的地址的效果是一样的如果原始的Go对象内存发生了移动Go语言运行时并不会同步更新它们。
## C长期持有Go指针对象 ## C长期持有Go指针对象
作为一个Go程序员在使用CGO时潜意识会认为总是Go调用C函数。其实CGO中C语言函数也可以回调Go语言实现的函数。特别是我们可以用Go语言写一个动态库导出C语言规范的接口给其它用户调用。当C语言函数调用Go语言函数的时候C语言函数就成了程序的调用方Go语言函数返回的Go对象内存的生命周期也就自然超出了Go语言运行时的管理。简言之我们不能在C语言函数中直接使用Go语言对象的内存。 作为一个Go程序员在使用CGO时潜意识会认为总是Go调用C函数。其实CGO中C语言函数也可以回调Go语言实现的函数。特别是我们可以用Go语言写一个动态库导出C语言规范的接口给其它用户调用。当C语言函数调用Go语言函数的时候C语言函数就成了程序的调用方Go语言函数返回的Go对象内存的生命周期也就自然超出了Go语言运行时的管理。简言之我们不能在C语言函数中直接使用Go语言对象的内存。
虽然Go语言禁止在C语言函数中长期持有Go指针对象但是这种需求是切实存在的。如果需要在C语言中访问Go语言内存对象我们可以将Go语言内存对象在Go语言空间映射为一个int类型的id然后构建此id来间接访问和空着Go语言对象。 虽然Go语言禁止在C语言函数中长期持有Go指针对象但是这种需求是切实存在的。如果需要在C语言中访问Go语言内存对象我们可以将Go语言内存对象在Go语言空间映射为一个int类型的id然后通过此id来间接访问和控制Go语言对象。
以下代码用于将Go对映射为整数类型的ObjectId用完之后需要手工调用free方法释放该对象ID 以下代码用于将Go对映射为整数类型的ObjectId用完之后需要手工调用free方法释放该对象ID
```go ```go
package main package main
@ -179,9 +179,9 @@ func (id *ObjectId) Free() interface{} {
} }
``` ```
我们通过一个map来管理Go语言对象和id对象的映射关系。其中NewObjectId用于创建一个和对象绑定的id而id对象的方法可用于解码出原始的Go对象也可以用于接触id和原始Go对象的绑定。 我们通过一个map来管理Go语言对象和id对象的映射关系。其中NewObjectId用于创建一个和对象绑定的id而id对象的方法可用于解码出原始的Go对象也可以用于结束id和原始Go对象的绑定。
下面一组函以C接口规范导出可以被C语言函数调用 下面一组函以C接口规范导出可以被C语言函数调用
```go ```go
package main package main
@ -224,4 +224,80 @@ func main() {
} }
``` ```
在printString函数中我们通过NewGoString创建一个对应的Go字符串对象返回的其实是一个ID不能直接使用。我们借助PrintGoString函数将id解析为Go语言字符串后打印。该字符串在C语言函数中完全跨越了Go语言的内存管理在PrintGoString调用前即时发生了栈伸缩导致的Go字符串地址发生变化也依然可以正常工作因为该字符串对应的id是稳定的在Go语言空间通过id解码得到的字符串也就是有效的。 在printString函数中我们通过NewGoString创建一个对应的Go字符串对象返回的其实是一个ID不能直接使用。我们借助PrintGoString函数将id解析为Go语言字符串后打印。该字符串在C语言函数中完全跨越了Go语言的内存管理在PrintGoString调用前即使发生了栈伸缩导致的Go字符串地址发生变化也依然可以正常工作因为该字符串对应的id是稳定的在Go语言空间通过id解码得到的字符串也就是有效的。
## 导出C函数不能返回Go内存
在Go语言中Go是从一个固定的虚拟地址空间分配内存。而C语言分配的内存则不能使用Go语言保留的虚拟内存空间。在CGO环境Go语言运行时默认会检查导出返回的内存是否是由Go语言分配的如果是则会抛出运行时异常。
下面是CGO运行时异常的例子
```go
/*
extern int* getGoPtr();
static void Main() {
int* p = getGoPtr();
*p = 42;
}
*/
import "C"
func main() {
C.Main()
}
//export getGoPtr
func getGoPtr() *C.int {
return new(C.int)
}
```
其中getGoPtr返回的虽然是C语言类型的指针但是内存本身是从Go语言的new函数分配也就是由Go语言运行时统一管理的内存。然后我们在C语言的Main函数中调用了getGoPtr函数此时默认将发送运行时异常
```
$ go run main.go
panic: runtime error: cgo result has Go pointer
goroutine 1 [running]:
main._cgoexpwrap_cfb3840e3af2_getGoPtr.func1(0xc420051dc0)
command-line-arguments/_obj/_cgo_gotypes.go:60 +0x3a
main._cgoexpwrap_cfb3840e3af2_getGoPtr(0xc420016078)
command-line-arguments/_obj/_cgo_gotypes.go:62 +0x67
main._Cfunc_Main()
command-line-arguments/_obj/_cgo_gotypes.go:43 +0x41
main.main()
/Users/chai/go/src/github.com/chai2010/advanced-go-programming-book/examples/ch2-xx/return-go-ptr/main.go:17 +0x20
exit status 2
```
异常说明cgo函数返回的结果中含有Go语言分配的指针。指针的检查操作发生在C语言版的getGoPtr函数中它是由cgo生成的桥接C语言和Go语言的函数。
下面是cgo生成的C语言版本getGoPtr函数的具体细节在cgo生成的`_cgo_export.c`文件定义):
```c
int* getGoPtr()
{
__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();
struct {
int* r0;
} __attribute__((__packed__)) a;
_cgo_tsan_release();
crosscall2(_cgoexp_95d42b8e6230_getGoPtr, &a, 8, _cgo_ctxt);
_cgo_tsan_acquire();
_cgo_release_context(_cgo_ctxt);
return a.r0;
}
```
其中`_cgo_tsan_acquire`是从LLVM项目移植过来的内存指针扫描函数它会检查cgo函数返回的结果是否包含Go指针。
需要说明的是cgo默认对返回结果的指针的检查是有代价的特别是cgo函数返回的结果是一个复杂的数据结构时将花费更多的时间。如果已经确保了cgo函数返回的结果是安全的话可以通过设置环境变量`GODEBUG=cgocheck=0`来关闭指针检查行为。
```
$ GODEBUG=cgocheck=0 go run main.go
```
关闭cgocheck功能后再运行上面的代码就不会出现上面的异常的。但是要注意的是如果C语言使用期间对应的内存被Go运行时释放了将会导致更严重的崩溃问题。cgocheck默认的值是1对应一个简化版本的检测如果需要完整的检测功能可以将cgocheck设置为2。
关于cgo运行时指针检测的功能详细说明可以参考Go语言的官方文档。

View File

@ -0,0 +1,316 @@
# 2.6. 静态库和动态库
CGO在使用C/C++资源的时候一般有三种形式:直接使用源码;链接静态库;链接动态库。直接使用源码就是在`import "C"`之前的注释部分包含C代码或者在当前包中包含C/C++源文件。链接静态库和动态库的方式比较类似都是通过在LDFLAGS选项指定要链接的库方式链接。本节我们主要关注CGO中如何处理静态库和动态库相关的问题。
## 使用C静态库
如果CGO中引入的C/C++资源有代码而且代码规模也比较小直接使用源码是最理想的方式。很多时候我们并没有源代码或者从C/C++源代码的构建过程异常复杂这种时候使用C静态库也是一个不错的选择。静态库因为是静态链接最终的目标程序并不会产生额外的运行时依赖而且也不会出现动态库特有的跨运行时资源管理的错误。不过静态库对链接阶段会有一定要求静态库一般包含了全部的代码里面会有大量的符号如果不同静态库之间出现符号冲突会导致链接的失败。
我们先用纯C语言构造一个简单的静态库。我们要构造的静态库名叫number库中只有一个number_add_mod函数用于表示数论中的模加法运算。number库的文件都在number目录下。
`number/number.h`头文件只有一个纯C语言风格的函数声明
```c
int number_add_mod(int a, int b, int mod);
```
`number/number.c`对应函数的实现:
```c
#include "number.h"
int number_add_mod(int a, int b, int mod) {
return (a+b)%mod;
}
```
因为CGO使用的是GCC命令来编译和链接C和Go桥接的代码。因此静态库也必须是GCC兼容的格式。
通过以下命令可以生成一个libnumber.a的静态库
```
$ cd ./number
$ gcc -c -o number.o number.c
$ ar rcs libnumber.a number.o
```
生成libnumber.a静态库之后我们就可以在CGO中使用该资源了。
创建main.go文件如下
```go
package main
//#cgo CFLAGS: -I./number
//#cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//#include "number.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.number_add_mod(10, 5, 12))
}
```
其中有两个#cgo命令分别是编译和链接参数。CFLAGS通过`-I./number`将number库对应头文件所在的目录加入头文件检索路径。LDFLAGS通过`-L${SRCDIR}/number`将编译后number静态库所在目录加为链接库检索路径`-lnumber`表示链接libnumber.a静态库。需要注意的是在链接部分的检索路径不能使用相对路径这是由于C/C++代码的链接程序所限制我们必须通过cgo特有的`${SRCDIR}`变量将源文件对应的当前目录路径展开为绝对路径因此在windows平台中绝对路径不能有空白符号
因为我们有number库的全部代码我们可以用go generate工具来生成静态库或者是通过Makefile来构建静态库。因此发布CGO源码包时我们并不需要提前构建C静态库。
因为多了一个静态库的构建步骤这种使用了自定义静态库并已经包含了静态库全部代码的Go包无法直接用go get安装。不过我们依然可以通过go get下载然后用go generate触发静态库构建最后才是go install安装来完成。
为了支持go get命令直接下载并安装我们C语言的`#include`语法可以将number库的源文件链接到当前的包。
创建`z_link_number_c.c`文件如下:
```c
#include "./number/number.c"
```
然后在执行go get或go build之类命令的时候CGO就是自动构建number库对应的代码。这种技术是在不改变静态库源代码组织结构的前提下将静态库转化为了源代码方式引用。这种CGO包是最完美的。
如果使用的是第三方的静态库,我们需要先下载安装静态库到合适的位置。然后在#cgo命令中通过CFLAGS和LDFLAGS来指定头文件和库的位置。对于不同的操作系统甚至同一种操作系统的不同版本来说,这些库的安装路径可能都是不同的,那么如何在代码中指定这些可能变化的参数呢?
在Linux环境有个一个pkg-config命令可以查询要使用某个静态库或动态库时的编译和链接参数。我们可以在#cgo命令中直接使用pkg-config命令来生成编译和链接参数。而且还可以通过PKG_CONFIG环境变量订制pkg-config命令。因为不同的操作系统对pkg-config命令的支持不尽相同通过该方式很难兼容不同的操作系统下的构建参数。不过对于Linux等特定的系统pkg-config命令确实可以简化构建参数的管理。关于pkg-config的使用细节我们不再深入展开大家可以自行参考相关文档。
## 使用C动态库
动态库出现的初衷是对于相同的库多个进程可以共享一个动态库可以节省内存和磁盘资源。但是在磁盘和内存已经白菜价的今天动态库能节省的空间已经微不足道了那么动态库还有哪些存在的价值呢从库开发角度来说动态库可以隔离不同动态库之间的关系减少链接时出现符号冲突的风险。而且对于windows等平台动态库是跨越VC和GCC不太编译器平台的唯一的可行方式。
对于CGO来说使用动态库和静态库是一样的因为动态库也必须要有一个小的静态导出库用于链接动态库Linux下可以直接链接so文件但是在Windows下必须为dll创建一个`.a`文件用于链接。我们还是以前面的number库为例来说明如何以动态库方式使用。
对于在macOS和Linux系统下的gcc环境我们可以用以下命令创建number库的的动态库
```
$ cd number
$ gcc -shared -o libnumber.so number.c
```
因为动态库和静态库的基础名称都是libnumber只是后缀名不同而已。因此Go语言部分的代码和静态库版本完全一样
```go
package main
//#cgo CFLAGS: -I./number
//#cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//#include "number.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.number_add_mod(10, 5, 12))
}
```
编译时GCC会自动找到libnumber.a或libnumber.so进行链接。
对于windows平台我们还可以用VC工具来生成动态库windows下有一些复杂的C++库只能用VC构建。我们需要先为number.dll创建一个def文件用于控制要导出到动态库的符号。
number.def文件的内容如下
```
LIBRARY number.dll
EXPORTS
number_add_mod
```
其中第一行的LIBRARY指明动态库的文件名然后的EXPORTS语句之后是要导出的符号名列表。
现在我们可以用以下命令来创建动态库需要进入VC对于的x64命令行环境
```
$ cl /c number.c
$ link /DLL /OUT:number.dll number.obj number.def
```
这时候会为dll同时生成一个number.lib的导出库。但是在CGO中我们无法使用lib格式的链接库。
要生成`.a`格式的导出库需要通过mingw工具箱中的dlltool命令完成
```
$ dlltool -dllname number.dll --def number.def --output-lib libnumber.a
```
生成了libnumber.a文件之后就可以通过`-lnumber`链接参数进行链接了。
需要注意的是在运行时需要将动态库放到系统能够找到的位置。对于windows来说可以将动态库和可执行程序放到同一个目录或者将动态库所在的目录绝对路径添加到PATH环境变量中。对于macOS来说需要设置DYLD_LIBRARY_PATH环境变量。而对于Linux系统来说需要设置LD_LIBRARY_PATH环境变量。
## 导出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动态库
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包的函数
通过`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子包用于提供莫加法函数
```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函数了。

View File

@ -1,3 +1,3 @@
# 第二章 CGO编程 # 第二章 CGO编程
C/C++经过几十年的发展已经积累了庞大的软件资产它们很多久经考验而且性能已经足够优化。Go语言必须能够站在C/C++这个巨人的肩膀之上有了海量的C/C++软件资产兜底之后我们才可以放心愉快地用Go语言编程。C语言作为一个通用语言很多库会选择提供一个C兼容的API然后用其他不同的编程语言实现。Go语言通过自带的一个叫CGO的工具来支援C语言函数调用同时我们可以用Go语言导出C东塔库接口给其它语言使用。本章主要讨论CGO编程中涉及的一些问题。 C/C++经过几十年的发展已经积累了庞大的软件资产它们很多久经考验而且性能已经足够优化。Go语言必须能够站在C/C++这个巨人的肩膀之上有了海量的C/C++软件资产兜底之后我们才可以放心愉快地用Go语言编程。C语言作为一个通用语言很多库会选择提供一个C兼容的API然后用其他不同的编程语言实现。Go语言通过自带的一个叫CGO的工具来支持C语言函数调用同时我们可以用Go语言导出C动态库接口给其它语言使用。本章主要讨论CGO编程中涉及的一些问题。

3
ch4-mobile/readme.md Normal file
View File

@ -0,0 +1,3 @@
# 第四章 移动平台
TODO

View File

@ -1,3 +0,0 @@
# 第四章 Go和脚本语言
TODO

View File

@ -228,7 +228,7 @@ func (r *Router) Use(m middleware) {
func (r *Router) Add(route string, h http.Handler) { func (r *Router) Add(route string, h http.Handler) {
var mergedHandler = h var mergedHandler = h
for i := len(r.middlewareChain); i>=0; i-- { for i := len(r.middlewareChain) - 1; i >= 0; i-- {
mergedHandler = r.middlewareChain[i](mergedHandler) mergedHandler = r.middlewareChain[i](mergedHandler)
} }

1
chx-unknown/readme.md Normal file
View File

@ -0,0 +1 @@
# 未知章节

View File

@ -0,0 +1,28 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
// go run x.go
// GODEBUG=cgocheck=0 go run x.go
// panic: runtime error: cgo result has Go pointer
/*
extern int* getGoPtr();
static void Main() {
int* p = getGoPtr();
*p = 42;
}
*/
import "C"
func main() {
C.Main()
}
//export getGoPtr
func getGoPtr() *C.int {
return new(C.int)
}

View File

@ -0,0 +1,20 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
cd mystring && make
go build -o a.out
LD_LIBRARY_PATH=$(shell pwd)/mystring ./a.out
macos:
cd mystring && make
go build -o a.out
DYLD_LIBRARY_PATH=$(shell pwd)/mystring ./a.out
windows:
# set path
clean:
-cd mystring && make clean
-rm *.a
-rm a.out

View File

@ -0,0 +1,22 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
//#cgo CFLAGS: -I./mystring
//#cgo LDFLAGS: -L${SRCDIR}/mystring -lmystring
//
//#include "mystring.h"
//#include <stdlib.h>
import "C"
import (
"fmt"
"unsafe"
)
func main() {
cs := C.make_string(C.CString("hello"))
defer C.free(unsafe.Pointer(cs))
fmt.Println(C.GoString(cs))
}

View File

@ -0,0 +1,8 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
gcc -shared -o libmystring.so mystring.c
clean:
-rm *.so

View File

@ -0,0 +1,26 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#include "mystring.h"
#include <string.h>
static char buffer[1024];
static char* malloc(int size) {
return &buffer[0];
}
static void free(void* p) {
//
}
char* make_string(const char* s) {
char* p = malloc(strlen(s)+1);
strcpy(p, s);
return p;
}
void free_string(char* s) {
free(s);
}

View File

@ -1,10 +1,6 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>. // Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ // License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main char* make_string(const char* s);
import "C" void free_string(char* s);
func main() {
println("hello cgo")
}

View File

@ -0,0 +1,20 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
go build -buildmode=c-archive -o number.a
gcc -o a.out _test_main.c number.a
./a.out
build_on_win64:
go build -buildmode=c-archive -o number.a
gcc -m64 -shared -o number-win64.dll number-win64.def number.a -Wl,--allow-multiple-definition -static -lstdc++ -lwinmm -lntdll -lWs2_32
lib /def:number-win64.def /machine:x64
build_with_vc:
cl -o a.out.exe _test_main.c number-win64.lib
clean:
-rm *.a *.lib
-rm a.out
-rm a.out.exe

View File

@ -0,0 +1,17 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#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;
}

View File

@ -0,0 +1,13 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
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
}

View File

@ -0,0 +1,4 @@
LIBRARY number-win64.dll
EXPORTS
number_add_mod

View File

@ -0,0 +1,60 @@
/* Created by "go tool cgo" - DO NOT EDIT. */
/* package github.com/chai2010/advanced-go-programming-book/examples/ch2-06/make-clib-dll */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern int number_add_mod(int p0, int p1, int p2);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,12 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
go build -buildmode=c-archive -o main.a
gcc -o a.out _test_main.c main.a
./a.out
clean:
-rm *.a *.lib
-rm a.out
-rm a.out.exe

View File

@ -0,0 +1,19 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#include "main.h"
#include "./number/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);
goPrintln("done");
return 0;
}

View File

@ -0,0 +1,21 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
import "C"
import (
"fmt"
_ "github.com/chai2010/advanced-go-programming-book/examples/ch2-06/make-clib-from-multi-pkg/number"
)
func main() {
println("Done")
}
//export goPrintln
func goPrintln(s *C.char) {
fmt.Println("goPrintln:", C.GoString(s))
}

View File

@ -0,0 +1,60 @@
/* Created by "go tool cgo" - DO NOT EDIT. */
/* package github.com/chai2010/advanced-go-programming-book/examples/ch2-06/make-clib-from-multi-pkg */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern void goPrintln(char* p0);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,11 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package number
import "C"
//export number_add_mod
func number_add_mod(a, b, mod C.int) C.int {
return (a + b) % mod
}

View File

@ -0,0 +1 @@
int number_add_mod(int a, int b, int mod);

View File

@ -0,0 +1,11 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
go build -buildmode=c-shared -o number.so
gcc -o a.out _test_main.c number.so
./a.out
clean:
-rm *.a
-rm a.out

View File

@ -0,0 +1,17 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#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;
}

View File

@ -0,0 +1,13 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
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
}

View File

@ -0,0 +1,60 @@
/* Created by "go tool cgo" - DO NOT EDIT. */
/* package github.com/chai2010/advanced-go-programming-book/examples/ch2-06/make-clib-shared */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern int number_add_mod(int p0, int p1, int p2);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,11 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
go build -buildmode=c-archive -o number.a
gcc -o a.out _test_main.c number.a
./a.out
clean:
-rm *.a
-rm a.out

View File

@ -0,0 +1,17 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#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;
}

View File

@ -0,0 +1,13 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
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
}

View File

@ -0,0 +1,60 @@
/* Created by "go tool cgo" - DO NOT EDIT. */
/* package github.com/chai2010/advanced-go-programming-book/examples/ch2-06/use-clib-static */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern int number_add_mod(int p0, int p1, int p2);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,8 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
go build -buildmode=plugin plugin.go -o plugin.so
clean:
-rm *.so

View File

@ -0,0 +1,23 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
import "plugin"
func main() {
p, err := plugin.Open("plugin.so")
if err != nil {
panic(err)
}
v, err := p.Lookup("V")
if err != nil {
panic(err)
}
f, err := p.Lookup("F")
if err != nil {
panic(err)
}
*v.(*int) = 7
f.(func())() // prints "Hello, number 7"
}

View File

@ -0,0 +1,15 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// +build ignore
// +build go1.10
package main
import "fmt"
func main()
var V int
func F() { fmt.Printf("Hello, number %d\n", V) }

View File

@ -0,0 +1,20 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
cd number && make
go build -o a.out
LD_LIBRARY_PATH=$(shell pwd)/number ./a.out
macos:
cd number && make
go build -o a.out
DYLD_LIBRARY_PATH=$(shell pwd)/number ./a.out
windows:
# set path
clean:
-cd number && make clean
-rm *.a
-rm a.out

View File

@ -0,0 +1,15 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
//#cgo CFLAGS: -I./number
//#cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//#include "number.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.number_add_mod(10, 5, 12))
}

View File

@ -0,0 +1,8 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
gcc -shared -o libnumber.so number.c
clean:
-rm *.so

View File

@ -0,0 +1,5 @@
#include "number.h"
int number_add_mod(int a, int b, int mod) {
return (a+b)%mod;
}

View File

@ -0,0 +1 @@
int number_add_mod(int a, int b, int mod);

View File

@ -0,0 +1,11 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
cd number && make
go run main.go
clean:
-cd number && make clean
-rm *.a
-rm a.out

View File

@ -0,0 +1,15 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
//#cgo CFLAGS: -I./number
//#cgo LDFLAGS: -L${SRCDIR}/number -lnumber
//
//#include "number.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.number_add_mod(10, 5, 12))
}

View File

@ -0,0 +1,10 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
gcc -c -o number.o number.c
ar rcs libnumber.a number.o
-rm *.o
clean:
-rm *.a *.o

View File

@ -0,0 +1,5 @@
#include "number.h"
int number_add_mod(int a, int b, int mod) {
return (a+b)%mod;
}

View File

@ -0,0 +1 @@
int number_add_mod(int a, int b, int mod);

View File

@ -0,0 +1,9 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
go build -o a.out
./a.out
clean:
-rm a.out

View File

@ -0,0 +1,13 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
//#cgo CFLAGS: -I./number
//#include "number.h"
import "C"
import "fmt"
func main() {
fmt.Println(C.number_add_mod(10, 5, 12))
}

View File

@ -0,0 +1,10 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
gcc -c -o number.o number.c
ar rcs libnumber.a number.o
-rm *.o
clean:
-rm *.a *.o

View File

@ -0,0 +1,5 @@
#include "number.h"
int number_add_mod(int a, int b, int mod) {
return (a+b)%mod;
}

View File

@ -0,0 +1 @@
int number_add_mod(int a, int b, int mod);

View File

@ -0,0 +1,4 @@
// Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#include "./number/number.c"

View File

@ -0,0 +1,8 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
default:
go run runme.go
clean:
-rm a.out

View File

@ -0,0 +1,8 @@
// Copyright © 2016 ChaiShushan (chaishushan{AT}gmail.com).
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#include <iostream>
void SayHello() {
std::cout << "Hello, World!" << std::endl;
}

View File

@ -0,0 +1,8 @@
// Copyright © 2016 ChaiShushan (chaishushan{AT}gmail.com).
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
%module hello
%inline %{
extern void SayHello();
%}

View File

@ -0,0 +1,12 @@
// Copyright © 2016 ChaiShushan (chaishushan{AT}gmail.com).
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package hello
import (
"testing"
)
func TestSayHello(t *testing.T) {
SayHello()
}

View File

@ -0,0 +1,14 @@
// Copyright © 2016 ChaiShushan (chaishushan{AT}gmail.com).
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// +build ignore
package main
import (
hello "."
)
func main() {
hello.SayHello()
}

View File

@ -0,0 +1,11 @@
# Copyright © 2017 ChaiShushan <chaishushan{AT}gmail.com>.
# License: https://creativecommons.org/licenses/by-nc-sa/4.0/
# use go generate
default:
swig -go -cgo -intgosize 64 -o swig_wrap.cc hello.i
go run runme.go
clean:
-rm a.out

View File

@ -0,0 +1,8 @@
// Copyright © 2016 ChaiShushan (chaishushan{AT}gmail.com).
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
#include <iostream>
void SayHello() {
std::cout << "Hello, World!" << std::endl;
}

View File

@ -0,0 +1,73 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 3.0.12
*
* This file is not intended to be easily readable and contains a number of
* coding conventions designed to improve portability and efficiency. Do not make
* changes to this file unless you know what you are doing--modify the SWIG
* interface file instead.
* ----------------------------------------------------------------------------- */
// source: hello.i
package hello
/*
#define intgo swig_intgo
typedef void *swig_voidp;
#include <stdint.h>
typedef long long intgo;
typedef unsigned long long uintgo;
typedef struct { char *p; intgo n; } _gostring_;
typedef struct { void* array; intgo len; intgo cap; } _goslice_;
extern void _wrap_Swig_free_hello_679051ef4cde42b8(uintptr_t arg1);
extern uintptr_t _wrap_Swig_malloc_hello_679051ef4cde42b8(swig_intgo arg1);
extern void _wrap_SayHello_hello_679051ef4cde42b8(void);
#undef intgo
*/
import "C"
import "unsafe"
import _ "runtime/cgo"
import "sync"
type _ unsafe.Pointer
var Swig_escape_always_false bool
var Swig_escape_val interface{}
type _swig_fnptr *byte
type _swig_memberptr *byte
type _ sync.Mutex
func Swig_free(arg1 uintptr) {
_swig_i_0 := arg1
C._wrap_Swig_free_hello_679051ef4cde42b8(C.uintptr_t(_swig_i_0))
}
func Swig_malloc(arg1 int) (_swig_ret uintptr) {
var swig_r uintptr
_swig_i_0 := arg1
swig_r = (uintptr)(C._wrap_Swig_malloc_hello_679051ef4cde42b8(C.swig_intgo(_swig_i_0)))
return swig_r
}
func SayHello() {
C._wrap_SayHello_hello_679051ef4cde42b8()
}

View File

@ -0,0 +1,9 @@
// Copyright © 2016 ChaiShushan (chaishushan{AT}gmail.com).
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
%module hello
%inline %{
extern void SayHello();
%}

View File

@ -0,0 +1,14 @@
// Copyright © 2016 ChaiShushan (chaishushan{AT}gmail.com).
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// +build ignore
package main
import (
hello "."
)
func main() {
hello.SayHello()
}

View File

@ -0,0 +1,257 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 3.0.12
*
* This file is not intended to be easily readable and contains a number of
* coding conventions designed to improve portability and efficiency. Do not make
* changes to this file unless you know what you are doing--modify the SWIG
* interface file instead.
* ----------------------------------------------------------------------------- */
/* source: hello.i */
#define SWIGMODULE hello
/* -----------------------------------------------------------------------------
* This section contains generic SWIG labels for method/variable
* declarations/attributes, and other compiler dependent labels.
* ----------------------------------------------------------------------------- */
/* template workaround for compilers that cannot correctly implement the C++ standard */
#ifndef SWIGTEMPLATEDISAMBIGUATOR
# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
# define SWIGTEMPLATEDISAMBIGUATOR template
# elif defined(__HP_aCC)
/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
# define SWIGTEMPLATEDISAMBIGUATOR template
# else
# define SWIGTEMPLATEDISAMBIGUATOR
# endif
#endif
/* inline attribute */
#ifndef SWIGINLINE
# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
# define SWIGINLINE inline
# else
# define SWIGINLINE
# endif
#endif
/* attribute recognised by some compilers to avoid 'unused' warnings */
#ifndef SWIGUNUSED
# if defined(__GNUC__)
# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
# define SWIGUNUSED __attribute__ ((__unused__))
# else
# define SWIGUNUSED
# endif
# elif defined(__ICC)
# define SWIGUNUSED __attribute__ ((__unused__))
# else
# define SWIGUNUSED
# endif
#endif
#ifndef SWIG_MSC_UNSUPPRESS_4505
# if defined(_MSC_VER)
# pragma warning(disable : 4505) /* unreferenced local function has been removed */
# endif
#endif
#ifndef SWIGUNUSEDPARM
# ifdef __cplusplus
# define SWIGUNUSEDPARM(p)
# else
# define SWIGUNUSEDPARM(p) p SWIGUNUSED
# endif
#endif
/* internal SWIG method */
#ifndef SWIGINTERN
# define SWIGINTERN static SWIGUNUSED
#endif
/* internal inline SWIG method */
#ifndef SWIGINTERNINLINE
# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
#endif
/* exporting methods */
#if defined(__GNUC__)
# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# ifndef GCC_HASCLASSVISIBILITY
# define GCC_HASCLASSVISIBILITY
# endif
# endif
#endif
#ifndef SWIGEXPORT
# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# if defined(STATIC_LINKED)
# define SWIGEXPORT
# else
# define SWIGEXPORT __declspec(dllexport)
# endif
# else
# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
# define SWIGEXPORT __attribute__ ((visibility("default")))
# else
# define SWIGEXPORT
# endif
# endif
#endif
/* calling conventions for Windows */
#ifndef SWIGSTDCALL
# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# define SWIGSTDCALL __stdcall
# else
# define SWIGSTDCALL
# endif
#endif
/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
# define _CRT_SECURE_NO_DEPRECATE
#endif
/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
# define _SCL_SECURE_NO_DEPRECATE
#endif
/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */
#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES)
# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
#endif
/* Intel's compiler complains if a variable which was never initialised is
* cast to void, which is a common idiom which we use to indicate that we
* are aware a variable isn't used. So we just silence that warning.
* See: https://github.com/swig/swig/issues/192 for more discussion.
*/
#ifdef __INTEL_COMPILER
# pragma warning disable 592
#endif
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
typedef long long intgo;
typedef unsigned long long uintgo;
# if !defined(__clang__) && (defined(__i386__) || defined(__x86_64__))
# define SWIGSTRUCTPACKED __attribute__((__packed__, __gcc_struct__))
# else
# define SWIGSTRUCTPACKED __attribute__((__packed__))
# endif
typedef struct { char *p; intgo n; } _gostring_;
typedef struct { void* array; intgo len; intgo cap; } _goslice_;
#define swiggo_size_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1];
#define swiggo_size_assert(t, n) swiggo_size_assert_eq(sizeof(t), n, swiggo_sizeof_##t##_is_not_##n)
swiggo_size_assert(char, 1)
swiggo_size_assert(short, 2)
swiggo_size_assert(int, 4)
typedef long long swiggo_long_long;
swiggo_size_assert(swiggo_long_long, 8)
swiggo_size_assert(float, 4)
swiggo_size_assert(double, 8)
#ifdef __cplusplus
extern "C" {
#endif
extern void crosscall2(void (*fn)(void *, int), void *, int);
extern char* _cgo_topofstack(void) __attribute__ ((weak));
extern void _cgo_allocate(void *, int);
extern void _cgo_panic(void *, int);
#ifdef __cplusplus
}
#endif
static char *_swig_topofstack() {
if (_cgo_topofstack) {
return _cgo_topofstack();
} else {
return 0;
}
}
static void _swig_gopanic(const char *p) {
struct {
const char *p;
} SWIGSTRUCTPACKED a;
a.p = p;
crosscall2(_cgo_panic, &a, (int) sizeof a);
}
#define SWIG_contract_assert(expr, msg) \
if (!(expr)) { _swig_gopanic(msg); } else
static void Swig_free(void* p) {
free(p);
}
static void* Swig_malloc(int c) {
return malloc(c);
}
extern void SayHello();
#ifdef __cplusplus
extern "C" {
#endif
void _wrap_Swig_free_hello_679051ef4cde42b8(void *_swig_go_0) {
void *arg1 = (void *) 0 ;
arg1 = *(void **)&_swig_go_0;
Swig_free(arg1);
}
void *_wrap_Swig_malloc_hello_679051ef4cde42b8(intgo _swig_go_0) {
int arg1 ;
void *result = 0 ;
void *_swig_go_result;
arg1 = (int)_swig_go_0;
result = (void *)Swig_malloc(arg1);
*(void **)&_swig_go_result = (void *)result;
return _swig_go_result;
}
void _wrap_SayHello_hello_679051ef4cde42b8() {
SayHello();
}
#ifdef __cplusplus
}
#endif