diff --git a/ch2-cgo/ch2-06-qsort.md b/ch2-cgo/ch2-06-qsort.md index ee77770..e793327 100644 --- a/ch2-cgo/ch2-06-qsort.md +++ b/ch2-cgo/ch2-06-qsort.md @@ -13,7 +13,7 @@ void qsort( ); ``` -其中base参数是要排序数组的首个元素的地址,num是数组中元素的个数,size是数组中每个元素的大小。最关键是cmp比较函数,用于对数组中任意两个元素进行排序。cmp排序函数的两个指针参数分别是要比较的两个元素的地址,如果第一个参数对应元素大于第二个参数对应的元素将返回1,如果两个元素相等则返回0,如果第一个元素小于第二个元素则返回-1。 +其中base参数是要排序数组的首个元素的地址,num是数组中元素的个数,size是数组中每个元素的大小。最关键是cmp比较函数,用于对数组中任意两个元素进行排序。cmp排序函数的两个指针参数分别是要比较的两个元素的地址,如果第一个参数对应元素大于第二个参数对应的元素将返回结果大于0,如果两个元素相等则返回0,如果第一个元素小于第二个元素则返回结果小于0。 下面的例子是用C语言的qsort对一个int类型的数组进行排序: @@ -48,14 +48,14 @@ int main() { /* #include -#define DIM(x) (sizeof(x)/sizeof((x)[0])) - static int compare(const void* a, const void* b) { - return ( *(int*)a - *(int*)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, DIM(values), sizeof(values[0]), compare); +static void qsort_proxy(int* values, size_t len, size_t elemsize) { + qsort(values, len, sizeof(values[0]), compare); } */ import "C" @@ -64,16 +64,18 @@ import "unsafe" import "fmt" func main() { - values := []int32{ 42, 9, 101, 95, 27, 25 }; - C.qsort_proxy( - unsafe.Pointer(&values[0]), - C.size_t(len(values)), - C.size_t(unsafe.Sizeof(values[0])), - ) - fmt.Println(values) + values := []int32{ 42, 9, 101, 95, 27, 25 }; + C.qsort_proxy( + (*C.int)(unsafe.Pointer(&values[0])), + C.size_t(len(values)), + C.size_t(unsafe.Sizeof(values[0])), + ) + fmt.Println(values) } ``` +因为 compare 函数固定了元素的大小,导致只能针对特点的数值类型排序。 + ## 在Go中自传入比较函数 ```go @@ -81,16 +83,16 @@ func main() { extern int go_qsort_compare(void* a, void* b); static int compare(const void* a, const void* b) { - return go_qsort_compare((void*)(a), (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) + pa := (*C.int)(a) + pb := (*C.int)(b) + return C.int(*pa - *pb) } ``` @@ -113,6 +115,8 @@ func main() { } ``` +直接传入Go导出的比较函数,需要进行一次类型转换(因为参数少了const修饰)。 + ```go import "C" diff --git a/examples/ch2-06-qsort/v1-qsort/Makefile b/examples/ch2-06-qsort/v1-qsort/Makefile new file mode 100644 index 0000000..e12893c --- /dev/null +++ b/examples/ch2-06-qsort/v1-qsort/Makefile @@ -0,0 +1,9 @@ +# Copyright © 2018 ChaiShushan . +# License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +default: + gcc main.c && ./a.out && rm a.out + + +clean: + -rm a.out diff --git a/examples/ch2-06-qsort/v1-qsort/main.c b/examples/ch2-06-qsort/v1-qsort/main.c new file mode 100644 index 0000000..aa0ba39 --- /dev/null +++ b/examples/ch2-06-qsort/v1-qsort/main.c @@ -0,0 +1,27 @@ +// Copyright © 2018 ChaiShushan . +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +#include +#include + +#define DIM(x) (sizeof(x)/sizeof((x)[0])) + +static int cmp(const void* a, const void* b) { + const int* pa = (int*)a; + const int* pb = (int*)b; + return *pa - *pb; +} + +int main() { + int values[] = { 42, 8, 109, 97, 23, 25 }; + int i; + + qsort(values, DIM(values), sizeof(values[0]), cmp); + + for(i = 0; i < DIM(values); i++) { + printf ("%d ",values[i]); + } + printf("\n"); + + return 0; +} diff --git a/examples/ch2-06-qsort/v2-qsort/main.go b/examples/ch2-06-qsort/v2-qsort/main.go new file mode 100644 index 0000000..6a231ef --- /dev/null +++ b/examples/ch2-06-qsort/v2-qsort/main.go @@ -0,0 +1,34 @@ +// Copyright © 2018 ChaiShushan . +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +package main + +/* +#include + +static int compare(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 "unsafe" +import "fmt" + +func main() { + values := []int32{42, 9, 101, 95, 27, 25} + + C.qsort_proxy( + (*C.int)(unsafe.Pointer(&values[0])), + C.size_t(len(values)), + C.size_t(unsafe.Sizeof(values[0])), + ) + + fmt.Println(values) +} diff --git a/examples/ch2-06-qsort/v3-qsort/main.go b/examples/ch2-06-qsort/v3-qsort/main.go new file mode 100644 index 0000000..02409fc --- /dev/null +++ b/examples/ch2-06-qsort/v3-qsort/main.go @@ -0,0 +1,43 @@ +// Copyright © 2018 ChaiShushan . +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +package main + +/* +#include + +typedef int (*qsort_cmp_func_t)(const void* a, const void* b); + +extern int go_qsort_compare(void* a, void* b); + +static int compare(const void* a, const void* b) { + return go_qsort_compare((void*)(a), (void*)(b)); +} + +static qsort_cmp_func_t get_compare_ptr() { + return compare; +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +func main() { + values := []int32{42, 9, 101, 95, 27, 25} + + C.qsort(unsafe.Pointer(&values[0]), + C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])), + C.get_compare_ptr(), + ) + + fmt.Println(values) +} + +//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) +} diff --git a/examples/ch2-06-qsort/v4-qsort/main.go b/examples/ch2-06-qsort/v4-qsort/main.go new file mode 100644 index 0000000..65a2fb3 --- /dev/null +++ b/examples/ch2-06-qsort/v4-qsort/main.go @@ -0,0 +1,35 @@ +// Copyright © 2018 ChaiShushan . +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +package main + +/* +#include + +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" + "unsafe" +) + +func main() { + values := []int32{42, 9, 101, 95, 27, 25} + + 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)), + ) + + fmt.Println(values) +} + +//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) +} diff --git a/examples/ch2-06-qsort/v5-qsort/main.go b/examples/ch2-06-qsort/v5-qsort/main.go new file mode 100644 index 0000000..1b125da --- /dev/null +++ b/examples/ch2-06-qsort/v5-qsort/main.go @@ -0,0 +1,50 @@ +// Copyright © 2018 ChaiShushan . +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +package main + +import ( + "fmt" +) + +/* +#include + +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" +) + +func main() { + values := []int32{42, 9, 101, 95, 27, 25} + + go_qsort_compare_info.Lock() + defer go_qsort_compare_info.Unlock() + + go_qsort_compare_info.fn = func(a, b unsafe.Pointer) C.int { + pa := (*C.int)(a) + pb := (*C.int)(b) + 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)), + ) + + fmt.Println(values) +} + +//export go_qsort_compare +func go_qsort_compare(a, b unsafe.Pointer) C.int { + return go_qsort_compare_info.fn(a, b) +} + +var go_qsort_compare_info struct { + fn func(a, b unsafe.Pointer) C.int + sync.RWMutex +} diff --git a/examples/ch2-06-qsort/v6-qsort/main.go b/examples/ch2-06-qsort/v6-qsort/main.go new file mode 100644 index 0000000..497e210 --- /dev/null +++ b/examples/ch2-06-qsort/v6-qsort/main.go @@ -0,0 +1,56 @@ +// Copyright © 2018 ChaiShushan . +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +package main + +import ( + "fmt" +) + +/* +#include + +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" +) + +func main() { + values := []int32{42, 9, 101, 95, 27, 25} + + qsort(values, func(a, b unsafe.Pointer) C.int { + pa := (*C.int)(a) + pb := (*C.int)(b) + return C.int(*pa - *pb) + }) + + fmt.Println(values) +} + +func qsort(values []int32, fn func(a, b unsafe.Pointer) C.int) { + 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)), + ) +} + +//export go_qsort_compare +func go_qsort_compare(a, b unsafe.Pointer) C.int { + return go_qsort_compare_info.fn(a, b) +} + +var go_qsort_compare_info struct { + fn func(a, b unsafe.Pointer) C.int + sync.RWMutex +} diff --git a/examples/ch2-06-qsort/v7-qsort/main.go b/examples/ch2-06-qsort/v7-qsort/main.go new file mode 100644 index 0000000..717333b --- /dev/null +++ b/examples/ch2-06-qsort/v7-qsort/main.go @@ -0,0 +1,92 @@ +// Copyright © 2018 ChaiShushan . +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +package main + +/* +#include + +typedef int (*qsort_cmp_func_t)(const void* a, const void* b); + +extern int go_qsort_compare(void* a, void* b); +extern void go_qsort_compare_save_base(void* base); + +static void +qsort_proxy( + void* base, size_t num, size_t size, + int (*compare)(const void* a, const void* b) +) { + go_qsort_compare_save_base(base); + qsort(base, num, size, compare); +} +*/ +import "C" + +import ( + "fmt" + "reflect" + "sync" + "unsafe" +) + +func main() { + values := []int64{42, 9, 101, 95, 27, 25} + + fmt.Println(values) + defer fmt.Println(values) + + qsort(values, func(i, j int) int { + return int(values[i] - values[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.elemsize = sv.Type().Elem().Size() + go_qsort_compare_info.fn = fn + + C.qsort_proxy( + unsafe.Pointer(unsafe.Pointer(sv.Index(0).Addr().Pointer())), + C.size_t(sv.Len()), + C.size_t(sv.Type().Elem().Size()), + (C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)), + ) +} + +//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)) +} + +//export go_qsort_compare_save_base +func go_qsort_compare_save_base(base unsafe.Pointer) { + go_qsort_compare_info.base = uintptr(base) +} + +var go_qsort_compare_info struct { + base uintptr + elemsize uintptr + fn func(a, b int) int + sync.RWMutex +}