mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 12:32:21 +00:00
ch2-6: 实战 qsort 包装 完成
This commit is contained in:
parent
84718a8e50
commit
58fb12e46e
@ -15,7 +15,7 @@
|
|||||||
* [2.3. 类型转换](ch2-cgo/ch2-03-cgo-types.md)
|
* [2.3. 类型转换](ch2-cgo/ch2-03-cgo-types.md)
|
||||||
* [2.4. 函数调用](ch2-cgo/ch2-04-func.md)
|
* [2.4. 函数调用](ch2-cgo/ch2-04-func.md)
|
||||||
* [2.5. 内部机制](ch2-cgo/ch2-05-internal.md)
|
* [2.5. 内部机制](ch2-cgo/ch2-05-internal.md)
|
||||||
* [2.6. 实战: 封装qsort(Doing)](ch2-cgo/ch2-06-qsort.md)
|
* [2.6. 实战: 封装qsort](ch2-cgo/ch2-06-qsort.md)
|
||||||
* [2.7. CGO内存模型](ch2-cgo/ch2-07-memory.md)
|
* [2.7. CGO内存模型](ch2-cgo/ch2-07-memory.md)
|
||||||
* [2.8. C++类包装](ch2-cgo/ch2-08-class.md)
|
* [2.8. C++类包装](ch2-cgo/ch2-08-class.md)
|
||||||
* [2.9. 静态库和动态库](ch2-cgo/ch2-09-static-shared-lib.md)
|
* [2.9. 静态库和动态库](ch2-cgo/ch2-09-static-shared-lib.md)
|
||||||
|
@ -4,7 +4,7 @@ qsort快速排序函数是C语言的高阶函数,支持用于自定义排序
|
|||||||
|
|
||||||
## 认识qsort函数
|
## 认识qsort函数
|
||||||
|
|
||||||
qosrt快速排序函数有`<stdlib.h>`标准库提供,函数的声明如下:
|
qsort快速排序函数有`<stdlib.h>`标准库提供,函数的声明如下:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void qsort(
|
void qsort(
|
||||||
@ -42,222 +42,295 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 用于Go固定类型数组的排序
|
其中`DIM(values)`宏用于几十数组元素的个数,`sizeof(values[0])`用于计算数组元素的大小。
|
||||||
|
cmp是用于排序时比较两个元素大小的回调函数。为了避免对全局名字空间的污染,我们将cmp回调函数定义为仅当前文件内可方位的静态函数。
|
||||||
|
|
||||||
|
## 将qsort函数从Go包导出
|
||||||
|
|
||||||
|
为了方便Go语言的非CGO用户使用qsort函数,我们需要将C语言的qsort函数包装为一个外部可以访问的Go函数。
|
||||||
|
|
||||||
|
用Go语言将qsort函数重新包装为`qsort.Sort`函数:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
/*
|
package qsort
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
static int compare(const void* a, const void* b) {
|
//typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
||||||
const int* pa = (int*)a;
|
|
||||||
const int* pb = (int*)b;
|
|
||||||
return *pa - *pb;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qsort_proxy(int* values, size_t len, size_t elemsize) {
|
|
||||||
qsort(values, len, sizeof(values[0]), compare);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func main() {
|
func Sort(
|
||||||
values := []int32{ 42, 9, 101, 95, 27, 25 };
|
base unsafe.Pointer, num, size C.size_t,
|
||||||
C.qsort_proxy(
|
cmp C.qsort_cmp_func_t,
|
||||||
(*C.int)(unsafe.Pointer(&values[0])),
|
) {
|
||||||
C.size_t(len(values)),
|
C.qsort(base, num, size, cmp)
|
||||||
C.size_t(unsafe.Sizeof(values[0])),
|
|
||||||
)
|
|
||||||
fmt.Println(values)
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
因为 compare 函数固定了元素的大小,导致只能针对特点的数值类型排序。
|
因为Go语言的CGO语言不好直接表达C语言的函数类型,因此在C语言空间将比较函数类型重新定义为一个`qsort_cmp_func_t`类型。
|
||||||
|
|
||||||
## 在Go中自传入比较函数
|
虽然Sort函数已经导出了,但是对于qsort包之外的用户依然不能直角使用该函数——Sort函数的参数还包含了虚拟的C包提供的类型。
|
||||||
|
在CGO的内部机制一节中我们已经提过,虚拟的C包下的任何名称其实都会被映射为包内的私有名字。比如`C.size_t`会被展开为`_Ctype_size_t`,`C.qsort_cmp_func_t`类型会被展开为`_Ctype_qsort_cmp_func_t`。
|
||||||
|
|
||||||
|
被CGO处理后的Sort函数的类型如下:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
/*
|
func Sort(
|
||||||
extern int go_qsort_compare(void* a, void* b);
|
base unsafe.Pointer, num, size _Ctype_size_t,
|
||||||
|
cmp _Ctype_qsort_cmp_func_t,
|
||||||
static int compare(const void* a, const void* b) {
|
)
|
||||||
return go_qsort_compare((void*)(a), (void*)(b));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
//export go_qsort_compare
|
|
||||||
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
|
||||||
pa := (*C.int)(a)
|
|
||||||
pb := (*C.int)(b)
|
|
||||||
return C.int(*pa - *pb)
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
这样将会导致包外部用于无法构造`_Ctype_size_t`和`_Ctype_qsort_cmp_func_t`类型的参数而无法使用Sort函数。因此,导出的Sort函数的参数和返回值要避免对虚拟C包的依赖。
|
||||||
|
|
||||||
|
重新调整Sort函数的参数类型和实现如下:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
/*
|
/*
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
||||||
extern int go_qsort_compare(void* a, void* b);
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
func main() {
|
type CompareFunc C.qsort_cmp_func_t
|
||||||
values := []int32{42, 9, 101, 95, 27, 25}
|
|
||||||
|
|
||||||
C.qsort(unsafe.Pointer(&values[0]),
|
func Sort(base unsafe.Pointer, num, size int, cmp CompareFunc) {
|
||||||
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])),
|
C.qsort(base, C.size_t(num), C.size_t(size), C.qsort_cmp_func_t(cmp))
|
||||||
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
直接传入Go导出的比较函数,需要进行一次类型转换(因为参数少了const修饰)。
|
我们将虚拟C包中的类型通过Go语言类型代替,在内部调用C函数时重新转型为C函数需要的类型。因此外部用户将不再依赖qsort包内的虚拟C包。
|
||||||
|
|
||||||
|
以下代码展示的Sort函数的使用方式:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
//extern int go_qsort_compare(void* a, void* b);
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
qsort "."
|
||||||
|
)
|
||||||
|
|
||||||
//export go_qsort_compare
|
//export go_qsort_compare
|
||||||
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
||||||
return go_qsort_compare_info.fn(a, b)
|
pa, pb := (*C.int)(a), (*C.int)(b)
|
||||||
|
return C.int(*pa - *pb)
|
||||||
}
|
}
|
||||||
|
|
||||||
var go_qsort_compare_info struct {
|
|
||||||
fn func(a, b unsafe.Pointer) C.int
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
func main() {
|
func main() {
|
||||||
values := []int32{42, 9, 101, 95, 27, 25}
|
values := []int32{42, 9, 101, 95, 27, 25}
|
||||||
|
|
||||||
go_qsort_compare_info.Lock()
|
qsort.Sort(unsafe.Pointer(&values[0]),
|
||||||
defer go_qsort_compare_info.Unlock()
|
len(values), int(unsafe.Sizeof(values[0])),
|
||||||
go_qsort_compare_info.fn = func(a, b unsafe.Pointer) C.int {
|
qsort.CompareFunc(C.go_qsort_compare),
|
||||||
pa := (*C.int)(a)
|
)
|
||||||
pb := (*C.int)(b)
|
fmt.Println(values)
|
||||||
return C.int(*pa - *pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
C.qsort(unsafe.Pointer(&values[0]),
|
|
||||||
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])),
|
|
||||||
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
为了使用Sort函数,我们需要将Go语言的切片取首地址、元素个数、元素大小等信息作为调用参数,同时还需要提过一个C语言规格的比较函数。
|
||||||
|
其中go_qsort_compare是用Go语言实现的,并导出到C语言空间的函数,用于qsort排序时的比较函数。
|
||||||
|
|
||||||
|
目前已经实现了对C语言的qsort初步包装,并且可以通过包的方式被其它用户使用。但是`qsort.Sort`函数已经有很多不便使用之处:用户要提过C语言的比较函数,这对许多Go语言用户是一个挑战。下一步我们将继续改进qsort函数的包装函数,尝试通过闭包函数代替C语言的比较函数。
|
||||||
|
|
||||||
|
消除用户对CGO代码的直角依赖。
|
||||||
|
|
||||||
|
## 改进:闭包函数作为比较函数
|
||||||
|
|
||||||
|
在改进之前我们先回顾下Go语言sort包自带的排序函数的接口:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func qsort(values []int32, fn func(a, b unsafe.Pointer) C.int) {
|
func Slice(slice interface{}, less func(i, j int) bool)
|
||||||
go_qsort_compare_info.Lock()
|
|
||||||
defer go_qsort_compare_info.Unlock()
|
|
||||||
|
|
||||||
go_qsort_compare_info.fn = fn
|
|
||||||
|
|
||||||
C.qsort(
|
|
||||||
unsafe.Pointer(&values[0]),
|
|
||||||
C.size_t(len(values)),
|
|
||||||
C.size_t(unsafe.Sizeof(values[0])),
|
|
||||||
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
标准库的sort.Slice因为支持通过闭包函数指定比较函数,对切片的排序非常简单:
|
||||||
## Go比较函数类型的简化
|
|
||||||
|
|
||||||
## 适配更多数组类型
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func qsort(slice interface{}, fn func(a, b unsafe.Pointer) C.int) {
|
import "sort"
|
||||||
sv := reflect.ValueOf(slice)
|
|
||||||
if sv.Kind() != reflect.Slice {
|
|
||||||
panic("not slice type")
|
|
||||||
}
|
|
||||||
|
|
||||||
go_qsort_compare_info.Lock()
|
func main() {
|
||||||
defer go_qsort_compare_info.Unlock()
|
values := []int32{42, 9, 101, 95, 27, 25}
|
||||||
go_qsort_compare_info.fn = fn
|
|
||||||
|
|
||||||
C.qsort(
|
sort.Slice(values, less func(i, j int) bool {
|
||||||
unsafe.Pointer(unsafe.Pointer(sv.Index(0).Addr().Pointer())),
|
return values[i] < values[j]
|
||||||
C.size_t(sv.Len()), C.size_t(sv.Type().Elem().Size()),
|
})
|
||||||
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
|
|
||||||
)
|
fmt.Println(values)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
我们也尝试将C语言的qsort函数包装为以下格式的Go语言函数:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func qsort(slice interface{}, fn func(a, b int) int) {
|
package qsort
|
||||||
...
|
|
||||||
|
func Sort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int)
|
||||||
|
```
|
||||||
|
|
||||||
|
闭包函数函数无法导出为C语言函数,因此无法之间将闭包函数传入C语言的qsort函数。
|
||||||
|
为此我们可以用Go构造一个可以导出为C语言的代理函数,然后通过一个全局变量临时保存当前的闭包比较函数。
|
||||||
|
|
||||||
|
代码如下:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var go_qsort_compare_info struct {
|
||||||
|
fn func(a, b unsafe.Pointer) int
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _cgo_qsort_compare
|
||||||
|
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
|
||||||
|
return C.int(go_qsort_compare_info.fn(a, b))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
其中导出的C语言函数`_cgo_qsort_compare`是公用的qsort比较函数,内部通过`go_qsort_compare_info.fn`来调用当前的闭包比较函数。
|
||||||
|
|
||||||
|
新的Sort包装函数实现如下:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
/*
|
/*
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
extern int go_qsort_compare(void* a, void* b);
|
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
||||||
extern void go_qsort_compare_save_base(void* base);
|
extern int _cgo_qsort_compare(void* a, void* b);
|
||||||
|
|
||||||
static void qsort_proxy(
|
|
||||||
void* base, size_t num, size_t size,
|
|
||||||
int (*compar)(const void* a, const void* b)
|
|
||||||
) {
|
|
||||||
go_qsort_compare_save_base(base); // 保存数组地址
|
|
||||||
qsort(base, num, size, compar);
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
func Sort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int) {
|
||||||
//export go_qsort_compare_save_base
|
go_qsort_compare_info.Lock()
|
||||||
func go_qsort_compare_save_base(base unsafe.Pointer) {
|
defer go_qsort_compare_info.Unlock()
|
||||||
go_qsort_compare_info.base = uintptr(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
var go_qsort_compare_info struct {
|
go_qsort_compare_info.fn = cmp
|
||||||
base uintptr
|
|
||||||
elemsize uintptr
|
C.qsort(base, C.size_t(num), C.size_t(size),
|
||||||
fn func(a, b int) int
|
C.qsort_cmp_func_t(C._cgo_qsort_compare),
|
||||||
sync.RWMutex
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```go
|
每次排序前,对全局的go_qsort_compare_info变量加锁,同时将当前的闭包函数保存到全局变量,然后调用C语言的qsort函数。
|
||||||
//export go_qsort_compare
|
|
||||||
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
|
||||||
var (
|
|
||||||
// array memory is locked
|
|
||||||
base = go_qsort_compare_info.base
|
|
||||||
elemsize = go_qsort_compare_info.elemsize
|
|
||||||
)
|
|
||||||
|
|
||||||
i := int((uintptr(a) - base) / elemsize)
|
基于新包装的函数,我们可以简化之前的排序代码:
|
||||||
j := int((uintptr(b) - base) / elemsize)
|
|
||||||
|
|
||||||
return C.int(go_qsort_compare_info.fn(i, j))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
values := []int64{42, 9, 101, 95, 27, 25}
|
values := []int32{42, 9, 101, 95, 27, 25}
|
||||||
|
|
||||||
qsort(values, func(i, j int) int {
|
qsort.Sort(unsafe.Pointer(&values[0]), len(values), int(unsafe.Sizeof(values[0])),
|
||||||
return int(values[i] - values[j])
|
func(a, b unsafe.Pointer) int {
|
||||||
})
|
pa, pb := (*int32)(a), (*int32)(b)
|
||||||
}
|
return int(*pa - *pb)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
func qsort(slice interface{}, fn func(a, b int) int) {
|
fmt.Println(values)
|
||||||
...
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||

|
现在排序不再需要通过CGO实现C语言版本的比较函数了,可以传入Go语言闭包函数作为比较函数。
|
||||||
|
但是导入的排序函数依然依赖unsafe包,这是违背Go语言编程习惯的。
|
||||||
|
|
||||||
|
## 改进:消除用户对unsafe包的依赖
|
||||||
|
|
||||||
|
前一个版本的qsort.Sort包装函数已经比最初的C语言版本的qsort易用很多,但是依然保留了很多C语言底层数据结构的细节。
|
||||||
|
现在我们将继续改进包装函数,尝试消除对unsafe包的依赖,并实现一个类似标准库中sort.Slice的排序函数。
|
||||||
|
|
||||||
|
新的包装函数声明如下:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package qsort
|
||||||
|
|
||||||
|
func Slice(slice interface{}, less func(a, b int) bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
首先,我们将slice作为接口类型参数传入,这样可以适配不同的切片类型。
|
||||||
|
然后切片的首个元素的地址、元素个数和元素大小可以通过reflect反射包从切片中获取。
|
||||||
|
|
||||||
|
为了保存必要的排序上下文信息,我们需要在全局包变量增加要排序数组的地址、元素个数和元素大小等信息,比较函数改为less:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var go_qsort_compare_info struct {
|
||||||
|
base unsafe.Pointer
|
||||||
|
elemnum int
|
||||||
|
elemsize int
|
||||||
|
less func(a, b int) bool
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
同样比较函数需要根据元素指针、排序数组的开始地址和元素的大小计算出元素对应数组的索引下标,
|
||||||
|
然后根据less函数的比较结果返回qsort函数需要格式的比较结果。
|
||||||
|
|
||||||
|
```go
|
||||||
|
//export _cgo_qsort_compare
|
||||||
|
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
|
||||||
|
var (
|
||||||
|
// array memory is locked
|
||||||
|
base = uintptr(go_qsort_compare_info.base)
|
||||||
|
elemsize = uintptr(go_qsort_compare_info.elemsize)
|
||||||
|
)
|
||||||
|
|
||||||
|
i := int((uintptr(a) - base) / elemsize)
|
||||||
|
j := int((uintptr(b) - base) / elemsize)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case go_qsort_compare_info.less(i, j): // v[i] < v[j]
|
||||||
|
return -1
|
||||||
|
case go_qsort_compare_info.less(j, i): // v[i] > v[j]
|
||||||
|
return +1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
新的Slice函数的实现如下:
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
func Slice(slice interface{}, less func(a, b int) bool) {
|
||||||
|
sv := reflect.ValueOf(slice)
|
||||||
|
if sv.Kind() != reflect.Slice {
|
||||||
|
panic(fmt.Sprintf("qsort called with non-slice value of type %T", slice))
|
||||||
|
}
|
||||||
|
if sv.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go_qsort_compare_info.Lock()
|
||||||
|
defer go_qsort_compare_info.Unlock()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
go_qsort_compare_info.base = nil
|
||||||
|
go_qsort_compare_info.elemnum = 0
|
||||||
|
go_qsort_compare_info.elemsize = 0
|
||||||
|
go_qsort_compare_info.less = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
// baseMem = unsafe.Pointer(sv.Index(0).Addr().Pointer())
|
||||||
|
// baseMem maybe moved, so must saved after call C.fn
|
||||||
|
go_qsort_compare_info.base = unsafe.Pointer(sv.Index(0).Addr().Pointer())
|
||||||
|
go_qsort_compare_info.elemnum = sv.Len()
|
||||||
|
go_qsort_compare_info.elemsize = int(sv.Type().Elem().Size())
|
||||||
|
go_qsort_compare_info.less = less
|
||||||
|
|
||||||
|
C.qsort(
|
||||||
|
go_qsort_compare_info.base,
|
||||||
|
C.size_t(go_qsort_compare_info.elemnum),
|
||||||
|
C.size_t(go_qsort_compare_info.elemsize),
|
||||||
|
C.qsort_cmp_func_t(C._cgo_qsort_compare),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
首先需要判断传入的接口类型必须是切片类型。然后通过反射获取qsort函数需要的切片信息,并调用C语言的qsort函数。
|
||||||
|
为了避免在排序过程中,排序数组的上下文信息`go_qsort_compare_info`被修改,我们进行了全局加锁。
|
||||||
|
因此目前版本的qsort.Slice函数是无法并发执行的,读者可以自己尝试改进这个限制。
|
||||||
|
|
||||||
|
@ -1,52 +1,32 @@
|
|||||||
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
|
// Copyright © 2018 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/
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
//extern int go_qsort_compare(void* a, void* b);
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
|
||||||
|
|
||||||
// disable static
|
|
||||||
int qsort_compare_callback(const void* a, const void* b) {
|
|
||||||
const int* pa = (int*)a;
|
|
||||||
const int* pb = (int*)b;
|
|
||||||
return *pa - *pb;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compare(const void* a, const void* b) {
|
|
||||||
const int* pa = (int*)a;
|
|
||||||
const int* pb = (int*)b;
|
|
||||||
return *pa - *pb;
|
|
||||||
}
|
|
||||||
static qsort_cmp_func_t get_compare_ptr() {
|
|
||||||
return compare;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
import "fmt"
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
func qsort(base unsafe.Pointer, num, size C.size_t, cmp C.qsort_cmp_func_t) {
|
qsort "."
|
||||||
C.qsort(base, num, size, C.qsort_cmp_func_t(cmp))
|
)
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
values := []int32{42, 9, 101, 95, 27, 25}
|
values := []int32{42, 9, 101, 95, 27, 25}
|
||||||
|
|
||||||
qsort(unsafe.Pointer(&values[0]),
|
qsort.Sort(unsafe.Pointer(&values[0]),
|
||||||
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])),
|
len(values), int(unsafe.Sizeof(values[0])),
|
||||||
C.get_compare_ptr(), // static callback must use proxy get
|
qsort.CompareFunc(C.go_qsort_compare),
|
||||||
)
|
|
||||||
fmt.Println(values)
|
|
||||||
|
|
||||||
values = []int32{42, 9, 101, 95, 27, 25}
|
|
||||||
|
|
||||||
qsort(unsafe.Pointer(&values[0]),
|
|
||||||
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])),
|
|
||||||
C.qsort_cmp_func_t(unsafe.Pointer(C.qsort_compare_callback)),
|
|
||||||
)
|
)
|
||||||
fmt.Println(values)
|
fmt.Println(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export go_qsort_compare
|
||||||
|
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
||||||
|
pa, pb := (*C.int)(a), (*C.int)(b)
|
||||||
|
return C.int(*pa - *pb)
|
||||||
|
}
|
||||||
|
19
examples/ch2-06-qsort/v2-qsort/qsort.go
Normal file
19
examples/ch2-06-qsort/v2-qsort/qsort.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
|
||||||
|
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
|
||||||
|
package qsort
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
type CompareFunc C.qsort_cmp_func_t
|
||||||
|
|
||||||
|
func Sort(base unsafe.Pointer, num, size int, cmp CompareFunc) {
|
||||||
|
C.qsort(base, C.size_t(num), C.size_t(size), C.qsort_cmp_func_t(cmp))
|
||||||
|
}
|
@ -1,38 +1,25 @@
|
|||||||
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
|
// Copyright © 2018 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/
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
|
||||||
|
|
||||||
extern int go_qsort_compare(void* a, void* b);
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
qsort "."
|
||||||
)
|
)
|
||||||
|
|
||||||
//export go_qsort_compare
|
|
||||||
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
|
||||||
pa := (*C.int)(a)
|
|
||||||
pb := (*C.int)(b)
|
|
||||||
return C.int(*pa - *pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
func qsort(base unsafe.Pointer, num, size C.size_t, cmp C.qsort_cmp_func_t) {
|
|
||||||
C.qsort(base, num, size, C.qsort_cmp_func_t(cmp))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
values := []int32{42, 9, 101, 95, 27, 25}
|
values := []int32{42, 9, 101, 95, 27, 25}
|
||||||
|
|
||||||
qsort(unsafe.Pointer(&values[0]),
|
qsort.Sort(unsafe.Pointer(&values[0]), len(values), int(unsafe.Sizeof(values[0])),
|
||||||
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])),
|
func(a, b unsafe.Pointer) int {
|
||||||
C.qsort_cmp_func_t(unsafe.Pointer(C.go_qsort_compare)),
|
pa, pb := (*int32)(a), (*int32)(b)
|
||||||
|
return int(*pa - *pb)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
fmt.Println(values)
|
fmt.Println(values)
|
||||||
|
37
examples/ch2-06-qsort/v3-qsort/sort.go
Normal file
37
examples/ch2-06-qsort/v3-qsort/sort.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
|
||||||
|
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
|
||||||
|
package qsort
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
||||||
|
extern int _cgo_qsort_compare(void* a, void* b);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var go_qsort_compare_info struct {
|
||||||
|
fn func(a, b unsafe.Pointer) int
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _cgo_qsort_compare
|
||||||
|
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
|
||||||
|
return C.int(go_qsort_compare_info.fn(a, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int) {
|
||||||
|
go_qsort_compare_info.Lock()
|
||||||
|
defer go_qsort_compare_info.Unlock()
|
||||||
|
|
||||||
|
go_qsort_compare_info.fn = cmp
|
||||||
|
|
||||||
|
C.qsort(base, C.size_t(num), C.size_t(size),
|
||||||
|
C.qsort_cmp_func_t(C._cgo_qsort_compare),
|
||||||
|
)
|
||||||
|
}
|
@ -1,54 +1,22 @@
|
|||||||
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
|
// Copyright © 2018 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/
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
qsort "."
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
|
||||||
extern int go_qsort_compare(void* a, void* b);
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var go_qsort_compare_info struct {
|
|
||||||
fn func(a, b unsafe.Pointer) int
|
|
||||||
sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_qsort_compare
|
|
||||||
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
|
||||||
return C.int(go_qsort_compare_info.fn(a, b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func qsort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int) {
|
|
||||||
go_qsort_compare_info.Lock()
|
|
||||||
defer go_qsort_compare_info.Unlock()
|
|
||||||
|
|
||||||
go_qsort_compare_info.fn = cmp
|
|
||||||
|
|
||||||
C.qsort(base, C.size_t(num), C.size_t(size),
|
|
||||||
C.qsort_cmp_func_t(C.go_qsort_compare),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
values := []int32{42, 9, 101, 95, 27, 25}
|
values := []int64{42, 9, 101, 95, 27, 25}
|
||||||
|
|
||||||
qsort(unsafe.Pointer(&values[0]), len(values), int(unsafe.Sizeof(values[0])),
|
qsort.Slice(values, func(i, j int) bool {
|
||||||
func(a, b unsafe.Pointer) int {
|
return values[i] < values[j]
|
||||||
pa, pb := (*C.int)(a), (*C.int)(b)
|
})
|
||||||
return int(*pa - *pb)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
fmt.Println(values)
|
fmt.Println(values)
|
||||||
}
|
}
|
||||||
|
83
examples/ch2-06-qsort/v4-qsort/sort.go
Normal file
83
examples/ch2-06-qsort/v4-qsort/sort.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
|
||||||
|
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
|
||||||
|
package qsort
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
||||||
|
|
||||||
|
extern int _cgo_qsort_compare(void* a, void* b);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var go_qsort_compare_info struct {
|
||||||
|
base unsafe.Pointer
|
||||||
|
elemnum int
|
||||||
|
elemsize int
|
||||||
|
less func(a, b int) bool
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _cgo_qsort_compare
|
||||||
|
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
|
||||||
|
var (
|
||||||
|
// array memory is locked
|
||||||
|
base = uintptr(go_qsort_compare_info.base)
|
||||||
|
elemsize = uintptr(go_qsort_compare_info.elemsize)
|
||||||
|
)
|
||||||
|
|
||||||
|
i := int((uintptr(a) - base) / elemsize)
|
||||||
|
j := int((uintptr(b) - base) / elemsize)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case go_qsort_compare_info.less(i, j): // v[i] < v[j]
|
||||||
|
return -1
|
||||||
|
case go_qsort_compare_info.less(j, i): // v[i] > v[j]
|
||||||
|
return +1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Slice(slice interface{}, less func(a, b int) bool) {
|
||||||
|
sv := reflect.ValueOf(slice)
|
||||||
|
if sv.Kind() != reflect.Slice {
|
||||||
|
panic(fmt.Sprintf("qsort called with non-slice value of type %T", slice))
|
||||||
|
}
|
||||||
|
if sv.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go_qsort_compare_info.Lock()
|
||||||
|
defer go_qsort_compare_info.Unlock()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
go_qsort_compare_info.base = nil
|
||||||
|
go_qsort_compare_info.elemnum = 0
|
||||||
|
go_qsort_compare_info.elemsize = 0
|
||||||
|
go_qsort_compare_info.less = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
// baseMem = unsafe.Pointer(sv.Index(0).Addr().Pointer())
|
||||||
|
// baseMem maybe moved, so must saved after call C.fn
|
||||||
|
go_qsort_compare_info.base = unsafe.Pointer(sv.Index(0).Addr().Pointer())
|
||||||
|
go_qsort_compare_info.elemnum = sv.Len()
|
||||||
|
go_qsort_compare_info.elemsize = int(sv.Type().Elem().Size())
|
||||||
|
go_qsort_compare_info.less = less
|
||||||
|
|
||||||
|
C.qsort(
|
||||||
|
go_qsort_compare_info.base,
|
||||||
|
C.size_t(go_qsort_compare_info.elemnum),
|
||||||
|
C.size_t(go_qsort_compare_info.elemsize),
|
||||||
|
C.qsort_cmp_func_t(C._cgo_qsort_compare),
|
||||||
|
)
|
||||||
|
}
|
@ -1,87 +0,0 @@
|
|||||||
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
|
|
||||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
|
|
||||||
|
|
||||||
extern int go_qsort_compare(void* a, void* b);
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var go_qsort_compare_info struct {
|
|
||||||
slice interface{}
|
|
||||||
base uintptr
|
|
||||||
elemsize uintptr
|
|
||||||
fn func(a, b int) int
|
|
||||||
sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_qsort_compare_save_base
|
|
||||||
func go_qsort_compare_save_base(base unsafe.Pointer) {
|
|
||||||
go_qsort_compare_info.base = uintptr(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_qsort_compare
|
|
||||||
func go_qsort_compare(a, b unsafe.Pointer) C.int {
|
|
||||||
if go_qsort_compare_info.base == 0 {
|
|
||||||
sv := reflect.ValueOf(go_qsort_compare_info.slice)
|
|
||||||
go_qsort_compare_info.base = uintptr(unsafe.Pointer(sv.Index(0).Addr().Pointer()))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// array memory is locked
|
|
||||||
base = go_qsort_compare_info.base
|
|
||||||
elemsize = go_qsort_compare_info.elemsize
|
|
||||||
)
|
|
||||||
|
|
||||||
i := int((uintptr(a) - base) / elemsize)
|
|
||||||
j := int((uintptr(b) - base) / elemsize)
|
|
||||||
|
|
||||||
return C.int(go_qsort_compare_info.fn(i, j))
|
|
||||||
}
|
|
||||||
|
|
||||||
func qsort(slice interface{}, fn func(a, b int) int) {
|
|
||||||
sv := reflect.ValueOf(slice)
|
|
||||||
if sv.Kind() != reflect.Slice {
|
|
||||||
panic(fmt.Sprintf("qsort called with non-slice value of type %T", slice))
|
|
||||||
}
|
|
||||||
if sv.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go_qsort_compare_info.Lock()
|
|
||||||
defer go_qsort_compare_info.Unlock()
|
|
||||||
|
|
||||||
// baseMem = unsafe.Pointer(sv.Index(0).Addr().Pointer())
|
|
||||||
// baseMem maybe moved, so must saved after call C.fn
|
|
||||||
go_qsort_compare_info.slice = slice
|
|
||||||
go_qsort_compare_info.elemsize = sv.Type().Elem().Size()
|
|
||||||
go_qsort_compare_info.fn = fn
|
|
||||||
|
|
||||||
C.qsort(
|
|
||||||
unsafe.Pointer(sv.Index(0).Addr().Pointer()),
|
|
||||||
C.size_t(sv.Len()), C.size_t(sv.Type().Elem().Size()),
|
|
||||||
C.qsort_cmp_func_t(C.go_qsort_compare),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
values := []int64{42, 9, 101, 95, 27, 25}
|
|
||||||
|
|
||||||
qsort(values, func(i, j int) int {
|
|
||||||
return int(values[i] - values[j])
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Println(values)
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user