go cgo skill


原文链接: go cgo skill
// int EC60789803D1(const char *CompanyTag, char *dataIn, int inlen, unsigned char *dataout, int outlen);

// Encrypt
{
    var byteOutBuffer []byte
	byteOutBuffer = make( []byte, 2048 )
	
    var outlen int = 2048
    var strKey string
    strKey = "test"
    var inString string
    inString = "fdsafdsafdsa"
    fmt.Println( "inlen=", len(inString) )
    InKey    := C.CString( strKey )
    InData   := C.CString( inString )
    InInLen  := C.int( len(inString) )
    InOut    := (*C.uchar)( &byteOutBuffer[0] )
    InOutLen := C.int( outlen )
    iRet := C.EC60789803D1( InKey, InData, InInLen, InOut, InOutLen )
    fmt.Println( "C.EC60789803D1 ret=", iRet, " OutBuffer=", util.Bytes2str( byteOutBuffer ) )
}

cgo 可以在 go 语言中夹杂着 C 函数或数据,在使用 cgo 时,有一些需要注意的:
1、go 中的 int/int32/int64/uint32/uint64 和 C 语言中的 int/int32 等是不同的,因此,C 语言的函数的参数不能是 go 语言的 int,需要转换,同理,go 函数的 int 也不能使用 C 的 int,需要转换。
C.int(n)

还有一点,C 的函数调用中,有很多参数是 size_t 类型,其实就是一个整型,但如果使用 C.int() 作为 C 函数的参数,就会编译出错:
 cannot use _Ctype_int(100) (type C.int) as type C.size_t in function argument
go 编译器严格限制参数类型必须一致,因此必须是 size_t 类型的参数。这是因为 go 语言没有 C 语言里面的强制转换的概念,你可以使用
C.size_t(n) 来得到 C 语言中的 sizt_t 类型。

2、go 语言中的字符串和 C 语言中的字符串转换
char * C.Cstring() string C.GoString()

3、结构体
使用 C.struct_xxxx 可以直接访问结构体中的成员
如果使用 struct 中的成员变量, 可以直接用. 来访问。

typedef struct face_t   face_t;
struct face_t {
	int x;
	int y;
	int w;
	int h;
	int neighbors;
	int angle;
};

var t C.face_t

face[i].X = int(t.x)
face[i].Y = int(t.y)
face[i].W = int(t.w)

4、指针
使用 unsafe.Pointer() 来转换,例如需要转换为c中的 int * : (*C.int)(unsafe.Pointer(&v))
在 c 语言中,指针即数组,可以使用 ptr[n] 来获得指针的第 n 个偏移,但在 go 中,这样不行,会报错:
invalid operation:xxxx
go 语言中,指针没有这样的操作。
unsafe.Pointer 对应 void * 代表的是一个指针
uintprt 表示的是一个指针的地址值
需要使用 unsafe.Pointer 和 uintptr 配合来获取指针的偏移。

5、函数调用
C.func()
文档中说,go 调用所有的 C 函数都会返回两个值,后一个值为 error 类型,即使是 void 函数。文档表述如下:

n, err := C.sqrt(-1)
_, err := C.voidFunc()

但我发现,C.malloc 似乎只返回一个值。

6、C 语言中的 NULL 在 go 中是 nil
例如
s := C.malloc(C.sizeof(100))
if s == nil {
….
}
这个很重要,在没发现 nil 可以比较 c 的指针前,我是这样比较的:
(*C.char)(unsafe.Pointer(uintptr(0)))
不过,cgo 的文档还很匮乏,很多都需要阅读代码。

package main

/*
#include 
#include 
#include

struct t {
    char *s;
};

*/
import “C”
import “unsafe”
import “fmt”

func main() {
	var t C.struct_t
	var pch *C.char
	var tmp *C.char

	// 1. go 实现再C中分配内存
	
	// 分配空间, 并判断是否分配成功
	t.s  = (*C.char)(C.malloc(C.size_t(100)))
	if t.s == nil {
		//if t.s == (*C.char)(unsafe.Pointer(uintptr(0))) {
		panic(“malloc failed!\n”)
	}	
	// 释放内存
    defer C.free(unsafe.Pointer(t.s))
 
    // 2. 将go的字符串转为c的字符串,并自动释放    
	var s = “hello world”
	pch = C.CString(s)
	defer C.free(unsafe.Pointer(pch))
	
	// 调用C的strncpy函数复制
	C.strncpy(t.s, pch, C.size_t(len(s)))
	// C的指针操作
	for i := C.size_t(0); i < C.strlen(t.s); i ++ {
		tmp = (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(t.s)) + uintptr(i)))
		*tmp = C.char(C.toupper(C.int(*tmp)))
	}
	
	fmt.Printf(“%s\n”, C.GoString(t.s))
	fmt.Printf(“sizeof struct t is %v\n”, unsafe.Sizeof(t))
}

