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

bannerAds