由于任务的原因,有一个项目需要用go语言调用c代码。之前有接触过swig,因此直接上官网去swig,果不其然,它是支持go语言的,二话不说,直接搞起。
首先,如果您不熟悉swig,可以自行百度。然后,本人也是不熟悉go语言,只为了能在go语言顺利调用c代码,因此才考虑用swig。不过,我也有几个考虑的,第一,我有java调用c代码的经验(jni),我知道调用c代码,最关键的内存的回收要处理好,不然会有不可忽略的bug等着你,hhh;第二,我囫囵吞枣的看了下go的语法,大概了知道了go与c类型的转换,还有go语言指针、数组等区别,一时间也无法很好的写出比较优良的代码,于是把希望寄托于swig。
好了下面开始正题。
Swing and Go 官方文档很清楚交了你怎么使用swig 来封装你的c代码,让go顺利的调用它。
文档第一个列子就是
% swig -go -cgo -intgosize 32 example.i
需要注意的文档要求把代码文件放入 GOPATH/src。这个为了让你后面 import的时候直接加入包名而方便的,你可以不需要这么做。直接在自己希望的目录中,运行上面的代码。
首先,目录有的文件为:example.c example.i。直接运行
% swig -go -cgo -intgosize 32 example.i
然后,你会得到 example.go example_wrap.c。其中example_wrap.c 是对example.c的再一次封装,为了更好的让go语言调用c代码,做了很多go与c类型的处理。然后,example.go就是直接让其他go语言调用的函数接口,即当main.go 调用example.go的函数时,example.go 会调用 example_wrap.c,然后example_wrap.c会调用example.c。
可能我写的不太清楚,如果您希望有更清楚的,可以去找更详细的资料,或者直接看官网的资料。
这里有一个特殊的情况
/* File : example.c */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
void test_a(int *p, int *q, int M, int m, int N,int n){
for (int i = 0; i < m; i++) {
printf("p[i] %d\n", p[i] + M);
p[i]=11;
}
for (int i = 0; i < n; i++) {
printf("q[i] %d\n", q[i] + N);
q[i]=12;
}
}
我希望能在go中调用该c函数,传入两个int数组p 和 q 并返回该数组的修改值。
在看了swig的官网的介绍之后,查到了官网的例子Swing and Go example,然后通过查找资料修改example.i,成功调用。
/* File : example.i */
%module example
%{
extern void test_a(int *p, int *q, int M, int m, int N,int n);
%}
%typemap(gotype) int *p ,int *q "[]int32"
%typemap(in) (int *p),(int *q)
%{
$1 = (int*)$input.array;
%}
extern void test_a(int *p, int *q, int M, int m, int N,int n);
下面为大家介绍一下思路:
%typemap(gotype) int *p , int *q "[]int32"
该语义为,将 int* p 和 int *q 的c代码函数中的参数的类型替换为go的[]int32;
func Test_a (arg1 []int32, arg2 []int32, arg3 int, arg4 int, arg5 int, arg6 int) {
_swig_i_0 := arg1
_swig_i_1 := arg2
_swig_i_2 := arg3
_swig_i_3 := arg4
_swig_i_4 := arg5
_swig_i_5 := arg6
.....
可以看到,在example.go中 test_a的函数中的参数类型,已经得到了替换,也是我们希望的类型。
接下来
%typemap(in) (int *p),(int *q)
%{
$1 = (int*)$input.array;
%}
该语义是告诉example_wrap.c,在go的数组和c的数组之间要做的处理。
我们知道,在swig会定义一个结构体
typedef struct { void* array; intgo len; intgo cap; } _goslice_;
用于转换go的数组与c的数组。
因此,我们真正的数组指针是array,len是数组长度,因此我们需要获得它。最终swig生成的代码如下
void _wrap_test_a_example_a6e85fed6d990a5b(_goslice_ _swig_go_0, _goslice_ _swig_go_1, intgo _swig_go_2, intgo _swig_go_3, intgo _swig_go_4, intgo _swig_go_5) {
int *arg1 = (int *) 0 ;
int *arg2 = (int *) 0 ;
int arg3 ;
int arg4 ;
int arg5 ;
int arg6 ;
arg1 = (int*)_swig_go_0.array;
arg2 = (int*)_swig_go_1.array;
arg3 = (int)_swig_go_2;
arg4 = (int)_swig_go_3;
arg5 = (int)_swig_go_4;
arg6 = (int)_swig_go_5;
test_a(arg1,arg2,arg3,arg4,arg5,arg6);
}
swing 其实很强大,还有很多未知等待大家去探索。
下面给出,剩下的例子
├── example
│ ├── example.c
│ ├── example.go
│ ├── example.i
│ └── example_wrap.c
├── testmain.go
package main
import (
"fmt"
"./example"
)
func main() {
fmt.Println("Hello World!")
ps := []int32{0, 0, 0, 0,0, 0,0, 0}
qs := []int32{0, 0, 0, 0,0, 0,0, 0}
example.Test_a(ps,qs,ctx,100, 8, 1000, 8)
fmt.Println(ps)
fmt.Println(qs)
}