package main

/*
#include <stdlib.h>
#include <string.h>
char* xmalloc(int len, int *rlen)
{
    static const char* s = "0123456789";
    char* p = malloc(len);
    if (len <= strlen(s)) {
        memcpy(p, s, len);
    } else {
        memset(p, 'a', len);
    }
    *rlen = len;
    return p;
}
*/
import "C"
import "unsafe"
import "fmt"

func main() {
	rlen := C.int(0)
	len := 10
	cstr := C.xmalloc(C.int(len), &rlen)
	defer C.free(unsafe.Pointer(cstr))
	gostr := C.GoStringN(cstr, rlen)
	fmt.Printf("retlen=%v\n", rlen)
	println(gostr)
}

go 调用C 中的struct

package main

/*
#include <stdlib.h>
#include <string.h>
struct MyString
{
    char* s;
    int len;
};
struct MyString xmalloc(int len)
{
    static const char* s = "0123456789";
    char* p = malloc(len);
    if (len <= strlen(s)) {
        memcpy(p, s, len);
    } else {
        memset(p, 'a', len);
    }
    struct MyString str;
    str.s = p;
    str.len = len;
    return str;
}
*/
import "C"
import "unsafe"
import "fmt"

func main() {
	len := 10
	str := C.xmalloc(C.int(len))
	defer C.free(unsafe.Pointer(str.s))
	gostr := C.GoStringN(str.s, str.len)
	fmt.Printf("retlen=%v\n", str.len)
	println(gostr)
}

函数调用

package magick

/*
#cgo pkg-config: MagickCore
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <magick/MagickCore.h>

void SetImageInfoFilename(ImageInfo *image_info, char *filename)
{
  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
}

Image *AddShadowToImage(Image *image, char *colorname, const double opacity, const double sigma,const ssize_t x_offset,const ssize_t y_offset, ExceptionInfo *exception)
{

  Image *shadow_image;
  if (QueryColorDatabase(colorname, &image->background_color, exception) == MagickFalse) {
    return MagickFalse;
  }
  shadow_image = ShadowImage(image, opacity, sigma, x_offset, y_offset, exception);
  AppendImageToList(&shadow_image, image);
  if (QueryColorDatabase("none", &shadow_image->background_color, exception) == MagickFalse) {
    return MagickFalse;
  }
  image = MergeImageLayers(shadow_image, MergeLayer, exception);
  DestroyImage(shadow_image);
  return image;
}
*/
import "C"
import (
	"io/ioutil"
	"math"
	"os"
	"strings"
	"unsafe"
)

func init() {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	c_wd := C.CString(wd)
	C.MagickCoreGenesis(c_wd, C.MagickFalse)
	defer C.free(unsafe.Pointer(c_wd))
}
// Shadow adds a dropshadow to the current (transparent) image and stores the shadowed image in place
// For more information about shadow options see: http://www.imagemagick.org/Usage/blur/#shadow
func (im *MagickImage) Shadow(color string, opacity, sigma float32, xoffset, yoffset int) (err error) {
	c_opacity := (C.double)(opacity)
	c_sigma := (C.double)(sigma)
	c_x := (C.ssize_t)(xoffset)
	c_y := (C.ssize_t)(yoffset)  // go类型转换为CGO size_t 类型
	c_color := C.CString(color)  // go类型转换为CGO sting 类型 
    defer C.free(unsafe.Pointer(c_color))
    
    new_image := C.AddShadowToImage(im.Image, c_color, c_opacity, c_sigma, c_x, c_y, exception)
    
	if failed := C.CheckException(exception); failed == C.MagickTrue {
		return ErrorFromExceptionInfo(exception)
	}
	im.ReplaceImage(new_image)
	return nil
}
`