golang学习7:编译为可被C语言调用的库

说明

在使用 go build 或 go install 两个命令编译代码的时候,可以使用-buildmode指定生成什么样的文件。

go build -buildmode=<mode>
或者
go install -buildmode=<mode>

使用 go help buildmode可以查看所有支持的buildmode选项。

        -buildmode=archive
                Build the listed non-main packages into .a files. Packages named
                main are ignored.

        -buildmode=c-archive
                Build the listed main package, plus all packages it imports,
                into a C archive file. The only callable symbols will be those
                functions exported using a cgo //export comment. Requires
                exactly one main package to be listed.

        -buildmode=c-shared
                Build the listed main package, plus all packages it imports,
                into a C shared library. The only callable symbols will
                be those functions exported using a cgo //export comment.
                Requires exactly one main package to be listed.

        -buildmode=default
                Listed main packages are built into executables and listed
                non-main packages are built into .a files (the default
                behavior).

        -buildmode=shared
                Combine all the listed non-main packages into a single shared
                library that will be used when building with the -linkshared
                option. Packages named main are ignored.

        -buildmode=exe
                Build the listed main packages and everything they import into
                executables. Packages not named main are ignored.

        -buildmode=pie
                Build the listed main packages and everything they import into
                position independent executables (PIE). Packages not named
                main are ignored.

        -buildmode=plugin
                Build the listed main packages, plus all packages that they
                import, into a Go plugin. Packages not named main are ignored.

分类说明下

  • -buildmode=shared是为了生成go编译编译期间使用的库,概念类似c语言的动态库。使用这些库编译go文件需加选项-linkshared。

  • -buildmode=c-archive和-buildmode=c-shared是为了生成C语言的库和头文件,分别对应静态库和动态库

  • -buildmode=plugin是为了生成go语言运行期间可加载的动态库(目前只能加载,无法卸载,是不是可以通过销毁协程来卸载呢?)

其它几个选项暂时不太明白。

将go脚本编译为C语言库

将go源码编译为C语言库需满足以下条件

  1. 编译选项使用-buildmode=c-archive或-buildmode=c-shared
  2. 编译源码里必须有main package
  3. 源码必须import “C”
  4. 导出符号位于main package,且前一行有//export NAME

由于导出的符号只能位于main package,所以必须定义main()函数,但是这个函数可以为空。而且导出的函数的参数和返回值类型只能使用golang的基础类型,无法使用go语言内部的结构体等复杂类型。

现在定义一个简单的go文件用于生成C语言的库

package main

import (
    "C"
	"fmt"
)

//export hello 
func hello(num int32, name string, size float32) uint8 {
   fmt.Printf("Hello, world, %v, %v, %v\n", num, name, size)
   return 0
}

func main() {
	fmt.Printf("Hello, in main.\n")
	hello(1, "Yuan", 3.14)
}

直接运行测试下:

$ go run main.go
Hello, in main.
Hello, world, 1, Yuan, 3.14

现在来生成C语言可以调用的库:

$ go build -buildmode=c-archive -o hello.a  main.go
$ ls
go.mod  hello.a  hello.h  main.go

可以看到,生成了两个文件:hello.a和hello.h。C语言开发的伙伴肯定不陌生了。

go生成的头文件

来看看上一步生成的头文件里都有啥:

/* Code generated by cmd/cgo; DO NOT EDIT. */

/* package command-line-arguments */


#line 1 "cgo-builtin-export-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#endif

/* Start of preamble from import "C" comments.  */




/* End of preamble from import "C" comments.  */


/* Start of boilerplate cgo prologue.  */
#line 1 "cgo-gcc-export-header-prolog"

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;

/*
  static assertion to make sure the file is being used on architecture
  at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

/* End of boilerplate cgo prologue.  */

#ifdef __cplusplus
extern "C" {
#endif

extern GoUint8 hello(GoInt32 num, GoString name, GoFloat32 size);

#ifdef __cplusplus
}
#endif

可以看到go语言内的基本类型都被typedef重新定义了。其中GoString稍微复杂点,被定为一种含指针和长度的结构体:

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif

使用C语言调用golang生成的库

现在编写一个简单的C文件,调用刚刚生成的库里的hello()这个函数。

#include <stdio.h>

#include "hello.h"

int main()
{
    GoInt32 num = 3;
    GoFloat32 size = 3.14;
    GoString name = {0};
    name.p = "Yuan";
    name.n = 4; //上面字符串的长度
    
    GoUint8 ret = hello(num, name, size);
    printf("ret=%d\n", ret);
    
    return 0;
}

现在来编译运行下:

$ mv hello.a libhello.a
$ gcc hello_test_go.c  -L ./  -l  hello -lpthread
$ ./a.out
Hello, world, 3, Yuan, 3.14
ret=0

可以看到,完美运行了go语言中的函数。

参考资料

Golang的构建模式

golang插件系统

Go 语言编译模式 (buildmode) 之 C 源码归档模式 ( c-archive) 静态库模式

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 Windows 系统下,使用 Golang 调用 C 语言开源 libxslt 可以通过以下步骤完成: 1. 安装 libxslt 在 Windows 上,可以通过 Cygwin 或 MinGW 等工具链来安装 libxslt 。 2. 编写 C 语言代码 编写 C 语言代码,实现 libxslt 的功能,并将其编译为动态链接(.dll)文件。 例如,以下是一个使用 libxslt 将 XML 文件转换为 HTML 文件的 C 语言代码: ```c #include <stdio.h> #include <string.h> #include <libxslt/xslt.h> #include <libxslt/transform.h> int transform(char *xml, char *xsl, char *output) { xmlDocPtr doc, res; xsltStylesheetPtr sty; const char *params[1] = { NULL }; int ret; xmlInitParser(); LIBXML_TEST_VERSION doc = xmlReadMemory(xml, strlen(xml), "noname.xml", NULL, 0); sty = xsltParseStylesheetFile((const xmlChar *)xsl); res = xsltApplyStylesheet(sty, doc, params); xmlSaveFormatFile(output, res, 1); xsltFreeStylesheet(sty); xmlFreeDoc(doc); xmlFreeDoc(res); xmlCleanupParser(); return ret; } ``` 将上述代码编译为动态链接,可使用以下命令: ``` gcc -shared -o libxslt.dll -I/path/to/libxslt/include -L/path/to/libxslt/lib -lxslt -lz -lm xslt.c ``` 其中,`/path/to/libxslt` 为 libxslt 的安装目录。 3. 使用 CGO 调用 C 函数 在 Golang 中,使用 CGO 可以调用 C 函数。在调用 C 函数前,需要将 C 函数声明为外部函数。 例如,以下是一个使用 CGO 调用上述 C 函数的 Golang 代码: ```go package main // #cgo LDFLAGS: -L./ -lxslt // int transform(char *xml, char *xsl, char *output); import "C" import "fmt" func main() { xml := "<xml>...</xml>" xsl := "<xsl>...</xsl>" output := "output.html" ret := C.transform(C.CString(xml), C.CString(xsl), C.CString(output)) fmt.Println(ret) } ``` 在编译 Golang 代码时,需要指定 libxslt 的链接标志 `-L` 和 `lxslt`。 以上就是在 Windows 系统下,使用 Golang 调用 C 语言开源 libxslt 的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值