Go语言的多平台支持库
因为写C语言 Advent Calendar 2016的人很少…
我想起了以前接触C语言的时候,所以决定写一篇。
从这里开始
去年,我为了适应多平台而苦恼不已,最终做出了选择。
你考虑到你不想再重新开发支持多平台的库,不如直接链接jre的库吧…(颤抖声)
然后,我突然意识到身边有一个非常方便的多平台适用的库存在。没错,就是大家喜欢的Golang。
将Golang的资产转化为共享库c-shared
c-shared是用于将Golang构建为C语言的共享库的模式。
你觉得能够从C语言中直接使用Golang的资源是最棒的,不是吗?
关于c-shared,我参考了这篇文章。
如果要尝试,推荐使用Linux或Mac环境。
Golang的cgo部分有许多隐含约定,一旦违反这些隐含约定,就会导致无法理解的错误信息。
如下链接中也有解释,重要的点有以下4点。*摘录
-
- パッケージは main でないと生成できない。
-
- main関数は実行されないが宣言する必要がある。
-
- エクスポートしたい関数にコメントで「//export funcname をつける。」(cgoと同じ)
- init は ライブラリロード時に実行される
package main
import (
"C"
"fmt"
"io/ioutil"
"net/http"
)
//export request
func request(url string) ([]byte, error) {
resp, err := http.Get(url)
//error握り潰し、だめ、ぜったい
if (err != nil) {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
func init() {
fmt.Println("Loaded!!")
}
func main() {
}
在Linux(x64)上尝试build方法。
$ time go build -buildmode=c-shared -o libgo.so main.go
real 0m6.494s
user 0m9.264s
sys 0m1.432s
-rw-rw-r-- 1 elise elise 1393 12月 4 00:47 libgo.h
-rw-rw-r-- 1 elise elise 6406448 12月 4 00:47 libgo.so
构建这个可能需要一些时间,毕竟是用Net库,文件大小为6MB。
我們來檢查生成的函數。請參考libgo.h。
/* Return type for request */
struct request_return {
GoSlice r0;
GoInterface r1;
};
extern struct request_return request(GoString p0);
typedef struct { const char *p; GoInt n; } GoString;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
typedef GoInt64 GoInt;
//以下为中文翻译:
// 这个Go函数被export了,可以在其他地方使用。
另外,Go生成的cgo的模板代码非常有趣,
特别是返回struct的部分,但在这里不详细介绍。
Go的error类型使用interface{}有些困扰。
该怎么处理呢?或许需要写一个帮助函数将其转换为错误代码?
尝试从C中调用Go的库
将Go的源代码和C的源代码放在一起时,
在使用golang进行构建时,会错误地将C源代码也视为cgo的目标并尝试去读取它,
所以我尝试将C源代码编写在一个单独的目录中。
#include "libgo.h"
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
GoString arg = {argv[1], strlen(argv[1])};
struct request_return ret = request(arg);
printf("%lld,%s\n", ret.r0.len, (char*)ret.r0.data);
return 0;
}
$ ls
C libgo.h libgo.so main.go
$ cd C
$ clang main.c -lgo -L.. -I..
$ export LD_LIBRARY_PATH=..
$ export GODEBUG=cgocheck=0 #良い子は真似しちゃいけない
$ a.out http://example.com
在GODEBUG=cgocheck=0下,跳过了对Go指针的保护检查。
由于上述代码直接通过指针引用了在Go中分配的内存,
存在被GC回收后可能变为无效指针的风险。
Loaded!!
1270,<!doctype html>
<html>
<head>
<title>Example Domain</title>
...
总结
我已经写到这里了,但在Windows平台上,无法使用buildmode=c-shared选项,
似乎只支持c-archive模式,
需要再做一些调整才行。
http://mattn.kaoriya.net/software/lang/go/20160405114638.htm